blob: 3188463042f29d8cdd8c2ea5233676ed4bcd0ff3 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// 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 <utility>
#include "base/guid.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.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/common/mojom/event_router.mojom.h"
#include "extensions/common/mojom/frame.mojom.h"
#include "extensions/common/mojom/renderer_host.mojom.h"
#include "extensions/common/trace_util.h"
#include "extensions/renderer/api/messaging/message_target.h"
#include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/extension_frame_helper.h"
#include "extensions/renderer/extensions_renderer_client.h"
#include "extensions/renderer/native_extension_bindings_system.h"
#include "extensions/renderer/script_context.h"
#include "extensions/renderer/trace_util.h"
#include "extensions/renderer/worker_thread_dispatcher.h"
#include "ipc/ipc_sync_channel.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
namespace extensions {
namespace {
class MainThreadIPCMessageSender : public IPCMessageSender {
public:
MainThreadIPCMessageSender() : render_thread_(content::RenderThread::Get()) {}
MainThreadIPCMessageSender(const MainThreadIPCMessageSender&) = delete;
MainThreadIPCMessageSender& operator=(const MainThreadIPCMessageSender&) =
delete;
~MainThreadIPCMessageSender() override {}
void SendRequestIPC(ScriptContext* context,
mojom::RequestParamsPtr params) override {
content::RenderFrame* frame = context->GetRenderFrame();
if (!frame)
return;
int request_id = params->request_id;
ExtensionFrameHelper::Get(frame)->GetLocalFrameHost()->Request(
std::move(params),
base::BindOnce(&MainThreadIPCMessageSender::OnResponse,
weak_ptr_factory_.GetWeakPtr(), request_id));
}
void SendOnRequestResponseReceivedIPC(int request_id) override {}
mojom::EventListenerParamPtr GetEventListenerParam(ScriptContext* context) {
return !context->GetExtensionID().empty()
? mojom::EventListenerParam::NewExtensionId(
context->GetExtensionID())
: mojom::EventListenerParam::NewListenerUrl(context->url());
}
void SendAddUnfilteredEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK(!context->IsForServiceWorker());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
GetEventRouter()->AddListenerForMainThread(GetEventListenerParam(context),
event_name);
}
void SendRemoveUnfilteredEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK(!context->IsForServiceWorker());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
GetEventRouter()->RemoveListenerForMainThread(
GetEventListenerParam(context), event_name);
}
void SendAddUnfilteredLazyEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK(!context->IsForServiceWorker());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
GetEventRouter()->AddLazyListenerForMainThread(context->GetExtensionID(),
event_name);
}
void SendRemoveUnfilteredLazyEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK(!context->IsForServiceWorker());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
GetEventRouter()->RemoveLazyListenerForMainThread(context->GetExtensionID(),
event_name);
}
void SendAddFilteredEventListenerIPC(ScriptContext* context,
const std::string& event_name,
const base::Value::Dict& filter,
bool is_lazy) override {
DCHECK(!context->IsForServiceWorker());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
GetEventRouter()->AddFilteredListenerForMainThread(
GetEventListenerParam(context), event_name, filter.Clone(), is_lazy);
}
void SendRemoveFilteredEventListenerIPC(ScriptContext* context,
const std::string& event_name,
const base::Value::Dict& filter,
bool remove_lazy_listener) override {
DCHECK(!context->IsForServiceWorker());
DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
GetEventRouter()->RemoveFilteredListenerForMainThread(
GetEventListenerParam(context), event_name, filter.Clone(),
remove_lazy_listener);
}
void SendOpenMessageChannel(ScriptContext* script_context,
const PortId& port_id,
const MessageTarget& target,
const std::string& channel_name) override {
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();
TRACE_RENDERER_EXTENSION_EVENT(
"MainThreadIPCMessageSender::SendOpenMessageChannel/extension",
*target.extension_id);
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;
if (target.document_id)
info.document_id = *target.document_id;
render_frame->Send(new ExtensionHostMsg_OpenChannelToTab(
frame_context, info, 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));
}
void SendMessageResponsePending(int routing_id,
const PortId& port_id) override {
render_thread_->Send(new ExtensionHostMsg_ResponsePending(
PortContext::ForFrame(routing_id), port_id));
}
void SendActivityLogIPC(const ExtensionId& extension_id,
ActivityLogCallType call_type,
const std::string& call_name,
base::Value::List args,
const std::string& extra) override {
switch (call_type) {
case ActivityLogCallType::APICALL:
GetRendererHost()->AddAPIActionToActivityLog(extension_id, call_name,
std::move(args), extra);
break;
case ActivityLogCallType::EVENT:
GetRendererHost()->AddEventToActivityLog(extension_id, call_name,
std::move(args), extra);
break;
}
}
private:
void OnResponse(int request_id,
bool success,
base::Value::List response,
const std::string& error,
mojom::ExtraResponseDataPtr response_data) {
ExtensionsRendererClient::Get()
->GetDispatcher()
->bindings_system()
->HandleResponse(request_id, success, std::move(response), error,
std::move(response_data));
}
mojom::EventRouter* GetEventRouter() {
if (!event_router_remote_.is_bound()) {
render_thread_->GetChannel()->GetRemoteAssociatedInterface(
&event_router_remote_);
}
return event_router_remote_.get();
}
mojom::RendererHost* GetRendererHost() {
if (!renderer_host_.is_bound()) {
render_thread_->GetChannel()->GetRemoteAssociatedInterface(
&renderer_host_);
}
return renderer_host_.get();
}
content::RenderThread* const render_thread_;
mojo::AssociatedRemote<mojom::EventRouter> event_router_remote_;
mojo::AssociatedRemote<mojom::RendererHost> renderer_host_;
base::WeakPtrFactory<MainThreadIPCMessageSender> weak_ptr_factory_{this};
};
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(const WorkerThreadIPCMessageSender&) = delete;
WorkerThreadIPCMessageSender& operator=(const WorkerThreadIPCMessageSender&) =
delete;
~WorkerThreadIPCMessageSender() override {}
void SendRequestIPC(ScriptContext* context,
mojom::RequestParamsPtr params) override {
DCHECK(!context->GetRenderFrame());
DCHECK(context->IsForServiceWorker());
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
// `SendOnRequestResponseReceivedIPC()`.
dispatcher_->IncrementServiceWorkerActivity(service_worker_version_id_,
guid);
dispatcher_->RequestWorker(std::move(params));
}
void SendOnRequestResponseReceivedIPC(int request_id) override {
auto iter = request_id_to_guid_.find(request_id);
DCHECK(iter != request_id_to_guid_.end());
dispatcher_->DecrementServiceWorkerActivity(service_worker_version_id_,
iter->second);
request_id_to_guid_.erase(iter);
}
void SendAddUnfilteredEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK(context->IsForServiceWorker());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
context->service_worker_version_id());
dispatcher_->SendAddEventListener(
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(context->IsForServiceWorker());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
context->service_worker_version_id());
dispatcher_->SendRemoveEventListener(
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(context->IsForServiceWorker());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
dispatcher_->SendAddEventLazyListener(
context->GetExtensionID(), context->service_worker_scope(), event_name);
}
void SendRemoveUnfilteredLazyEventListenerIPC(
ScriptContext* context,
const std::string& event_name) override {
DCHECK(context->IsForServiceWorker());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
dispatcher_->SendRemoveEventLazyListener(
context->GetExtensionID(), context->service_worker_scope(), event_name);
}
void SendAddFilteredEventListenerIPC(ScriptContext* context,
const std::string& event_name,
const base::Value::Dict& filter,
bool is_lazy) override {
DCHECK(context->IsForServiceWorker());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
context->service_worker_version_id());
dispatcher_->SendAddEventFilteredListener(
context->GetExtensionID(), context->service_worker_scope(), event_name,
context->service_worker_version_id(),
content::WorkerThread::GetCurrentId(), filter.Clone(), is_lazy);
}
void SendRemoveFilteredEventListenerIPC(ScriptContext* context,
const std::string& event_name,
const base::Value::Dict& filter,
bool remove_lazy_listener) override {
DCHECK(context->IsForServiceWorker());
DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
context->service_worker_version_id());
dispatcher_->SendRemoveEventFilteredListener(
context->GetExtensionID(), context->service_worker_scope(), event_name,
context->service_worker_version_id(),
content::WorkerThread::GetCurrentId(), filter.Clone(),
remove_lazy_listener);
}
void SendOpenMessageChannel(ScriptContext* script_context,
const PortId& port_id,
const MessageTarget& target,
const std::string& channel_name) override {
DCHECK(!script_context->GetRenderFrame());
DCHECK(script_context->IsForServiceWorker());
const Extension* extension = script_context->extension();
switch (target.type) {
case MessageTarget::EXTENSION: {
ExtensionMsg_ExternalConnectionInfo info;
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();
TRACE_RENDERER_EXTENSION_EVENT(
"WorkerThreadIPCMessageSender::SendOpenMessageChannel/extension",
*target.extension_id);
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, channel_name, port_id));
break;
}
case MessageTarget::NATIVE_APP:
dispatcher_->Send(new ExtensionHostMsg_OpenChannelToNativeApp(
PortContextForCurrentWorker(), *target.native_application_name,
port_id));
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));
}
void SendMessageResponsePending(int routing_id,
const PortId& port_id) override {
DCHECK_EQ(MSG_ROUTING_NONE, routing_id);
dispatcher_->Send(new ExtensionHostMsg_ResponsePending(
PortContextForCurrentWorker(), port_id));
}
void SendActivityLogIPC(const ExtensionId& extension_id,
ActivityLogCallType call_type,
const std::string& call_name,
base::Value::List args,
const std::string& extra) override {
ExtensionHostMsg_APIActionOrEvent_Params params;
params.api_call = call_name;
params.arguments = std::move(args);
params.extra = extra;
switch (call_type) {
case ActivityLogCallType::APICALL:
dispatcher_->Send(new ExtensionHostMsg_AddAPIActionToActivityLog(
extension_id, params));
break;
case ActivityLogCallType::EVENT:
dispatcher_->Send(
new ExtensionHostMsg_AddEventToActivityLog(extension_id, params));
break;
}
}
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_;
absl::optional<ExtensionId> extension_id_;
// request id -> GUID map for each outstanding requests.
std::map<int, std::string> request_id_to_guid_;
};
} // namespace
IPCMessageSender::IPCMessageSender() = default;
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