blob: 04f71f80678ea036def997d0ccca6e46e037e18e [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/ipc_message_sender.h"
#include <map>
#include "base/guid.h"
#include "base/metrics/histogram_macros.h"
#include "base/optional.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/worker_thread.h"
#include "extensions/common/api/messaging/messaging_endpoint.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/features/feature.h"
#include "extensions/renderer/message_target.h"
#include "extensions/renderer/script_context.h"
#include "extensions/renderer/worker_thread_dispatcher.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
namespace extensions {
namespace {
// These values are logged to UMA. Entries should not be renumbered.
enum class IncludeTlsChannelIdBehavior {
// The TLS channel ID was not requested.
kNotRequested = 0,
// DEPRECATED: The TLS channel ID was requested, but was not included because
// the target extension did not allow it.
// kRequestedButDenied = 1,
// DEPRECATED: The TLS channel ID was requested, but was not found.
// kRequestedButNotFound = 2,
// DEPRECATED: The TLS channel ID was requested, allowed, and included in the
// response.
// kRequestedAndIncluded = 3,
// The TLS channel ID was requested, but was not provided because Channel ID
// is no longer supported.
kRequestedButNotSupported = 4,
kMaxValue = kRequestedButNotSupported,
};
void RecordIncludeTlsChannelIdBehavior(bool include_tls_channel_id) {
auto tls_channel_id_behavior =
include_tls_channel_id
? IncludeTlsChannelIdBehavior::kRequestedButNotSupported
: IncludeTlsChannelIdBehavior::kNotRequested;
UMA_HISTOGRAM_ENUMERATION("Extensions.Messaging.IncludeChannelIdBehavior",
tls_channel_id_behavior);
}
class MainThreadIPCMessageSender : public IPCMessageSender {
public:
MainThreadIPCMessageSender() : render_thread_(content::RenderThread::Get()) {}
~MainThreadIPCMessageSender() override {}
void SendRequestIPC(ScriptContext* context,
std::unique_ptr<ExtensionHostMsg_Request_Params> params,
binding::RequestThread thread) override {
content::RenderFrame* frame = context->GetRenderFrame();
if (!frame)
return;
switch (thread) {
case binding::RequestThread::UI:
frame->Send(
new ExtensionHostMsg_Request(frame->GetRoutingID(), *params));
break;
case binding::RequestThread::IO:
frame->Send(new ExtensionHostMsg_RequestForIOThread(
frame->GetRoutingID(), *params));
break;
}
}
void SendOnRequestResponseReceivedIPC(int request_id) override {}
void SendAddUnfilteredEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
render_thread_->Send(new ExtensionHostMsg_AddListener(
context->GetExtensionID(), context->url(), event_name,
blink::mojom::kInvalidServiceWorkerVersionId, kMainThreadId));
}
void SendRemoveUnfilteredEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
render_thread_->Send(new ExtensionHostMsg_RemoveListener(
context->GetExtensionID(), context->url(), event_name,
blink::mojom::kInvalidServiceWorkerVersionId, kMainThreadId));
}
void SendAddUnfilteredLazyEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
render_thread_->Send(new ExtensionHostMsg_AddLazyListener(
context->GetExtensionID(), event_name));
}
void SendRemoveUnfilteredLazyEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
render_thread_->Send(new ExtensionHostMsg_RemoveLazyListener(
context->GetExtensionID(), event_name));
}
void SendAddFilteredEventListenerIPC(ScriptContext* context,
const std::string& event_name,
const base::DictionaryValue& filter,
bool is_lazy) override {
DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
render_thread_->Send(new ExtensionHostMsg_AddFilteredListener(
context->GetExtensionID(), event_name, base::nullopt, filter, is_lazy));
}
void SendRemoveFilteredEventListenerIPC(ScriptContext* context,
const std::string& event_name,
const base::DictionaryValue& filter,
bool remove_lazy_listener) override {
DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
render_thread_->Send(new ExtensionHostMsg_RemoveFilteredListener(
context->GetExtensionID(), event_name, base::nullopt, filter,
remove_lazy_listener));
}
void SendOpenMessageChannel(ScriptContext* script_context,
const PortId& port_id,
const MessageTarget& target,
const std::string& channel_name,
bool include_tls_channel_id) override {
RecordIncludeTlsChannelIdBehavior(include_tls_channel_id);
content::RenderFrame* render_frame = script_context->GetRenderFrame();
DCHECK(render_frame);
PortContext frame_context =
PortContext::ForFrame(render_frame->GetRoutingID());
const Extension* extension = script_context->extension();
switch (target.type) {
case MessageTarget::EXTENSION: {
ExtensionMsg_ExternalConnectionInfo info;
if (extension && !extension->is_hosted_app()) {
info.source_endpoint =
script_context->context_type() == Feature::CONTENT_SCRIPT_CONTEXT
? MessagingEndpoint::ForContentScript(extension->id())
: MessagingEndpoint::ForExtension(extension->id());
} else {
info.source_endpoint = MessagingEndpoint::ForWebPage();
}
info.target_id = *target.extension_id;
info.source_url = script_context->url();
render_thread_->Send(new ExtensionHostMsg_OpenChannelToExtension(
frame_context, info, channel_name, port_id));
break;
}
case MessageTarget::TAB: {
DCHECK(extension);
DCHECK_NE(script_context->context_type(),
Feature::CONTENT_SCRIPT_CONTEXT);
ExtensionMsg_TabTargetConnectionInfo info;
info.tab_id = *target.tab_id;
info.frame_id = *target.frame_id;
render_frame->Send(new ExtensionHostMsg_OpenChannelToTab(
frame_context, info, extension->id(), channel_name, port_id));
break;
}
case MessageTarget::NATIVE_APP:
render_frame->Send(new ExtensionHostMsg_OpenChannelToNativeApp(
frame_context, *target.native_application_name, port_id));
break;
}
}
void SendOpenMessagePort(int routing_id, const PortId& port_id) override {
render_thread_->Send(new ExtensionHostMsg_OpenMessagePort(
PortContext::ForFrame(routing_id), port_id));
}
void SendCloseMessagePort(int routing_id,
const PortId& port_id,
bool close_channel) override {
render_thread_->Send(new ExtensionHostMsg_CloseMessagePort(
PortContext::ForFrame(routing_id), port_id, close_channel));
}
void SendPostMessageToPort(const PortId& port_id,
const Message& message) override {
render_thread_->Send(new ExtensionHostMsg_PostMessage(port_id, message));
}
private:
content::RenderThread* const render_thread_;
DISALLOW_COPY_AND_ASSIGN(MainThreadIPCMessageSender);
};
class WorkerThreadIPCMessageSender : public IPCMessageSender {
public:
WorkerThreadIPCMessageSender(WorkerThreadDispatcher* dispatcher,
int64_t service_worker_version_id)
: dispatcher_(dispatcher),
service_worker_version_id_(service_worker_version_id) {}
~WorkerThreadIPCMessageSender() override {}
void SendRequestIPC(ScriptContext* context,
std::unique_ptr<ExtensionHostMsg_Request_Params> params,
binding::RequestThread thread) override {
DCHECK(!context->GetRenderFrame());
DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_EQ(binding::RequestThread::UI, thread);
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
int worker_thread_id = content::WorkerThread::GetCurrentId();
params->worker_thread_id = worker_thread_id;
params->service_worker_version_id = service_worker_version_id_;
std::string guid = base::GenerateGUID();
request_id_to_guid_[params->request_id] = guid;
// Keeps the worker alive during extension function call. Balanced in
// HandleWorkerResponse().
dispatcher_->Send(new ExtensionHostMsg_IncrementServiceWorkerActivity(
service_worker_version_id_, guid));
dispatcher_->Send(new ExtensionHostMsg_RequestWorker(*params));
}
void SendOnRequestResponseReceivedIPC(int request_id) override {
auto iter = request_id_to_guid_.find(request_id);
DCHECK(iter != request_id_to_guid_.end());
dispatcher_->Send(new ExtensionHostMsg_DecrementServiceWorkerActivity(
service_worker_version_id_, iter->second));
request_id_to_guid_.erase(iter);
}
void SendAddUnfilteredEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
context->service_worker_version_id());
dispatcher_->Send(new ExtensionHostMsg_AddListener(
context->GetExtensionID(), context->service_worker_scope(), event_name,
context->service_worker_version_id(),
content::WorkerThread::GetCurrentId()));
}
void SendRemoveUnfilteredEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
context->service_worker_version_id());
dispatcher_->Send(new ExtensionHostMsg_RemoveListener(
context->GetExtensionID(), context->service_worker_scope(), event_name,
context->service_worker_version_id(),
content::WorkerThread::GetCurrentId()));
}
void SendAddUnfilteredLazyEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
dispatcher_->Send(new ExtensionHostMsg_AddLazyServiceWorkerListener(
context->GetExtensionID(), event_name,
context->service_worker_scope()));
}
void SendRemoveUnfilteredLazyEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
dispatcher_->Send(new ExtensionHostMsg_RemoveLazyServiceWorkerListener(
context->GetExtensionID(), event_name,
context->service_worker_scope()));
}
void SendAddFilteredEventListenerIPC(ScriptContext* context,
const std::string& event_name,
const base::DictionaryValue& filter,
bool is_lazy) override {
DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
context->service_worker_version_id());
ServiceWorkerIdentifier sw_identifier;
sw_identifier.scope = context->service_worker_scope();
sw_identifier.thread_id = content::WorkerThread::GetCurrentId();
sw_identifier.version_id = context->service_worker_version_id();
dispatcher_->Send(new ExtensionHostMsg_AddFilteredListener(
context->GetExtensionID(), event_name, sw_identifier, filter, is_lazy));
}
void SendRemoveFilteredEventListenerIPC(ScriptContext* context,
const std::string& event_name,
const base::DictionaryValue& filter,
bool remove_lazy_listener) override {
DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
context->service_worker_version_id());
ServiceWorkerIdentifier sw_identifier;
sw_identifier.scope = context->service_worker_scope();
sw_identifier.thread_id = content::WorkerThread::GetCurrentId();
sw_identifier.version_id = context->service_worker_version_id();
dispatcher_->Send(new ExtensionHostMsg_RemoveFilteredListener(
context->GetExtensionID(), event_name, sw_identifier, filter,
remove_lazy_listener));
}
void SendOpenMessageChannel(ScriptContext* script_context,
const PortId& port_id,
const MessageTarget& target,
const std::string& channel_name,
bool include_tls_channel_id) override {
DCHECK(!script_context->GetRenderFrame());
const Extension* extension = script_context->extension();
switch (target.type) {
case MessageTarget::EXTENSION: {
ExtensionMsg_ExternalConnectionInfo info;
// TODO(crbug.com/925918): Support extension Service Worker to extension
// messaging.
DCHECK_EQ(Feature::CONTENT_SCRIPT_CONTEXT,
script_context->context_type());
if (extension && !extension->is_hosted_app()) {
info.source_endpoint =
MessagingEndpoint::ForExtension(extension->id());
}
info.target_id = *target.extension_id;
info.source_url = script_context->url();
dispatcher_->Send(new ExtensionHostMsg_OpenChannelToExtension(
PortContextForCurrentWorker(), info, channel_name, port_id));
break;
}
case MessageTarget::TAB: {
DCHECK(extension);
ExtensionMsg_TabTargetConnectionInfo info;
info.tab_id = *target.tab_id;
info.frame_id = *target.frame_id;
dispatcher_->Send(new ExtensionHostMsg_OpenChannelToTab(
PortContextForCurrentWorker(), info, extension->id(), channel_name,
port_id));
break;
}
case MessageTarget::NATIVE_APP:
NOTIMPLEMENTED() << "https://crbug.com/925918.";
break;
}
}
void SendOpenMessagePort(int routing_id, const PortId& port_id) override {
DCHECK_EQ(MSG_ROUTING_NONE, routing_id);
dispatcher_->Send(new ExtensionHostMsg_OpenMessagePort(
PortContextForCurrentWorker(), port_id));
}
void SendCloseMessagePort(int routing_id,
const PortId& port_id,
bool close_channel) override {
DCHECK_EQ(MSG_ROUTING_NONE, routing_id);
dispatcher_->Send(new ExtensionHostMsg_CloseMessagePort(
PortContextForCurrentWorker(), port_id, close_channel));
}
void SendPostMessageToPort(const PortId& port_id,
const Message& message) override {
dispatcher_->Send(new ExtensionHostMsg_PostMessage(port_id, message));
}
private:
const ExtensionId& GetExtensionId() {
if (!extension_id_)
extension_id_ = dispatcher_->GetScriptContext()->extension()->id();
return *extension_id_;
}
PortContext PortContextForCurrentWorker() {
return PortContext::ForWorker(content::WorkerThread::GetCurrentId(),
service_worker_version_id_, GetExtensionId());
}
WorkerThreadDispatcher* const dispatcher_;
const int64_t service_worker_version_id_;
base::Optional<ExtensionId> extension_id_;
// request id -> GUID map for each outstanding requests.
std::map<int, std::string> request_id_to_guid_;
DISALLOW_COPY_AND_ASSIGN(WorkerThreadIPCMessageSender);
};
} // namespace
IPCMessageSender::IPCMessageSender() {}
IPCMessageSender::~IPCMessageSender() = default;
// static
std::unique_ptr<IPCMessageSender>
IPCMessageSender::CreateMainThreadIPCMessageSender() {
return std::make_unique<MainThreadIPCMessageSender>();
}
// static
std::unique_ptr<IPCMessageSender>
IPCMessageSender::CreateWorkerThreadIPCMessageSender(
WorkerThreadDispatcher* dispatcher,
int64_t service_worker_version_id) {
return std::make_unique<WorkerThreadIPCMessageSender>(
dispatcher, service_worker_version_id);
}
} // namespace extensions