// 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/renderer_messaging_service.h" #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/callback_helpers.h" #include "base/unguessable_token.h" #include "base/values.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.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/renderer/extension_bindings_system.h" #include "extensions/renderer/extension_port.h" #include "extensions/renderer/ipc_message_sender.h" #include "extensions/renderer/messaging_util.h" #include "extensions/renderer/script_context.h" #include "extensions/renderer/script_context_set.h" #include "third_party/blink/public/web/web_document.h" #include "third_party/blink/public/web/web_local_frame.h" #include "third_party/blink/public/web/web_scoped_user_gesture.h" #include "third_party/blink/public/web/web_scoped_window_focus_allowed_indicator.h" #include "third_party/blink/public/web/web_user_gesture_indicator.h" #include "v8/include/v8.h" namespace extensions { RendererMessagingService::RendererMessagingService( ExtensionBindingsSystem* bindings_system) : bindings_system_(bindings_system) {} RendererMessagingService::~RendererMessagingService() {} void RendererMessagingService::ValidateMessagePort( const ScriptContextSet& context_set, const PortId& port_id, content::RenderFrame* render_frame) { // TODO(devlin): In practice, |render_frame| should never be null here (unlike // in other methods, where it legitimately can), but it can be in testing. It // would be better to fake it somehow, but unfortunately, there's no good way // to have a RenderFrame in a unittest. :( int routing_id = render_frame ? render_frame->GetRoutingID() : MSG_ROUTING_NONE; bool has_port = false; // The base::Unretained() below is safe since ScriptContextSet::ForEach is // synchronous. context_set.ForEach( render_frame, base::Bind(&RendererMessagingService::ValidateMessagePortInContext, base::Unretained(this), port_id, &has_port)); // A reply is only sent if the port is missing, because the port is assumed to // exist unless stated otherwise. if (!has_port) { bindings_system_->GetIPCMessageSender()->SendCloseMessagePort( routing_id, port_id, false); } } void RendererMessagingService::DispatchOnConnect( const ScriptContextSet& context_set, const PortId& target_port_id, const std::string& channel_name, const ExtensionMsg_TabConnectionInfo& source, const ExtensionMsg_ExternalConnectionInfo& info, const std::string& tls_channel_id, content::RenderFrame* restrict_to_render_frame) { DCHECK(!target_port_id.is_opener); int routing_id = restrict_to_render_frame ? restrict_to_render_frame->GetRoutingID() : MSG_ROUTING_NONE; bool port_created = false; context_set.ForEach( info.target_id, restrict_to_render_frame, base::Bind(&RendererMessagingService::DispatchOnConnectToScriptContext, base::Unretained(this), target_port_id, channel_name, &source, info, tls_channel_id, &port_created)); // Note: |restrict_to_render_frame| may have been deleted at this point! IPCMessageSender* ipc_sender = bindings_system_->GetIPCMessageSender(); if (port_created) { ipc_sender->SendOpenMessagePort(routing_id, target_port_id); } else { ipc_sender->SendCloseMessagePort(routing_id, target_port_id, false); } } void RendererMessagingService::DeliverMessage( const ScriptContextSet& context_set, const PortId& target_port_id, const Message& message, content::RenderFrame* restrict_to_render_frame) { context_set.ForEach( restrict_to_render_frame, base::Bind(&RendererMessagingService::DeliverMessageToScriptContext, base::Unretained(this), message, target_port_id)); } void RendererMessagingService::DispatchOnDisconnect( const ScriptContextSet& context_set, const PortId& port_id, const std::string& error_message, content::RenderFrame* restrict_to_render_frame) { context_set.ForEach( restrict_to_render_frame, base::Bind(&RendererMessagingService::DispatchOnDisconnectToScriptContext, base::Unretained(this), port_id, error_message)); } void RendererMessagingService::ValidateMessagePortInContext( const PortId& port_id, bool* has_port, ScriptContext* script_context) { if (*has_port) return; // Stop checking if the port was found. // No need for |=; we know this is false right now from above. *has_port = ContextHasMessagePort(script_context, port_id); } void RendererMessagingService::DispatchOnConnectToScriptContext( const PortId& target_port_id, const std::string& channel_name, const ExtensionMsg_TabConnectionInfo* source, const ExtensionMsg_ExternalConnectionInfo& info, const std::string& tls_channel_id, bool* port_created, ScriptContext* script_context) { // If the channel was opened by this same context, ignore it. This should only // happen when messages are sent to an entire process (rather than a single // frame) as an optimization; otherwise the browser process filters this out. if (script_context->context_id() == target_port_id.context_id) return; // First, determine the event we'll use to connect. std::string target_extension_id = script_context->GetExtensionID(); bool is_external = info.source_id != target_extension_id; std::string event_name; if (channel_name == messaging_util::kSendRequestChannel) { event_name = is_external ? messaging_util::kOnRequestExternalEvent : messaging_util::kOnRequestEvent; } else if (channel_name == messaging_util::kSendMessageChannel) { event_name = is_external ? messaging_util::kOnMessageExternalEvent : messaging_util::kOnMessageEvent; } else { event_name = is_external ? messaging_util::kOnConnectExternalEvent : messaging_util::kOnConnectEvent; } // If there are no listeners for the given event, then we know the port won't // be used in this context. if (!bindings_system_->HasEventListenerInContext(event_name, script_context)) { return; } *port_created = true; DispatchOnConnectToListeners(script_context, target_port_id, target_extension_id, channel_name, source, info, tls_channel_id, event_name); } void RendererMessagingService::DeliverMessageToScriptContext( const Message& message, const PortId& target_port_id, ScriptContext* script_context) { if (!ContextHasMessagePort(script_context, target_port_id)) return; std::unique_ptr web_user_gesture; std::unique_ptr allow_window_focus; if (message.user_gesture) { web_user_gesture = std::make_unique( script_context->web_frame()); if (script_context->web_frame()) { blink::WebDocument document = script_context->web_frame()->GetDocument(); allow_window_focus = std::make_unique( &document); } } DispatchOnMessageToListeners(script_context, message, target_port_id); } void RendererMessagingService::DispatchOnDisconnectToScriptContext( const PortId& port_id, const std::string& error_message, ScriptContext* script_context) { if (!ContextHasMessagePort(script_context, port_id)) return; DispatchOnDisconnectToListeners(script_context, port_id, error_message); } } // namespace extensions