blob: f8dee4f5a1785bdc6c62f96f7ce59a48d9ef1f3a [file] [log] [blame]
// Copyright 2017 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.
#include "extensions/renderer/js_renderer_messaging_service.h"
#include <stdint.h>
#include <string>
#include "content/public/common/child_process_host.h"
#include "content/public/renderer/v8_value_converter.h"
#include "extensions/common/api/messaging/message.h"
#include "extensions/common/api/messaging/port_id.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/manifest_handlers/externally_connectable.h"
#include "extensions/renderer/extension_port.h"
#include "extensions/renderer/messaging_bindings.h"
#include "extensions/renderer/script_context.h"
#include "extensions/renderer/v8_helpers.h"
#include "v8/include/v8.h"
namespace extensions {
using v8_helpers::ToV8String;
JSRendererMessagingService::JSRendererMessagingService(
ExtensionBindingsSystem* bindings_system)
: RendererMessagingService(bindings_system) {}
JSRendererMessagingService::~JSRendererMessagingService() {}
bool JSRendererMessagingService::ContextHasMessagePort(
ScriptContext* script_context,
const PortId& port_id) {
MessagingBindings* bindings = MessagingBindings::ForContext(script_context);
DCHECK(bindings);
return bindings->GetPortWithId(port_id) != nullptr;
}
void JSRendererMessagingService::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& tls_channel_id,
const std::string& event_name) {
MessagingBindings* bindings = MessagingBindings::ForContext(script_context);
ExtensionPort* port = bindings->CreateNewPortWithId(target_port_id);
v8::Isolate* isolate = script_context->isolate();
v8::HandleScope handle_scope(isolate);
const std::string& source_url_spec = info.source_url.spec();
const Extension* extension = script_context->extension();
v8::Local<v8::Value> tab = v8::Null(isolate);
v8::Local<v8::Value> tls_channel_id_value = v8::Undefined(isolate);
v8::Local<v8::Value> guest_process_id = v8::Undefined(isolate);
v8::Local<v8::Value> guest_render_frame_routing_id = v8::Undefined(isolate);
if (extension) {
if (!source->tab.empty() && !extension->is_platform_app()) {
tab = content::V8ValueConverter::Create()->ToV8Value(
&source->tab, script_context->v8_context());
}
ExternallyConnectableInfo* externally_connectable =
ExternallyConnectableInfo::Get(extension);
if (externally_connectable &&
externally_connectable->accepts_tls_channel_id) {
v8::Local<v8::String> v8_tls_channel_id;
if (ToV8String(isolate, tls_channel_id.c_str(), &v8_tls_channel_id))
tls_channel_id_value = v8_tls_channel_id;
}
if (info.guest_process_id != content::ChildProcessHost::kInvalidUniqueID) {
guest_process_id = v8::Integer::New(isolate, info.guest_process_id);
guest_render_frame_routing_id =
v8::Integer::New(isolate, info.guest_render_frame_routing_id);
}
}
v8::Local<v8::String> v8_channel_name;
v8::Local<v8::String> v8_source_id;
v8::Local<v8::String> v8_target_extension_id;
v8::Local<v8::String> v8_source_url_spec;
if (!ToV8String(isolate, channel_name.c_str(), &v8_channel_name) ||
!ToV8String(isolate, info.source_id.c_str(), &v8_source_id) ||
!ToV8String(isolate, target_extension_id.c_str(),
&v8_target_extension_id) ||
!ToV8String(isolate, source_url_spec.c_str(), &v8_source_url_spec)) {
NOTREACHED() << "dispatchOnConnect() passed non-string argument";
return;
}
v8::Local<v8::Value> arguments[] = {
// portId
v8::Integer::New(isolate, port->js_id()),
// channelName
v8_channel_name,
// sourceTab
tab,
// source_frame_id
v8::Integer::New(isolate, source->frame_id),
// guestProcessId
guest_process_id,
// guestRenderFrameRoutingId
guest_render_frame_routing_id,
// sourceExtensionId
v8_source_id,
// targetExtensionId
v8_target_extension_id,
// sourceUrl
v8_source_url_spec,
// tlsChannelId
tls_channel_id_value,
};
// Note: this can execute asynchronously if JS is suspended.
script_context->module_system()->CallModuleMethodSafe(
"messaging", "dispatchOnConnect", arraysize(arguments), arguments);
}
void JSRendererMessagingService::DispatchOnMessageToListeners(
ScriptContext* script_context,
const Message& message,
const PortId& target_port_id) {
MessagingBindings* bindings = MessagingBindings::ForContext(script_context);
ExtensionPort* port = bindings->GetPortWithId(target_port_id);
DCHECK(port);
v8::Isolate* isolate = script_context->isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Value> port_id_handle =
v8::Integer::New(isolate, port->js_id());
v8::Local<v8::String> v8_data;
if (!ToV8String(isolate, message.data.c_str(), &v8_data))
return;
std::vector<v8::Local<v8::Value>> arguments;
arguments.push_back(v8_data);
arguments.push_back(port_id_handle);
script_context->module_system()->CallModuleMethodSafe(
"messaging", "dispatchOnMessage", &arguments);
}
void JSRendererMessagingService::DispatchOnDisconnectToListeners(
ScriptContext* script_context,
const PortId& port_id,
const std::string& error_message) {
MessagingBindings* bindings = MessagingBindings::ForContext(script_context);
ExtensionPort* port = bindings->GetPortWithId(port_id);
DCHECK(port);
v8::Isolate* isolate = script_context->isolate();
v8::HandleScope handle_scope(isolate);
std::vector<v8::Local<v8::Value>> arguments;
arguments.push_back(v8::Integer::New(isolate, port->js_id()));
v8::Local<v8::String> v8_error_message;
if (!error_message.empty())
ToV8String(isolate, error_message.c_str(), &v8_error_message);
if (!v8_error_message.IsEmpty()) {
arguments.push_back(v8_error_message);
} else {
arguments.push_back(v8::Null(isolate));
}
script_context->module_system()->CallModuleMethodSafe(
"messaging", "dispatchOnDisconnect", &arguments);
}
} // namespace extensions