blob: 40402650869f28a96aa61c47b3b38d15f6defc4a [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_RENDERER_NATIVE_RENDERER_MESSAGING_SERVICE_H_
#define EXTENSIONS_RENDERER_NATIVE_RENDERER_MESSAGING_SERVICE_H_
#include <string>
#include "base/macros.h"
#include "extensions/common/extension_id.h"
#include "extensions/renderer/gin_port.h"
#include "extensions/renderer/one_time_message_handler.h"
#include "extensions/renderer/renderer_messaging_service.h"
#include "gin/handle.h"
struct ExtensionMsg_ExternalConnectionInfo;
struct ExtensionMsg_TabConnectionInfo;
namespace extensions {
class NativeExtensionBindingsSystem;
struct Message;
struct MessageTarget;
struct PortId;
// The messaging service to handle dispatching extension messages and connection
// events to different contexts.
// This primarily handles long-lived port-based communications (like
// runtime.connect). A basic flow will create an "opener" port and one ore more
// "receiver" ports in different contexts (and possibly processes). This class
// manages the communication with the browser to forward these messages along.
// From JavaScript, a basic flow would be:
// // page1.js
// var port = chrome.runtime.connect();
// port.onMessage.addListener(function() { <handle message> });
// port.postMessage('hi!');
// <eventually> port.disconnect();
// // page2.js
// chrome.runtime.onConnect.addListener(function(port) {
// port.onMessage.addListener(function() { <handle message> });
// port.postMessage('hey!');
// });
// This causes the following steps in the messaging service:
// Connection:
// * connect() triggers OpenChannelToExtension, which notifies the browser of
// a new connection.
// * The browser dispatches OnConnect messages to different renderers. If a
// renderer has a listener, it will respond with an OpenMessagePort message.
// If no renderer has a listener, the browser will close the port.
// Message Posting
// * Calls to postMessage() trigger a PostMessageToPort messge to the browser.
// * The browser sends a DeliverMessage message to listening renderers. These
// then dispatch the onMessage event to listeners.
// Disconnecting
// * disconnect() calls result in sending a CloseMessagePort message to the
// browser.
// * The browser then sends a DispatchOnDisconnect message to other renderers,
// which triggers the onDisconnect() event.
// TODO(devlin): This is a pretty large comment for a class, and it documents
// browser/renderer interaction. I wonder if this would be better in a
// messaging.md document?
class NativeRendererMessagingService : public RendererMessagingService,
public GinPort::Delegate {
public:
explicit NativeRendererMessagingService(
NativeExtensionBindingsSystem* bindings_system);
~NativeRendererMessagingService() override;
// Creates and opens a new message port in the specified context.
gin::Handle<GinPort> Connect(ScriptContext* script_context,
const MessageTarget& target,
const std::string& name,
bool include_tls_channel_id);
// Sends a one-time message, as is used by runtime.sendMessage.
void SendOneTimeMessage(ScriptContext* script_context,
const MessageTarget& target,
const std::string& channel_name,
bool include_tls_channel_id,
const Message& message,
v8::Local<v8::Function> response_callback);
// GinPort::Delegate:
void PostMessageToPort(v8::Local<v8::Context> context,
const PortId& port_id,
int routing_id,
std::unique_ptr<Message> message) override;
void ClosePort(v8::Local<v8::Context> context,
const PortId& port_id,
int routing_id) override;
gin::Handle<GinPort> CreatePortForTesting(ScriptContext* script_context,
const std::string& channel_name,
const PortId& port_id);
gin::Handle<GinPort> GetPortForTesting(ScriptContext* script_context,
const PortId& port_id);
bool HasPortForTesting(ScriptContext* script_context, const PortId& port_id);
private:
// RendererMessagingService:
bool ContextHasMessagePort(ScriptContext* script_context,
const PortId& port_id) override;
void DispatchOnConnectToListeners(
ScriptContext* script_context,
const PortId& target_port_id,
const ExtensionId& target_extension_id,
const std::string& channel_name,
const ExtensionMsg_TabConnectionInfo* source,
const ExtensionMsg_ExternalConnectionInfo& info,
const std::string& event_name) override;
void DispatchOnMessageToListeners(ScriptContext* script_context,
const Message& message,
const PortId& target_port_id) override;
void DispatchOnDisconnectToListeners(ScriptContext* script_context,
const PortId& port_id,
const std::string& error) override;
// Creates a new port in the given context, with the specified |channel_name|
// and |port_id|. Assumes no such port exists.
gin::Handle<GinPort> CreatePort(ScriptContext* script_context,
const std::string& channel_name,
const PortId& port_id);
// Returns the port with the given |port_id| in the given |script_context|;
// requires that such a port exists.
gin::Handle<GinPort> GetPort(ScriptContext* script_context,
const PortId& port_id);
// The associated bindings system; guaranteed to outlive this object.
NativeExtensionBindingsSystem* const bindings_system_;
OneTimeMessageHandler one_time_message_handler_;
DISALLOW_COPY_AND_ASSIGN(NativeRendererMessagingService);
};
} // namespace extensions
#endif // EXTENSIONS_RENDERER_NATIVE_RENDERER_MESSAGING_SERVICE_H_