chromium/ppapi/proxy/host_dispatcher.h

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef PPAPI_PROXY_HOST_DISPATCHER_H_
#define PPAPI_PROXY_HOST_DISPATCHER_H_

#include <map>
#include <string>
#include <unordered_map>
#include <vector>

#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/process/process.h"
#include "base/task/single_thread_task_runner.h"
#include "ipc/message_filter.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/proxy/dispatcher.h"

struct PPB_Proxy_Private;

namespace ppapi {

struct Preferences;

namespace proxy {

class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher {
 public:
  // This interface receives notifications about sync messages being sent by
  // the dispatcher to the plugin process. Some parts of Chrome may need to
  // know whether we are sending a synchronous message to the plugin; e.g. to
  // detect a hung plugin or to avoid re-entering JavaScript.
  //
  // Note that there can be nested sync messages, so the begin/end status
  // actually represents a stack of blocking messages.
  class SyncMessageStatusObserver {
   public:
    // Notification that a sync message is about to be sent out.
    virtual void BeginBlockOnSyncMessage() = 0;

    // Notification that a sync message reply was received and the dispatcher
    // is no longer blocked on a sync message.
    virtual void EndBlockOnSyncMessage() = 0;

   protected:
    virtual ~SyncMessageStatusObserver() {}
  };

  // Constructor for the renderer side. This will take a reference to the
  // SyncMessageStatusReceiver.
  //
  // You must call InitHostWithChannel after the constructor.
  HostDispatcher(PP_Module module,
                 PP_GetInterface_Func local_get_interface,
                 const PpapiPermissions& permissions);

  HostDispatcher(const HostDispatcher&) = delete;
  HostDispatcher& operator=(const HostDispatcher&) = delete;

  ~HostDispatcher();

  // You must call this function before anything else. Returns true on success.
  // The delegate pointer must outlive this class, ownership is not
  // transferred.
  virtual bool InitHostWithChannel(
      Delegate* delegate,
      base::ProcessId peer_pid,
      const IPC::ChannelHandle& channel_handle,
      bool is_client,
      const Preferences& preferences,
      scoped_refptr<base::SingleThreadTaskRunner> task_runner);

  // The host side maintains a mapping from PP_Instance to Dispatcher so
  // that we can send the messages to the right channel.
  static HostDispatcher* GetForInstance(PP_Instance instance);
  static void SetForInstance(PP_Instance instance,
                             HostDispatcher* dispatcher);
  static void RemoveForInstance(PP_Instance instance);

  // Returns the host's notion of our PP_Module. This will be different than
  // the plugin's notion of its PP_Module because the plugin process may be
  // used by multiple renderer processes.
  //
  // Use this value instead of a value from the plugin whenever talking to the
  // host.
  PP_Module pp_module() const { return pp_module_; }

  // Dispatcher overrides.
  bool IsPlugin() const override;
  bool Send(IPC::Message* msg) override;

  // IPC::Listener.
  bool OnMessageReceived(const IPC::Message& msg) override;
  void OnChannelError() override;

  // Proxied version of calling GetInterface on the plugin. This will check
  // if the plugin supports the given interface (with caching) and returns the
  // pointer to the proxied interface if it is supported. Returns NULL if the
  // given interface isn't supported by the plugin or the proxy.
  const void* GetProxiedInterface(const std::string& iface_name);

  // See the value below. Call this when processing a scripting message from
  // the plugin that can be reentered. This is set to false at the beginning
  // of processing of each message from the plugin.
  void set_allow_plugin_reentrancy() {
    allow_plugin_reentrancy_ = true;
  }

  // Returns the proxy interface for talking to the implementation.
  const PPB_Proxy_Private* ppb_proxy() const { return ppb_proxy_; }

  // Register an observer that will be invoked when the dispatcher begins
  // sending a sync message and finishes sending a sync message.
  // Returns a OnceClosure that can be used to unregister the observer (the
  // OnceClosure is bound to a weak pointer, so is safe to call even after the
  // HostDispatcher is gone.)
  base::OnceClosure AddSyncMessageStatusObserver(
      SyncMessageStatusObserver* obs);

  void AddFilter(IPC::Listener* listener);

 protected:
  // Overridden from Dispatcher.
  void OnInvalidMessageReceived() override;

 private:
  void OnHostMsgLogWithSource(PP_Instance instance,
                              int int_log_level,
                              const std::string& source,
                              const std::string& value);

  void RemoveSyncMessageStatusObserver(SyncMessageStatusObserver* obs);

  PP_Module pp_module_;

  // Maps interface name to whether that interface is supported. If an interface
  // name is not in the map, that implies that we haven't queried for it yet.
  typedef std::unordered_map<std::string, bool> PluginSupportedMap;
  PluginSupportedMap plugin_supported_;

  // Guaranteed non-NULL.
  const PPB_Proxy_Private* ppb_proxy_;

  // Set to true when the plugin is in a state that it can be reentered by a
  // sync message from the host. We allow reentrancy only when we're processing
  // a sync message from the renderer that is a scripting command. When the
  // plugin is in this state, it needs to accept reentrancy since scripting may
  // ultimately call back into the plugin.
  bool allow_plugin_reentrancy_;

  base::ObserverList<SyncMessageStatusObserver>::Unchecked
      sync_status_observer_list_;

  std::vector<IPC::Listener*> filters_;

  base::WeakPtrFactory<HostDispatcher> weak_ptr_factory_{this};
};

// Create this object on the stack to prevent the module (and hence the
// dispatcher) from being deleted out from under you. This is necessary when
// calling some scripting functions that may delete the plugin.
//
// This class does nothing if used on the plugin side.
class ScopedModuleReference {
 public:
  explicit ScopedModuleReference(Dispatcher* dispatcher);

  ScopedModuleReference(const ScopedModuleReference&) = delete;
  ScopedModuleReference& operator=(const ScopedModuleReference&) = delete;

  ~ScopedModuleReference();

 private:
  HostDispatcher* dispatcher_;
};

}  // namespace proxy
}  // namespace ppapi

#endif  // PPAPI_PROXY_HOST_DISPATCHER_H_