#include "extensions/browser/api/messaging/message_service.h"
#include <stdint.h>
#include <limits>
#include <memory>
#include <utility>
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/back_forward_cache/back_forward_cache_disable.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/browser/api/messaging/channel_endpoint.h"
#include "extensions/browser/api/messaging/extension_message_port.h"
#include "extensions/browser/api/messaging/message_port.h"
#include "extensions/browser/api/messaging/messaging_delegate.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_api_frame_id_map.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/extension_web_contents_observer.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/guest_view/web_view/web_view_guest.h"
#include "extensions/browser/pref_names.h"
#include "extensions/browser/process_manager.h"
#include "extensions/common/api/messaging/channel_type.h"
#include "extensions/common/api/messaging/messaging_endpoint.h"
#include "extensions/common/api/messaging/port_context.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/common/manifest_handlers/externally_connectable.h"
#include "extensions/common/manifest_handlers/incognito_info.h"
#include "extensions/common/permissions/permissions_data.h"
#include "url/gurl.h"
#if defined(OHOS_ARKWEB_EXTENSIONS)
#include "cef/libcef/browser/extensions/contents_extensions_util.h"
#endif
using content::BrowserContext;
using content::BrowserThread;
using content::SiteInstance;
using content::WebContents;
namespace extensions {
namespace {
const char kReceivingEndDoesntExistError[] =
"Could not establish connection. Receiving end does not exist.";
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS)
const char kMissingPermissionError[] =
"Access to native messaging requires nativeMessaging permission.";
const char kProhibitedByPoliciesError[] =
"Access to the native messaging host was disabled by the system "
"administrator.";
#endif
LazyContextId LazyContextIdFor(content::BrowserContext* browser_context,
const Extension* extension) {
if (BackgroundInfo::HasLazyBackgroundPage(extension))
return LazyContextId(browser_context, extension->id());
DCHECK(BackgroundInfo::IsServiceWorkerBased(extension));
return LazyContextId(browser_context, extension->id(), extension->url());
}
const Extension* GetExtensionForNativeAppChannel(
const ChannelEndpoint& source) {
DCHECK(!source.is_for_native_host());
if (source.is_for_service_worker()) {
const ExtensionId& extension_id =
source.port_context().worker->extension_id;
return ExtensionRegistry::Get(source.browser_context())
->enabled_extensions()
.GetByID(extension_id);
}
DCHECK(source.is_for_render_frame());
content::RenderFrameHost* source_rfh = source.GetRenderFrameHost();
if (!source_rfh)
return nullptr;
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(source_rfh);
if (!web_contents)
return nullptr;
ExtensionWebContentsObserver* extension_web_contents_observer =
ExtensionWebContentsObserver::GetForWebContents(web_contents);
if (!extension_web_contents_observer)
return nullptr;
return extension_web_contents_observer->GetExtensionFromFrame(source_rfh,
true);
}
bool IsExtensionMessageSupportedInBackForwardCache() {
if (!content::BackForwardCache::IsBackForwardCacheFeatureEnabled())
return false;
static const bool is_extension_message_supported =
base::FeatureParam<bool>(&features::kBackForwardCache,
"extension_message_supported", true)
.Get();
return is_extension_message_supported;
}
void MaybeDisableBackForwardCacheForMessaging(content::RenderFrameHost* host) {
if (!host || IsExtensionMessageSupportedInBackForwardCache())
return;
content::BackForwardCache::DisableForRenderFrameHost(
host, back_forward_cache::DisabledReason(
back_forward_cache::DisabledReasonId::kExtensionMessaging));
}
constexpr const char* kDefaultSWExtendedLifetimeList[] = {
"chrome-extension://khpfeaanjngmcnplbdlpegiifgpfgdco/",
"chrome-extension://haiffjcadagjlijoggckpgfnoeiflnem/",
"chrome-extension://lbfgjakkeeccemhonnolnmglmfmccaag/",
"chrome-extension://anjihnbmjbbpofafpmklejenkgnjfcdi/",
"chrome-extension://ppkfnjlimknmjoaemnpidmdlfchhehel/",
"chrome-extension://kenkpdjcfppbccchillfdjkjnejjgand/",
};
std::vector<url::Origin> GetServiceWorkerExtendedLifetimeOrigins(
content::BrowserContext* browser_context) {
std::vector<url::Origin> origins;
const base::Value::List& extended_lifetime_urls =
ExtensionPrefs::Get(browser_context)
->pref_service()
->GetList(
pref_names::kExtendedBackgroundLifetimeForPortConnectionsToUrls);
origins.reserve(std::size(kDefaultSWExtendedLifetimeList) +
extended_lifetime_urls.size());
for (const std::string& default_value : kDefaultSWExtendedLifetimeList) {
url::Origin origin = url::Origin::Create(GURL(default_value));
origins.push_back(std::move(origin));
}
for (const base::Value& value : extended_lifetime_urls) {
GURL url(value.GetString());
if (!url.is_valid()) {
continue;
}
url::Origin origin = url::Origin::Create(url);
if (origin.opaque()) {
continue;
}
origins.push_back(std::move(origin));
}
return origins;
}
}
struct MessageService::MessageChannel {
std::unique_ptr<MessagePort> opener;
std::unique_ptr<MessagePort> receiver;
};
struct MessageService::OpenChannelParams {
ChannelEndpoint source;
absl::optional<base::Value::Dict> source_tab;
ExtensionApiFrameIdMap::FrameData source_frame;
std::unique_ptr<MessagePort> receiver;
PortId receiver_port_id;
MessagingEndpoint source_endpoint;
std::unique_ptr<MessagePort> opener_port;
std::string target_extension_id;
GURL source_url;
absl::optional<url::Origin> source_origin;
ChannelType channel_type;
std::string channel_name;
bool include_guest_process_info;
OpenChannelParams(const ChannelEndpoint& source,
absl::optional<base::Value::Dict> source_tab,
const ExtensionApiFrameIdMap::FrameData& source_frame,
MessagePort* receiver,
const PortId& receiver_port_id,
const MessagingEndpoint& source_endpoint,
std::unique_ptr<MessagePort> opener_port,
const std::string& target_extension_id,
const GURL& source_url,
absl::optional<url::Origin> source_origin,
ChannelType channel_type,
const std::string& channel_name,
bool include_guest_process_info)
: source(source),
source_tab(std::move(source_tab)),
source_frame(source_frame),
receiver(receiver),
receiver_port_id(receiver_port_id),
source_endpoint(source_endpoint),
opener_port(std::move(opener_port)),
target_extension_id(target_extension_id),
source_url(source_url),
source_origin(source_origin),
channel_type(channel_type),
channel_name(channel_name),
include_guest_process_info(include_guest_process_info) {}
OpenChannelParams(const OpenChannelParams&) = delete;
OpenChannelParams& operator=(const OpenChannelParams&) = delete;
bool is_onetime_channel() const {
return channel_type == ChannelType::kSendMessage ||
channel_type == ChannelType::kSendRequest;
}
};
MessageService::MessageService(BrowserContext* context)
: context_(context),
messaging_delegate_(ExtensionsAPIClient::Get()->GetMessagingDelegate()) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_NE(nullptr, messaging_delegate_);
}
MessageService::~MessageService() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
channels_.clear();
}
static base::LazyInstance<
BrowserContextKeyedAPIFactory<MessageService>>::DestructorAtExit g_factory =
LAZY_INSTANCE_INITIALIZER;
BrowserContextKeyedAPIFactory<MessageService>*
MessageService::GetFactoryInstance() {
return g_factory.Pointer();
}
MessageService* MessageService::Get(BrowserContext* context) {
return BrowserContextKeyedAPIFactory<MessageService>::Get(context);
}
void MessageService::OpenChannelToExtension(
const ChannelEndpoint& source,
const PortId& source_port_id,
const MessagingEndpoint& source_endpoint,
std::unique_ptr<MessagePort> opener_port,
const std::string& target_extension_id,
const GURL& source_url,
ChannelType channel_type,
const std::string& channel_name) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(source_port_id.is_opener);
DCHECK(!target_extension_id.empty());
DCHECK(source_endpoint.extension_id.has_value() ||
source_endpoint.type == MessagingEndpoint::Type::kWebPage ||
source_endpoint.type == MessagingEndpoint::Type::kNativeApp);
DCHECK_EQ(source_endpoint.native_app_name.has_value(),
source_endpoint.type == MessagingEndpoint::Type::kNativeApp);
int source_process_id = source.render_process_id();
DCHECK_EQ(source_process_id == content::ChildProcessHost::kInvalidUniqueID,
source_endpoint.type == MessagingEndpoint::Type::kNativeApp);
content::RenderFrameHost* source_render_frame_host =
source.is_for_render_frame() ? source.GetRenderFrameHost() : nullptr;
if (!source.IsValid())
return;
BrowserContext* context = source.browser_context();
DCHECK(ExtensionsBrowserClient::Get()->IsSameContext(context, context_));
MaybeDisableBackForwardCacheForMessaging(source_render_frame_host);
if (!opener_port) {
DCHECK(source_endpoint.type == MessagingEndpoint::Type::kContentScript ||
source_endpoint.type == MessagingEndpoint::Type::kWebPage ||
source_endpoint.type == MessagingEndpoint::Type::kExtension);
opener_port = ExtensionMessagePort::CreateForEndpoint(
weak_factory_.GetWeakPtr(), source_port_id,
source_endpoint.extension_id ? *source_endpoint.extension_id
: ExtensionId(),
source);
}
if (!opener_port->IsValidPort())
return;
ExtensionRegistry* registry = ExtensionRegistry::Get(context);
const Extension* target_extension =
registry->enabled_extensions().GetByID(target_extension_id);
if (!target_extension) {
opener_port->DispatchOnDisconnect(kReceivingEndDoesntExistError);
return;
}
MessagingEndpoint::Relationship relationship =
MessagingEndpoint::GetRelationship(source_endpoint, target_extension_id);
if (relationship != MessagingEndpoint::Relationship::kInternal &&
relationship != MessagingEndpoint::Relationship::kExternalNativeApp) {
ExternallyConnectableInfo* externally_connectable =
static_cast<ExternallyConnectableInfo*>(
target_extension->GetManifestData(
manifest_keys::kExternallyConnectable));
bool is_externally_connectable = false;
if (externally_connectable) {
if (relationship == MessagingEndpoint::Relationship::kExternalExtension) {
DCHECK(source_endpoint.extension_id);
is_externally_connectable =
externally_connectable->IdCanConnect(*source_endpoint.extension_id);
} else {
DCHECK(source_render_frame_host);
DCHECK_EQ(MessagingEndpoint::Relationship::kExternalWebPage,
relationship);
is_externally_connectable = externally_connectable->matches.MatchesURL(
source_render_frame_host->GetLastCommittedURL());
}
} else {
is_externally_connectable =
relationship == MessagingEndpoint::Relationship::kExternalExtension;
}
if (!is_externally_connectable) {
opener_port->DispatchOnDisconnect(kReceivingEndDoesntExistError);
return;
}
}
WebContents* source_contents = nullptr;
if (source_render_frame_host) {
source_contents =
WebContents::FromRenderFrameHost(source_render_frame_host);
}
ExtensionApiFrameIdMap::FrameData source_frame;
bool include_guest_process_info = false;
absl::optional<base::Value::Dict> source_tab =
messaging_delegate_->MaybeGetTabInfo(source_contents);
absl::optional<url::Origin> source_origin;
if (source_render_frame_host)
source_origin = source_render_frame_host->GetLastCommittedOrigin();
if (source_tab) {
DCHECK(source_render_frame_host);
source_frame = ExtensionApiFrameIdMap::Get()->GetFrameData(
source_render_frame_host->GetGlobalId());
} else {
bool is_web_view =
!!WebViewGuest::FromRenderFrameHost(source_render_frame_host);
if (is_web_view &&
Manifest::IsComponentLocation(target_extension->location())) {
include_guest_process_info = true;
}
}
std::unique_ptr<OpenChannelParams> params =
std::make_unique<OpenChannelParams>(
source, std::move(source_tab), source_frame, nullptr,
source_port_id.GetOppositePortId(), source_endpoint,
std::move(opener_port), target_extension_id, source_url,
std::move(source_origin), channel_type, channel_name,
include_guest_process_info);
pending_incognito_channels_[params->receiver_port_id.GetChannelId()] =
PendingMessagesQueue();
if (context->IsOffTheRecord() &&
!util::IsIncognitoEnabled(target_extension_id, context)) {
if (relationship != MessagingEndpoint::Relationship::kExternalWebPage ||
IncognitoInfo::IsSplitMode(target_extension) ||
util::CanBeIncognitoEnabled(target_extension)) {
OnOpenChannelAllowed(std::move(params), false);
return;
}
EventRouter* event_router = EventRouter::Get(context);
const char* const events[] = {
"runtime.onConnectExternal", "runtime.onConnectNative",
"runtime.onMessageExternal", "extension.onRequestExternal", nullptr};
bool has_event_listener = false;
for (const char* const* event = events; *event; event++) {
has_event_listener |=
event_router->ExtensionHasEventListener(target_extension_id, *event);
}
if (!has_event_listener) {
OnOpenChannelAllowed(std::move(params), false);
return;
}
messaging_delegate_->QueryIncognitoConnectability(
context, target_extension, source_contents, source_url,
base::BindOnce(&MessageService::OnOpenChannelAllowed,
weak_factory_.GetWeakPtr(), std::move(params)));
return;
}
OnOpenChannelAllowed(std::move(params), true);
}
void MessageService::OpenChannelToNativeApp(
const ChannelEndpoint& source,
const PortId& source_port_id,
const std::string& native_app_name) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(source_port_id.is_opener);
if (!source.IsValid())
return;
const Extension* extension = GetExtensionForNativeAppChannel(source);
if (!extension)
return;
std::unique_ptr<ExtensionMessagePort> opener_port =
ExtensionMessagePort::CreateForEndpoint(
weak_factory_.GetWeakPtr(), source_port_id, extension->id(), source);
if (!opener_port->IsValidPort())
return;
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS)
bool has_permission = extension->permissions_data()->HasAPIPermission(
mojom::APIPermissionID::kNativeMessaging);
if (!has_permission) {
opener_port->DispatchOnDisconnect(kMissingPermissionError);
return;
}
BrowserContext* source_context = source.browser_context();
DCHECK(
ExtensionsBrowserClient::Get()->IsSameContext(source_context, context_));
MessagingDelegate::PolicyPermission policy_permission =
messaging_delegate_->IsNativeMessagingHostAllowed(source_context,
native_app_name);
if (policy_permission == MessagingDelegate::PolicyPermission::DISALLOW) {
opener_port->DispatchOnDisconnect(kProhibitedByPoliciesError);
return;
}
std::unique_ptr<MessageChannel> channel = std::make_unique<MessageChannel>();
channel->opener = std::move(opener_port);
channel->opener->OpenPort(source.render_process_id(), source.port_context());
content::RenderFrameHost* source_rfh =
source.is_for_render_frame() ? source.GetRenderFrameHost() : nullptr;
MaybeDisableBackForwardCacheForMessaging(source_rfh);
std::string error = kReceivingEndDoesntExistError;
const PortId receiver_port_id = source_port_id.GetOppositePortId();
std::unique_ptr<MessagePort> receiver(
messaging_delegate_->CreateReceiverForNativeApp(
context_, weak_factory_.GetWeakPtr(), source_rfh, extension->id(),
receiver_port_id, native_app_name,
policy_permission == MessagingDelegate::PolicyPermission::ALLOW_ALL,
&error));
if (!receiver.get()) {
channel->opener->DispatchOnDisconnect(error);
return;
}
channel->receiver = std::move(receiver);
channel->opener->set_should_have_strong_keepalive(true);
channel->opener->IncrementLazyKeepaliveCount(Activity::MESSAGE_PORT);
AddChannel(std::move(channel), receiver_port_id);
#else
const char kNativeMessagingNotSupportedError[] =
"Native Messaging is not supported on this platform.";
opener_port->DispatchOnDisconnect(kNativeMessagingNotSupportedError);
#endif
}
void MessageService::OpenChannelToTab(const ChannelEndpoint& source,
const PortId& source_port_id,
int tab_id,
int frame_id,
const std::string& document_id,
const std::string& extension_id,
ChannelType channel_type,
const std::string& channel_name) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_GE(frame_id, -1);
DCHECK(source_port_id.is_opener);
if (!source.IsValid())
return;
std::unique_ptr<ExtensionMessagePort> opener_port =
ExtensionMessagePort::CreateForEndpoint(
weak_factory_.GetWeakPtr(), source_port_id, extension_id, source);
if (!opener_port->IsValidPort())
return;
BrowserContext* source_context = source.browser_context();
DCHECK(
ExtensionsBrowserClient::Get()->IsSameContext(source_context, context_));
content::WebContents* receiver_contents =
#if defined(OHOS_ARKWEB_EXTENSIONS)
GetWebContentByTabId(tab_id);
#else
messaging_delegate_->GetWebContentsByTabId(source_context, tab_id);
#endif
if (!receiver_contents || receiver_contents->GetController().NeedsReload()) {
opener_port->DispatchOnDisconnect(kReceivingEndDoesntExistError);
return;
}
MaybeDisableBackForwardCacheForMessaging(
receiver_contents->GetPrimaryMainFrame());
const PortId receiver_port_id = source_port_id.GetOppositePortId();
std::unique_ptr<MessagePort> receiver =
messaging_delegate_->CreateReceiverForTab(
weak_factory_.GetWeakPtr(), extension_id, receiver_port_id,
receiver_contents, frame_id, document_id);
if (!receiver.get()) {
opener_port->DispatchOnDisconnect(kReceivingEndDoesntExistError);
return;
}
const Extension* extension = nullptr;
if (!extension_id.empty()) {
extension = ExtensionRegistry::Get(source_context)
->enabled_extensions()
.GetByID(extension_id);
DCHECK(extension);
}
BrowserContext* receiver_context = receiver_contents->GetBrowserContext();
DCHECK(ExtensionsBrowserClient::Get()->IsSameContext(receiver_context,
context_));
std::unique_ptr<OpenChannelParams> params =
std::make_unique<OpenChannelParams>(
source,
absl::nullopt,
ExtensionApiFrameIdMap::FrameData(), receiver.release(),
receiver_port_id, MessagingEndpoint::ForExtension(extension_id),
std::move(opener_port), extension_id,
GURL(),
url::Origin(),
channel_type, channel_name,
false);
OpenChannelImpl(receiver_context, std::move(params), extension,
false );
}
void MessageService::OpenChannelImpl(BrowserContext* browser_context,
std::unique_ptr<OpenChannelParams> params,
const Extension* target_extension,
bool did_enqueue) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(
ExtensionsBrowserClient::Get()->IsSameContext(browser_context, context_));
DCHECK_EQ(target_extension != nullptr, !params->target_extension_id.empty());
const ChannelEndpoint& source = params->source;
bool will_open_channel = true;
if (!source.IsValid()) {
will_open_channel = false;
}
if (!params->opener_port->IsValidPort()) {
will_open_channel = false;
}
if (!params->receiver || !params->receiver->IsValidPort()) {
will_open_channel = false;
params->opener_port->DispatchOnDisconnect(kReceivingEndDoesntExistError);
}
if (!will_open_channel) {
pending_lazy_context_channels_.erase(
params->receiver_port_id.GetChannelId());
return;
}
const PortContext& port_context = source.port_context();
params->opener_port->OpenPort(source.render_process_id(), port_context);
params->opener_port->RevalidatePort();
params->opener_port->set_is_for_onetime_channel(params->is_onetime_channel());
params->receiver->RemoveCommonFrames(*params->opener_port);
if (!params->receiver->IsValidPort()) {
params->opener_port->DispatchOnDisconnect(kReceivingEndDoesntExistError);
return;
}
params->receiver->set_is_for_onetime_channel(params->is_onetime_channel());
std::unique_ptr<MessageChannel> channel_ptr =
std::make_unique<MessageChannel>();
MessageChannel* channel = channel_ptr.get();
channel->opener = std::move(params->opener_port);
channel->receiver = std::move(params->receiver);
AddChannel(std::move(channel_ptr), params->receiver_port_id);
int guest_process_id = content::ChildProcessHost::kInvalidUniqueID;
int guest_render_frame_routing_id = MSG_ROUTING_NONE;
if (params->include_guest_process_info &&
source.is_for_render_frame()) {
guest_process_id = params->source.render_process_id();
DCHECK(port_context.frame);
guest_render_frame_routing_id = port_context.frame->routing_id;
DCHECK(WebViewGuest::FromRenderFrameHost(source.GetRenderFrameHost()));
}
channel->receiver->DispatchOnConnect(
params->channel_type, params->channel_name, std::move(params->source_tab),
params->source_frame, guest_process_id, guest_render_frame_routing_id,
params->source_endpoint, params->target_extension_id, params->source_url,
params->source_origin);
if (target_extension) {
events::HistogramValue histogram_value = events::UNKNOWN;
MessagingEndpoint::Relationship relationship =
MessagingEndpoint::GetRelationship(params->source_endpoint,
params->target_extension_id);
bool is_external = false;
switch (relationship) {
case MessagingEndpoint::Relationship::kInternal:
break;
case MessagingEndpoint::Relationship::kExternalExtension:
case MessagingEndpoint::Relationship::kExternalWebPage:
case MessagingEndpoint::Relationship::kExternalNativeApp:
is_external = true;
}
if (params->source_endpoint.type == MessagingEndpoint::Type::kNativeApp) {
histogram_value = events::RUNTIME_ON_CONNECT_NATIVE;
} else if (params->channel_name == "chrome.runtime.onRequest") {
histogram_value = is_external ? events::RUNTIME_ON_REQUEST_EXTERNAL
: events::RUNTIME_ON_REQUEST;
} else if (params->channel_name == "chrome.runtime.onMessage") {
histogram_value = is_external ? events::RUNTIME_ON_MESSAGE_EXTERNAL
: events::RUNTIME_ON_MESSAGE;
} else {
histogram_value = is_external ? events::RUNTIME_ON_CONNECT_EXTERNAL
: events::RUNTIME_ON_CONNECT;
}
EventRouter::Get(browser_context)
->ReportEvent(histogram_value, target_extension, did_enqueue);
}
std::vector<url::Origin> extended_lifetime_origins =
GetServiceWorkerExtendedLifetimeOrigins(browser_context);
url::Origin source_origin = url::Origin::Create(params->source_url);
url::Origin target_origin =
Extension::CreateOriginFromExtensionId(params->target_extension_id);
for (const url::Origin& origin : extended_lifetime_origins) {
if (origin == source_origin) {
channel->receiver->set_should_have_strong_keepalive(true);
}
if (origin == target_origin) {
channel->opener->set_should_have_strong_keepalive(true);
}
}
channel->opener->IncrementLazyKeepaliveCount(Activity::MESSAGE_PORT);
channel->receiver->IncrementLazyKeepaliveCount(Activity::MESSAGE_PORT);
}
void MessageService::AddChannel(std::unique_ptr<MessageChannel> channel,
const PortId& receiver_port_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ChannelId channel_id = receiver_port_id.GetChannelId();
CHECK(channels_.find(channel_id) == channels_.end());
channels_[channel_id] = std::move(channel);
pending_lazy_context_channels_.erase(channel_id);
}
void MessageService::OpenPort(const PortId& port_id,
int process_id,
const PortContext& port_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!port_id.is_opener);
ChannelId channel_id = port_id.GetChannelId();
auto it = channels_.find(channel_id);
if (it == channels_.end())
return;
it->second->receiver->OpenPort(process_id, port_context);
}
void MessageService::ClosePort(const PortId& port_id,
int process_id,
const PortContext& context,
bool force_close) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
int routing_id = context.frame ? context.frame->routing_id : MSG_ROUTING_NONE;
int worker_thread_id =
context.worker ? context.worker->thread_id : kMainThreadId;
ClosePortImpl(port_id, process_id, routing_id, worker_thread_id, force_close,
std::string());
}
void MessageService::CloseChannel(const PortId& port_id,
const std::string& error_message) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ClosePortImpl(port_id, content::ChildProcessHost::kInvalidUniqueID,
MSG_ROUTING_NONE, kMainThreadId, true, error_message);
}
void MessageService::ClosePortImpl(const PortId& port_id,
int process_id,
int routing_id,
int worker_thread_id,
bool force_close,
const std::string& error_message) {
ChannelId channel_id = port_id.GetChannelId();
auto it = channels_.find(channel_id);
if (it == channels_.end()) {
auto pending = pending_lazy_context_channels_.find(channel_id);
if (pending != pending_lazy_context_channels_.end()) {
const LazyContextId& context_id = pending->second;
context_id.GetTaskQueue()->AddPendingTask(
context_id,
base::BindOnce(&MessageService::PendingLazyContextClosePort,
weak_factory_.GetWeakPtr(), port_id, process_id,
routing_id, worker_thread_id, force_close,
error_message));
}
return;
}
if (force_close) {
CloseChannelImpl(it, port_id, error_message, true);
} else if (port_id.is_opener) {
it->second->opener->ClosePort(process_id, routing_id, worker_thread_id);
} else {
it->second->receiver->ClosePort(process_id, routing_id, worker_thread_id);
}
}
void MessageService::CloseChannelImpl(MessageChannelMap::iterator channel_iter,
const PortId& closing_port_id,
const std::string& error_message,
bool notify_other_port) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::unique_ptr<MessageChannel> channel = std::move(channel_iter->second);
channels_.erase(channel_iter);
if (notify_other_port) {
MessagePort* port = closing_port_id.is_opener ? channel->receiver.get()
: channel->opener.get();
port->DispatchOnDisconnect(error_message);
}
channel->opener->DecrementLazyKeepaliveCount(Activity::MESSAGE_PORT);
channel->receiver->DecrementLazyKeepaliveCount(Activity::MESSAGE_PORT);
}
void MessageService::PostMessage(const PortId& source_port_id,
const Message& message) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ChannelId channel_id = source_port_id.GetChannelId();
auto iter = channels_.find(channel_id);
if (iter == channels_.end()) {
EnqueuePendingMessage(source_port_id, channel_id, message);
return;
}
DispatchMessage(source_port_id, iter->second.get(), message);
}
void MessageService::EnqueuePendingMessage(const PortId& source_port_id,
const ChannelId& channel_id,
const Message& message) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto pending_for_incognito = pending_incognito_channels_.find(channel_id);
if (pending_for_incognito != pending_incognito_channels_.end()) {
pending_for_incognito->second.push_back(
PendingMessage(source_port_id, message));
DCHECK(!base::Contains(pending_lazy_context_channels_, channel_id));
return;
}
EnqueuePendingMessageForLazyBackgroundLoad(source_port_id, channel_id,
message);
}
void MessageService::EnqueuePendingMessageForLazyBackgroundLoad(
const PortId& source_port_id,
const ChannelId& channel_id,
const Message& message) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto pending = pending_lazy_context_channels_.find(channel_id);
if (pending == pending_lazy_context_channels_.end()) {
return;
}
const LazyContextId& context_id = pending->second;
ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
if (!registry->enabled_extensions().Contains(context_id.extension_id())) {
return;
}
context_id.GetTaskQueue()->AddPendingTask(
context_id,
base::BindOnce(&MessageService::PendingLazyContextPostMessage,
weak_factory_.GetWeakPtr(), source_port_id, message));
}
void MessageService::DispatchMessage(const PortId& source_port_id,
MessageChannel* channel,
const Message& message) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
MessagePort* dest_port = source_port_id.is_opener ? channel->receiver.get()
: channel->opener.get();
dest_port->DispatchOnMessage(message);
}
void MessageService::NotifyResponsePending(const PortId& port_id,
int process_id,
const PortContext& port_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!port_id.is_opener);
ChannelId channel_id = port_id.GetChannelId();
auto it = channels_.find(channel_id);
if (it == channels_.end())
return;
it->second->receiver->NotifyResponsePending();
}
bool MessageService::MaybeAddPendingLazyContextOpenChannelTask(
BrowserContext* context,
const Extension* extension,
std::unique_ptr<OpenChannelParams>* params,
const PendingMessagesQueue& pending_messages) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(ExtensionsBrowserClient::Get()->IsSameContext(context, context_));
const bool is_event_page = BackgroundInfo::HasLazyBackgroundPage(extension);
bool is_worker_based = false;
if (!is_event_page)
is_worker_based = BackgroundInfo::IsServiceWorkerBased(extension);
if (!is_worker_based && !is_event_page)
return false;
if (!IncognitoInfo::IsSplitMode(extension))
context = ExtensionsBrowserClient::Get()->GetOriginalContext(context);
const LazyContextId context_id = LazyContextIdFor(context, extension);
LazyContextTaskQueue* task_queue = context_id.GetTaskQueue();
if (!task_queue->ShouldEnqueueTask(context, extension))
return false;
ChannelId channel_id = (*params)->receiver_port_id.GetChannelId();
pending_lazy_context_channels_.emplace(channel_id, context_id);
task_queue->AddPendingTask(
context_id,
base::BindOnce(&MessageService::PendingLazyContextOpenChannel,
weak_factory_.GetWeakPtr(), std::move(*params)));
for (const PendingMessage& message : pending_messages) {
EnqueuePendingMessageForLazyBackgroundLoad(message.first, channel_id,
message.second);
}
return true;
}
void MessageService::OnOpenChannelAllowed(
std::unique_ptr<OpenChannelParams> params,
bool allowed) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ChannelId channel_id = params->receiver_port_id.GetChannelId();
auto pending_for_incognito = pending_incognito_channels_.find(channel_id);
if (pending_for_incognito == pending_incognito_channels_.end()) {
NOTREACHED();
return;
}
PendingMessagesQueue pending_messages;
pending_messages.swap(pending_for_incognito->second);
pending_incognito_channels_.erase(pending_for_incognito);
const ChannelEndpoint& source = params->source;
if (!source.IsValid())
return;
if (!params->opener_port->IsValidPort())
return;
if (!allowed) {
params->opener_port->DispatchOnDisconnect(kReceivingEndDoesntExistError);
return;
}
BrowserContext* context = source.browser_context();
DCHECK(ExtensionsBrowserClient::Get()->IsSameContext(context, context_));
ExtensionRegistry* registry = ExtensionRegistry::Get(context);
const Extension* target_extension =
registry->enabled_extensions().GetByID(params->target_extension_id);
if (!target_extension) {
params->opener_port->DispatchOnDisconnect(kReceivingEndDoesntExistError);
return;
}
BrowserContext* context_to_use_for_extension =
IncognitoInfo::IsSplitMode(target_extension)
? context
: ExtensionsBrowserClient::Get()->GetOriginalContext(context);
params->receiver = ExtensionMessagePort::CreateForExtension(
weak_factory_.GetWeakPtr(), params->receiver_port_id,
params->target_extension_id, context_to_use_for_extension);
if (!MaybeAddPendingLazyContextOpenChannelTask(context, target_extension,
¶ms, pending_messages)) {
OpenChannelImpl(context, std::move(params), target_extension,
false );
DispatchPendingMessages(pending_messages, channel_id);
}
}
void MessageService::PendingLazyContextOpenChannel(
std::unique_ptr<OpenChannelParams> params,
std::unique_ptr<LazyContextTaskQueue::ContextInfo> context_info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (context_info == nullptr)
return;
params->receiver = ExtensionMessagePort::CreateForExtension(
weak_factory_.GetWeakPtr(), params->receiver_port_id,
params->target_extension_id, context_info->browser_context);
const Extension* const extension =
ExtensionRegistry::Get(context_info->browser_context)
->enabled_extensions()
.GetByID(context_info->extension_id);
OpenChannelImpl(context_info->browser_context, std::move(params), extension,
true );
}
void MessageService::DispatchPendingMessages(const PendingMessagesQueue& queue,
const ChannelId& channel_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto channel_iter = channels_.find(channel_id);
if (channel_iter != channels_.end()) {
for (const PendingMessage& message : queue) {
DispatchMessage(message.first, channel_iter->second.get(),
message.second);
}
}
}
}