1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/messaging/message_service.h"
7 #include "base/atomic_sequence_num.h"
9 #include "base/callback.h"
10 #include "base/json/json_writer.h"
11 #include "base/lazy_instance.h"
12 #include "base/metrics/histogram.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/stl_util.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/extensions/api/messaging/extension_message_port.h"
17 #include "chrome/browser/extensions/api/messaging/incognito_connectability.h"
18 #include "chrome/browser/extensions/api/messaging/native_message_port.h"
19 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/extension_tab_util.h"
22 #include "chrome/browser/extensions/extension_util.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/tab_contents/tab_util.h"
25 #include "components/guest_view/common/guest_view_constants.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/notification_service.h"
28 #include "content/public/browser/render_frame_host.h"
29 #include "content/public/browser/render_process_host.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "content/public/browser/render_widget_host.h"
32 #include "content/public/browser/render_widget_host_view.h"
33 #include "content/public/browser/site_instance.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/common/child_process_host.h"
36 #include "extensions/browser/event_router.h"
37 #include "extensions/browser/extension_host.h"
38 #include "extensions/browser/extension_registry.h"
39 #include "extensions/browser/extension_system.h"
40 #include "extensions/browser/extension_util.h"
41 #include "extensions/browser/extensions_browser_client.h"
42 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
43 #include "extensions/browser/lazy_background_task_queue.h"
44 #include "extensions/browser/pref_names.h"
45 #include "extensions/browser/process_manager.h"
46 #include "extensions/common/extension.h"
47 #include "extensions/common/manifest_constants.h"
48 #include "extensions/common/manifest_handlers/background_info.h"
49 #include "extensions/common/manifest_handlers/externally_connectable.h"
50 #include "extensions/common/manifest_handlers/incognito_info.h"
51 #include "extensions/common/permissions/permissions_data.h"
52 #include "net/base/completion_callback.h"
55 using content::BrowserContext
;
56 using content::BrowserThread
;
57 using content::SiteInstance
;
58 using content::WebContents
;
60 // Since we have 2 ports for every channel, we just index channels by half the
62 #define GET_CHANNEL_ID(port_id) ((port_id) / 2)
63 #define GET_CHANNEL_OPENER_ID(channel_id) ((channel_id) * 2)
64 #define GET_CHANNEL_RECEIVERS_ID(channel_id) ((channel_id) * 2 + 1)
66 // Port1 is always even, port2 is always odd.
67 #define IS_OPENER_PORT_ID(port_id) (((port_id) & 1) == 0)
69 // Change even to odd and vice versa, to get the other side of a given channel.
70 #define GET_OPPOSITE_PORT_ID(source_port_id) ((source_port_id) ^ 1)
72 namespace extensions
{
74 MessageService::PolicyPermission
MessageService::IsNativeMessagingHostAllowed(
75 const PrefService
* pref_service
,
76 const std::string
& native_host_name
) {
77 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
79 PolicyPermission allow_result
= ALLOW_ALL
;
80 if (pref_service
->IsManagedPreference(
81 pref_names::kNativeMessagingUserLevelHosts
)) {
82 if (!pref_service
->GetBoolean(pref_names::kNativeMessagingUserLevelHosts
))
83 allow_result
= ALLOW_SYSTEM_ONLY
;
86 // All native messaging hosts are allowed if there is no blacklist.
87 if (!pref_service
->IsManagedPreference(pref_names::kNativeMessagingBlacklist
))
89 const base::ListValue
* blacklist
=
90 pref_service
->GetList(pref_names::kNativeMessagingBlacklist
);
94 // Check if the name or the wildcard is in the blacklist.
95 base::StringValue
name_value(native_host_name
);
96 base::StringValue
wildcard_value("*");
97 if (blacklist
->Find(name_value
) == blacklist
->end() &&
98 blacklist
->Find(wildcard_value
) == blacklist
->end()) {
102 // The native messaging host is blacklisted. Check the whitelist.
103 if (pref_service
->IsManagedPreference(
104 pref_names::kNativeMessagingWhitelist
)) {
105 const base::ListValue
* whitelist
=
106 pref_service
->GetList(pref_names::kNativeMessagingWhitelist
);
107 if (whitelist
&& whitelist
->Find(name_value
) != whitelist
->end())
114 const char kReceivingEndDoesntExistError
[] =
115 "Could not establish connection. Receiving end does not exist.";
116 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
117 const char kMissingPermissionError
[] =
118 "Access to native messaging requires nativeMessaging permission.";
119 const char kProhibitedByPoliciesError
[] =
120 "Access to the native messaging host was disabled by the system "
124 struct MessageService::MessageChannel
{
125 scoped_ptr
<MessagePort
> opener
;
126 scoped_ptr
<MessagePort
> receiver
;
129 struct MessageService::OpenChannelParams
{
130 int source_process_id
;
131 scoped_ptr
<base::DictionaryValue
> source_tab
;
134 scoped_ptr
<MessagePort
> receiver
;
135 int receiver_port_id
;
136 std::string source_extension_id
;
137 std::string target_extension_id
;
139 std::string channel_name
;
140 bool include_tls_channel_id
;
141 std::string tls_channel_id
;
142 bool include_guest_process_info
;
144 // Takes ownership of receiver.
145 OpenChannelParams(int source_process_id
,
146 scoped_ptr
<base::DictionaryValue
> source_tab
,
149 MessagePort
* receiver
,
150 int receiver_port_id
,
151 const std::string
& source_extension_id
,
152 const std::string
& target_extension_id
,
153 const GURL
& source_url
,
154 const std::string
& channel_name
,
155 bool include_tls_channel_id
,
156 bool include_guest_process_info
)
157 : source_process_id(source_process_id
),
158 source_frame_id(source_frame_id
),
159 target_frame_id(target_frame_id
),
161 receiver_port_id(receiver_port_id
),
162 source_extension_id(source_extension_id
),
163 target_extension_id(target_extension_id
),
164 source_url(source_url
),
165 channel_name(channel_name
),
166 include_tls_channel_id(include_tls_channel_id
),
167 include_guest_process_info(include_guest_process_info
) {
169 this->source_tab
= source_tab
.Pass();
173 DISALLOW_COPY_AND_ASSIGN(OpenChannelParams
);
178 static base::StaticAtomicSequenceNumber g_next_channel_id
;
180 static content::RenderProcessHost
* GetExtensionProcess(
181 BrowserContext
* context
,
182 const std::string
& extension_id
) {
183 scoped_refptr
<SiteInstance
> site_instance
=
184 ProcessManager::Get(context
)->GetSiteInstanceForURL(
185 Extension::GetBaseURLFromExtensionId(extension_id
));
186 return site_instance
->HasProcess() ? site_instance
->GetProcess() : NULL
;
191 content::RenderProcessHost
*
192 MessageService::MessagePort::GetRenderProcessHost() {
197 void MessageService::AllocatePortIdPair(int* port1
, int* port2
) {
198 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
200 unsigned channel_id
=
201 static_cast<unsigned>(g_next_channel_id
.GetNext()) % (kint32max
/2);
202 unsigned port1_id
= channel_id
* 2;
203 unsigned port2_id
= channel_id
* 2 + 1;
205 // Sanity checks to make sure our channel<->port converters are correct.
206 DCHECK(IS_OPENER_PORT_ID(port1_id
));
207 DCHECK_EQ(GET_OPPOSITE_PORT_ID(port1_id
), port2_id
);
208 DCHECK_EQ(GET_OPPOSITE_PORT_ID(port2_id
), port1_id
);
209 DCHECK_EQ(GET_CHANNEL_ID(port1_id
), GET_CHANNEL_ID(port2_id
));
210 DCHECK_EQ(GET_CHANNEL_ID(port1_id
), channel_id
);
211 DCHECK_EQ(GET_CHANNEL_OPENER_ID(channel_id
), port1_id
);
212 DCHECK_EQ(GET_CHANNEL_RECEIVERS_ID(channel_id
), port2_id
);
218 MessageService::MessageService(BrowserContext
* context
)
219 : lazy_background_task_queue_(
220 LazyBackgroundTaskQueue::Get(context
)),
221 weak_factory_(this) {
222 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
224 registrar_
.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
,
225 content::NotificationService::AllBrowserContextsAndSources());
226 registrar_
.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
227 content::NotificationService::AllBrowserContextsAndSources());
230 MessageService::~MessageService() {
231 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
233 STLDeleteContainerPairSecondPointers(channels_
.begin(), channels_
.end());
237 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<MessageService
> >
238 g_factory
= LAZY_INSTANCE_INITIALIZER
;
241 BrowserContextKeyedAPIFactory
<MessageService
>*
242 MessageService::GetFactoryInstance() {
243 return g_factory
.Pointer();
247 MessageService
* MessageService::Get(BrowserContext
* context
) {
248 return BrowserContextKeyedAPIFactory
<MessageService
>::Get(context
);
251 void MessageService::OpenChannelToExtension(
252 int source_process_id
, int source_routing_id
, int receiver_port_id
,
253 const std::string
& source_extension_id
,
254 const std::string
& target_extension_id
,
255 const GURL
& source_url
,
256 const std::string
& channel_name
,
257 bool include_tls_channel_id
) {
258 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
260 content::RenderProcessHost
* source
=
261 content::RenderProcessHost::FromID(source_process_id
);
264 BrowserContext
* context
= source
->GetBrowserContext();
266 ExtensionRegistry
* registry
= ExtensionRegistry::Get(context
);
267 const Extension
* target_extension
=
268 registry
->enabled_extensions().GetByID(target_extension_id
);
269 if (!target_extension
) {
270 DispatchOnDisconnect(
271 source
, receiver_port_id
, kReceivingEndDoesntExistError
);
275 bool is_web_connection
= false;
277 if (source_extension_id
!= target_extension_id
) {
278 // It's an external connection. Check the externally_connectable manifest
279 // key if it's present. If it's not, we allow connection from any extension
281 ExternallyConnectableInfo
* externally_connectable
=
282 static_cast<ExternallyConnectableInfo
*>(
283 target_extension
->GetManifestData(
284 manifest_keys::kExternallyConnectable
));
285 bool is_externally_connectable
= false;
287 if (externally_connectable
) {
288 if (source_extension_id
.empty()) {
289 // No source extension ID so the source was a web page. Check that the
291 is_web_connection
= true;
292 is_externally_connectable
=
293 externally_connectable
->matches
.MatchesURL(source_url
);
294 // Only include the TLS channel ID for externally connected web pages.
295 include_tls_channel_id
&=
296 is_externally_connectable
&&
297 externally_connectable
->accepts_tls_channel_id
;
299 // Source extension ID so the source was an extension. Check that the
300 // extension matches.
301 is_externally_connectable
=
302 externally_connectable
->IdCanConnect(source_extension_id
);
305 // Default behaviour. Any extension, no webpages.
306 is_externally_connectable
= !source_extension_id
.empty();
309 if (!is_externally_connectable
) {
310 // Important: use kReceivingEndDoesntExistError here so that we don't
311 // leak information about this extension to callers. This way it's
312 // indistinguishable from the extension just not existing.
313 DispatchOnDisconnect(
314 source
, receiver_port_id
, kReceivingEndDoesntExistError
);
319 WebContents
* source_contents
= tab_util::GetWebContentsByFrameID(
320 source_process_id
, source_routing_id
);
322 bool include_guest_process_info
= false;
324 // Include info about the opener's tab (if it was a tab).
325 scoped_ptr
<base::DictionaryValue
> source_tab
;
326 int source_frame_id
= -1;
327 if (source_contents
&& ExtensionTabUtil::GetTabId(source_contents
) >= 0) {
328 // Only the tab id is useful to platform apps for internal use. The
329 // unnecessary bits will be stripped out in
330 // MessagingBindings::DispatchOnConnect().
331 source_tab
.reset(ExtensionTabUtil::CreateTabValue(source_contents
));
333 content::RenderFrameHost
* rfh
=
334 content::RenderFrameHost::FromID(source_process_id
, source_routing_id
);
335 // Main frame's frameId is 0.
337 source_frame_id
= !rfh
->GetParent() ? 0 : source_routing_id
;
339 // Check to see if it was a WebView making the request.
340 // Sending messages from WebViews to extensions breaks webview isolation,
341 // so only allow component extensions to receive messages from WebViews.
342 bool is_web_view
= !!WebViewGuest::FromWebContents(source_contents
);
343 if (is_web_view
&& extensions::Manifest::IsComponentLocation(
344 target_extension
->location())) {
345 include_guest_process_info
= true;
346 auto* rfh
= content::RenderFrameHost::FromID(source_process_id
,
348 // Include |source_frame_id| so that we can retrieve the guest's frame
349 // routing id in OpenChannelImpl.
351 source_frame_id
= source_routing_id
;
355 scoped_ptr
<OpenChannelParams
> params(new OpenChannelParams(
356 source_process_id
, source_tab
.Pass(), source_frame_id
,
357 -1, // no target_frame_id for a channel to an extension/background page.
358 nullptr, receiver_port_id
, source_extension_id
, target_extension_id
,
359 source_url
, channel_name
, include_tls_channel_id
,
360 include_guest_process_info
));
362 pending_incognito_channels_
[GET_CHANNEL_ID(params
->receiver_port_id
)] =
363 PendingMessagesQueue();
364 if (context
->IsOffTheRecord() &&
365 !util::IsIncognitoEnabled(target_extension_id
, context
)) {
366 // Give the user a chance to accept an incognito connection from the web if
367 // they haven't already, with the conditions:
368 // - Only for spanning-mode incognito. We don't want the complication of
369 // spinning up an additional process here which might need to do some
370 // setup that we're not expecting.
371 // - Only for extensions that can't normally be enabled in incognito, since
372 // that surface (e.g. chrome://extensions) should be the only one for
373 // enabling in incognito. In practice this means platform apps only.
374 if (!is_web_connection
|| IncognitoInfo::IsSplitMode(target_extension
) ||
375 util::CanBeIncognitoEnabled(target_extension
)) {
376 OnOpenChannelAllowed(params
.Pass(), false);
380 // If the target extension isn't even listening for connect/message events,
381 // there is no need to go any further and the connection should be
382 // rejected without showing a prompt. See http://crbug.com/442497
383 EventRouter
* event_router
= EventRouter::Get(context
);
384 const char* const events
[] = {"runtime.onConnectExternal",
385 "runtime.onMessageExternal",
386 "extension.onRequestExternal",
388 bool has_event_listener
= false;
389 for (const char* const* event
= events
; *event
; event
++) {
390 has_event_listener
|=
391 event_router
->ExtensionHasEventListener(target_extension_id
, *event
);
393 if (!has_event_listener
) {
394 OnOpenChannelAllowed(params
.Pass(), false);
398 // This check may show a dialog.
399 IncognitoConnectability::Get(context
)
400 ->Query(target_extension
, source_contents
, source_url
,
401 base::Bind(&MessageService::OnOpenChannelAllowed
,
402 weak_factory_
.GetWeakPtr(), base::Passed(¶ms
)));
406 OnOpenChannelAllowed(params
.Pass(), true);
409 void MessageService::OpenChannelToNativeApp(
410 int source_process_id
,
411 int source_routing_id
,
412 int receiver_port_id
,
413 const std::string
& source_extension_id
,
414 const std::string
& native_app_name
) {
415 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
417 content::RenderProcessHost
* source
=
418 content::RenderProcessHost::FromID(source_process_id
);
422 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
423 Profile
* profile
= Profile::FromBrowserContext(source
->GetBrowserContext());
424 ExtensionService
* extension_service
=
425 ExtensionSystem::Get(profile
)->extension_service();
426 bool has_permission
= false;
427 if (extension_service
) {
428 const Extension
* extension
=
429 extension_service
->GetExtensionById(source_extension_id
, false);
430 has_permission
= extension
&&
431 extension
->permissions_data()->HasAPIPermission(
432 APIPermission::kNativeMessaging
);
435 if (!has_permission
) {
436 DispatchOnDisconnect(source
, receiver_port_id
, kMissingPermissionError
);
440 PrefService
* pref_service
= profile
->GetPrefs();
442 // Verify that the host is not blocked by policies.
443 PolicyPermission policy_permission
=
444 IsNativeMessagingHostAllowed(pref_service
, native_app_name
);
445 if (policy_permission
== DISALLOW
) {
446 DispatchOnDisconnect(source
, receiver_port_id
, kProhibitedByPoliciesError
);
450 scoped_ptr
<MessageChannel
> channel(new MessageChannel());
451 channel
->opener
.reset(new ExtensionMessagePort(source
, MSG_ROUTING_CONTROL
,
452 source_extension_id
));
454 // Get handle of the native view and pass it to the native messaging host.
455 content::RenderFrameHost
* render_frame_host
=
456 content::RenderFrameHost::FromID(source_process_id
, source_routing_id
);
457 gfx::NativeView native_view
=
458 render_frame_host
? render_frame_host
->GetNativeView() : nullptr;
460 std::string error
= kReceivingEndDoesntExistError
;
461 scoped_ptr
<NativeMessageHost
> native_host
= NativeMessageHost::Create(
465 policy_permission
== ALLOW_ALL
,
468 // Abandon the channel.
469 if (!native_host
.get()) {
470 LOG(ERROR
) << "Failed to create native process.";
471 DispatchOnDisconnect(source
, receiver_port_id
, error
);
474 channel
->receiver
.reset(new NativeMessagePort(
475 weak_factory_
.GetWeakPtr(), receiver_port_id
, native_host
.Pass()));
477 // Keep the opener alive until the channel is closed.
478 channel
->opener
->IncrementLazyKeepaliveCount();
480 AddChannel(channel
.release(), receiver_port_id
);
481 #else // !(defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX))
482 const char kNativeMessagingNotSupportedError
[] =
483 "Native Messaging is not supported on this platform.";
484 DispatchOnDisconnect(
485 source
, receiver_port_id
, kNativeMessagingNotSupportedError
);
486 #endif // !(defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX))
489 void MessageService::OpenChannelToTab(int source_process_id
,
490 int receiver_port_id
,
493 const std::string
& extension_id
,
494 const std::string
& channel_name
) {
495 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
497 content::RenderProcessHost
* source
=
498 content::RenderProcessHost::FromID(source_process_id
);
501 Profile
* profile
= Profile::FromBrowserContext(source
->GetBrowserContext());
503 WebContents
* contents
= NULL
;
504 scoped_ptr
<MessagePort
> receiver
;
505 if (!ExtensionTabUtil::GetTabById(tab_id
, profile
, true, NULL
, NULL
,
507 contents
->GetController().NeedsReload()) {
508 // The tab isn't loaded yet. Don't attempt to connect.
509 DispatchOnDisconnect(
510 source
, receiver_port_id
, kReceivingEndDoesntExistError
);
514 int receiver_routing_id
;
516 // Positive frame ID is child frame.
517 int receiver_process_id
= contents
->GetRenderProcessHost()->GetID();
518 if (!content::RenderFrameHost::FromID(receiver_process_id
, frame_id
)) {
519 // Frame does not exist.
520 DispatchOnDisconnect(
521 source
, receiver_port_id
, kReceivingEndDoesntExistError
);
524 receiver_routing_id
= frame_id
;
525 } else if (frame_id
== 0) {
526 // Frame ID 0 is main frame.
527 receiver_routing_id
= contents
->GetMainFrame()->GetRoutingID();
529 DCHECK_EQ(-1, frame_id
);
530 // If the frame ID is not set (i.e. -1), then the channel has to be opened
532 // TODO(robwu): Update logic so that frames that are not hosted in the main
533 // frame's process can also receive the port.
534 receiver_routing_id
= MSG_ROUTING_CONTROL
;
536 receiver
.reset(new ExtensionMessagePort(contents
->GetRenderProcessHost(),
537 receiver_routing_id
, extension_id
));
539 const Extension
* extension
= nullptr;
540 if (!extension_id
.empty()) {
541 // Source extension == target extension so the extension must exist, or
542 // where did the IPC come from?
543 extension
= ExtensionRegistry::Get(profile
)->enabled_extensions().GetByID(
548 scoped_ptr
<OpenChannelParams
> params(new OpenChannelParams(
550 scoped_ptr
<base::DictionaryValue
>(), // Source tab doesn't make sense
551 // for opening to tabs.
552 -1, // If there is no tab, then there is no frame either.
554 receiver
.release(), receiver_port_id
, extension_id
, extension_id
,
555 GURL(), // Source URL doesn't make sense for opening to tabs.
557 false, // Connections to tabs don't get TLS channel IDs.
558 false)); // Connections to tabs aren't webview guests.
559 OpenChannelImpl(contents
->GetBrowserContext(), params
.Pass(), extension
,
560 false /* did_enqueue */);
563 void MessageService::OpenChannelImpl(BrowserContext
* browser_context
,
564 scoped_ptr
<OpenChannelParams
> params
,
565 const Extension
* target_extension
,
567 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
568 DCHECK_EQ(target_extension
!= nullptr, !params
->target_extension_id
.empty());
570 content::RenderProcessHost
* source
=
571 content::RenderProcessHost::FromID(params
->source_process_id
);
573 return; // Closed while in flight.
575 if (!params
->receiver
|| !params
->receiver
->GetRenderProcessHost()) {
576 DispatchOnDisconnect(source
, params
->receiver_port_id
,
577 kReceivingEndDoesntExistError
);
581 MessageChannel
* channel(new MessageChannel
);
582 channel
->opener
.reset(new ExtensionMessagePort(source
, MSG_ROUTING_CONTROL
,
583 params
->source_extension_id
));
584 channel
->receiver
.reset(params
->receiver
.release());
585 AddChannel(channel
, params
->receiver_port_id
);
587 int guest_process_id
= content::ChildProcessHost::kInvalidUniqueID
;
588 int guest_render_frame_routing_id
= MSG_ROUTING_NONE
;
589 if (params
->include_guest_process_info
) {
590 guest_process_id
= params
->source_process_id
;
591 guest_render_frame_routing_id
= params
->source_frame_id
;
592 auto* guest_rfh
= content::RenderFrameHost::FromID(
593 guest_process_id
, guest_render_frame_routing_id
);
594 // Reset the |source_frame_id| parameter.
595 params
->source_frame_id
= -1;
597 DCHECK(guest_rfh
== nullptr ||
598 WebViewGuest::FromWebContents(
599 WebContents::FromRenderFrameHost(guest_rfh
)) != nullptr);
602 // Send the connect event to the receiver. Give it the opener's port ID (the
603 // opener has the opposite port ID).
604 channel
->receiver
->DispatchOnConnect(
605 params
->receiver_port_id
, params
->channel_name
, params
->source_tab
.Pass(),
606 params
->source_frame_id
, params
->target_frame_id
, guest_process_id
,
607 guest_render_frame_routing_id
, params
->source_extension_id
,
608 params
->target_extension_id
, params
->source_url
, params
->tls_channel_id
);
610 // Report the event to the event router, if the target is an extension.
612 // First, determine what event this will be (runtime.onConnect vs
613 // runtime.onMessage etc), and what the event target is (view vs background
616 // Yes - even though this is opening a channel, they may actually be
617 // runtime.onRequest/onMessage events because those single-use events are
618 // built using the connect framework (see messaging.js).
620 // Likewise, if you're wondering about native messaging events, these are
621 // only initiated *by* the extension, so aren't really events, just the
622 // endpoint of a communication channel.
623 if (target_extension
) {
624 events::HistogramValue histogram_value
= events::UNKNOWN
;
626 params
->source_extension_id
!= params
->target_extension_id
;
627 if (params
->channel_name
== "chrome.runtime.onRequest") {
628 histogram_value
= is_external
? events::RUNTIME_ON_REQUEST_EXTERNAL
629 : events::RUNTIME_ON_REQUEST
;
630 } else if (params
->channel_name
== "chrome.runtime.onMessage") {
631 histogram_value
= is_external
? events::RUNTIME_ON_MESSAGE_EXTERNAL
632 : events::RUNTIME_ON_MESSAGE
;
634 histogram_value
= is_external
? events::RUNTIME_ON_CONNECT_EXTERNAL
635 : events::RUNTIME_ON_CONNECT
;
637 EventRouter::Get(browser_context
)
638 ->ReportEvent(histogram_value
, target_extension
, did_enqueue
);
641 // Keep both ends of the channel alive until the channel is closed.
642 channel
->opener
->IncrementLazyKeepaliveCount();
643 channel
->receiver
->IncrementLazyKeepaliveCount();
646 void MessageService::AddChannel(MessageChannel
* channel
, int receiver_port_id
) {
647 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
649 int channel_id
= GET_CHANNEL_ID(receiver_port_id
);
650 CHECK(channels_
.find(channel_id
) == channels_
.end());
651 channels_
[channel_id
] = channel
;
652 pending_lazy_background_page_channels_
.erase(channel_id
);
655 void MessageService::CloseChannel(int port_id
,
656 const std::string
& error_message
) {
657 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
659 // Note: The channel might be gone already, if the other side closed first.
660 int channel_id
= GET_CHANNEL_ID(port_id
);
661 MessageChannelMap::iterator it
= channels_
.find(channel_id
);
662 if (it
== channels_
.end()) {
663 PendingLazyBackgroundPageChannelMap::iterator pending
=
664 pending_lazy_background_page_channels_
.find(channel_id
);
665 if (pending
!= pending_lazy_background_page_channels_
.end()) {
666 lazy_background_task_queue_
->AddPendingTask(
667 pending
->second
.first
, pending
->second
.second
,
668 base::Bind(&MessageService::PendingLazyBackgroundPageCloseChannel
,
669 weak_factory_
.GetWeakPtr(), port_id
, error_message
));
673 CloseChannelImpl(it
, port_id
, error_message
, true);
676 void MessageService::CloseChannelImpl(
677 MessageChannelMap::iterator channel_iter
,
679 const std::string
& error_message
,
680 bool notify_other_port
) {
681 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
683 MessageChannel
* channel
= channel_iter
->second
;
685 // Notify the other side.
686 if (notify_other_port
) {
687 MessagePort
* port
= IS_OPENER_PORT_ID(closing_port_id
) ?
688 channel
->receiver
.get() : channel
->opener
.get();
689 port
->DispatchOnDisconnect(GET_OPPOSITE_PORT_ID(closing_port_id
),
693 // Balance the IncrementLazyKeepaliveCount() in OpenChannelImpl.
694 channel
->opener
->DecrementLazyKeepaliveCount();
695 channel
->receiver
->DecrementLazyKeepaliveCount();
697 delete channel_iter
->second
;
698 channels_
.erase(channel_iter
);
701 void MessageService::PostMessage(int source_port_id
, const Message
& message
) {
702 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
704 int channel_id
= GET_CHANNEL_ID(source_port_id
);
705 MessageChannelMap::iterator iter
= channels_
.find(channel_id
);
706 if (iter
== channels_
.end()) {
707 // If this channel is pending, queue up the PostMessage to run once
708 // the channel opens.
709 EnqueuePendingMessage(source_port_id
, channel_id
, message
);
713 DispatchMessage(source_port_id
, iter
->second
, message
);
716 void MessageService::Observe(int type
,
717 const content::NotificationSource
& source
,
718 const content::NotificationDetails
& details
) {
719 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
722 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
:
723 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED
: {
724 content::RenderProcessHost
* renderer
=
725 content::Source
<content::RenderProcessHost
>(source
).ptr();
726 OnProcessClosed(renderer
);
735 void MessageService::OnProcessClosed(content::RenderProcessHost
* process
) {
736 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
738 // Close any channels that share this renderer. We notify the opposite
739 // port that its pair has closed.
740 for (MessageChannelMap::iterator it
= channels_
.begin();
741 it
!= channels_
.end(); ) {
742 MessageChannelMap::iterator current
= it
++;
744 content::RenderProcessHost
* opener_process
=
745 current
->second
->opener
->GetRenderProcessHost();
746 content::RenderProcessHost
* receiver_process
=
747 current
->second
->receiver
->GetRenderProcessHost();
749 // Only notify the other side if it has a different porocess host.
750 bool notify_other_port
= opener_process
&& receiver_process
&&
751 opener_process
!= receiver_process
;
753 if (opener_process
== process
) {
754 CloseChannelImpl(current
, GET_CHANNEL_OPENER_ID(current
->first
),
755 std::string(), notify_other_port
);
756 } else if (receiver_process
== process
) {
757 CloseChannelImpl(current
, GET_CHANNEL_RECEIVERS_ID(current
->first
),
758 std::string(), notify_other_port
);
763 void MessageService::EnqueuePendingMessage(int source_port_id
,
765 const Message
& message
) {
766 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
768 PendingChannelMap::iterator pending_for_incognito
=
769 pending_incognito_channels_
.find(channel_id
);
770 if (pending_for_incognito
!= pending_incognito_channels_
.end()) {
771 pending_for_incognito
->second
.push_back(
772 PendingMessage(source_port_id
, message
));
773 // A channel should only be holding pending messages because it is in one
775 DCHECK(!ContainsKey(pending_tls_channel_id_channels_
, channel_id
));
776 DCHECK(!ContainsKey(pending_lazy_background_page_channels_
, channel_id
));
779 PendingChannelMap::iterator pending_for_tls_channel_id
=
780 pending_tls_channel_id_channels_
.find(channel_id
);
781 if (pending_for_tls_channel_id
!= pending_tls_channel_id_channels_
.end()) {
782 pending_for_tls_channel_id
->second
.push_back(
783 PendingMessage(source_port_id
, message
));
784 // A channel should only be holding pending messages because it is in one
786 DCHECK(!ContainsKey(pending_lazy_background_page_channels_
, channel_id
));
789 EnqueuePendingMessageForLazyBackgroundLoad(source_port_id
,
794 void MessageService::EnqueuePendingMessageForLazyBackgroundLoad(
797 const Message
& message
) {
798 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
800 PendingLazyBackgroundPageChannelMap::iterator pending
=
801 pending_lazy_background_page_channels_
.find(channel_id
);
802 if (pending
!= pending_lazy_background_page_channels_
.end()) {
803 lazy_background_task_queue_
->AddPendingTask(
804 pending
->second
.first
, pending
->second
.second
,
805 base::Bind(&MessageService::PendingLazyBackgroundPagePostMessage
,
806 weak_factory_
.GetWeakPtr(), source_port_id
, message
));
810 void MessageService::DispatchMessage(int source_port_id
,
811 MessageChannel
* channel
,
812 const Message
& message
) {
813 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
815 // Figure out which port the ID corresponds to.
816 int dest_port_id
= GET_OPPOSITE_PORT_ID(source_port_id
);
817 MessagePort
* port
= IS_OPENER_PORT_ID(dest_port_id
) ?
818 channel
->opener
.get() : channel
->receiver
.get();
820 port
->DispatchOnMessage(message
, dest_port_id
);
823 bool MessageService::MaybeAddPendingLazyBackgroundPageOpenChannelTask(
824 BrowserContext
* context
,
825 const Extension
* extension
,
826 scoped_ptr
<OpenChannelParams
>* params
,
827 const PendingMessagesQueue
& pending_messages
) {
828 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
830 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
833 // If the extension uses spanning incognito mode, make sure we're always
834 // using the original profile since that is what the extension process
836 if (!IncognitoInfo::IsSplitMode(extension
))
837 context
= ExtensionsBrowserClient::Get()->GetOriginalContext(context
);
839 if (!lazy_background_task_queue_
->ShouldEnqueueTask(context
, extension
))
842 int channel_id
= GET_CHANNEL_ID((*params
)->receiver_port_id
);
843 pending_lazy_background_page_channels_
[channel_id
] =
844 PendingLazyBackgroundPageChannel(context
, extension
->id());
845 int source_id
= (*params
)->source_process_id
;
846 lazy_background_task_queue_
->AddPendingTask(
847 context
, extension
->id(),
848 base::Bind(&MessageService::PendingLazyBackgroundPageOpenChannel
,
849 weak_factory_
.GetWeakPtr(), base::Passed(params
), source_id
));
851 for (const PendingMessage
& message
: pending_messages
) {
852 EnqueuePendingMessageForLazyBackgroundLoad(message
.first
, channel_id
,
858 void MessageService::OnOpenChannelAllowed(scoped_ptr
<OpenChannelParams
> params
,
860 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
862 int channel_id
= GET_CHANNEL_ID(params
->receiver_port_id
);
864 PendingChannelMap::iterator pending_for_incognito
=
865 pending_incognito_channels_
.find(channel_id
);
866 if (pending_for_incognito
== pending_incognito_channels_
.end()) {
870 PendingMessagesQueue pending_messages
;
871 pending_messages
.swap(pending_for_incognito
->second
);
872 pending_incognito_channels_
.erase(pending_for_incognito
);
874 // Re-lookup the source process since it may no longer be valid.
875 content::RenderProcessHost
* source
=
876 content::RenderProcessHost::FromID(params
->source_process_id
);
882 DispatchOnDisconnect(source
, params
->receiver_port_id
,
883 kReceivingEndDoesntExistError
);
887 BrowserContext
* context
= source
->GetBrowserContext();
889 // Note: we use the source's profile here. If the source is an incognito
890 // process, we will use the incognito EPM to find the right extension process,
891 // which depends on whether the extension uses spanning or split mode.
892 params
->receiver
.reset(new ExtensionMessagePort(
893 GetExtensionProcess(context
, params
->target_extension_id
),
894 MSG_ROUTING_CONTROL
, params
->target_extension_id
));
896 // If the target requests the TLS channel id, begin the lookup for it.
897 // The target might also be a lazy background page, checked next, but the
898 // loading of lazy background pages continues asynchronously, so enqueue
899 // messages awaiting TLS channel ID first.
900 if (params
->include_tls_channel_id
) {
901 // Transfer pending messages to the next pending channel list.
902 pending_tls_channel_id_channels_
[channel_id
].swap(pending_messages
);
903 // Capture this reference before params is invalidated by base::Passed().
904 const GURL
& source_url
= params
->source_url
;
905 property_provider_
.GetChannelID(
906 Profile::FromBrowserContext(context
), source_url
,
907 base::Bind(&MessageService::GotChannelID
, weak_factory_
.GetWeakPtr(),
908 base::Passed(¶ms
)));
912 ExtensionRegistry
* registry
= ExtensionRegistry::Get(context
);
913 const Extension
* target_extension
=
914 registry
->enabled_extensions().GetByID(params
->target_extension_id
);
915 if (!target_extension
) {
916 DispatchOnDisconnect(source
, params
->receiver_port_id
,
917 kReceivingEndDoesntExistError
);
921 // The target might be a lazy background page. In that case, we have to check
922 // if it is loaded and ready, and if not, queue up the task and load the
924 if (!MaybeAddPendingLazyBackgroundPageOpenChannelTask(
925 context
, target_extension
, ¶ms
, pending_messages
)) {
926 OpenChannelImpl(context
, params
.Pass(), target_extension
,
927 false /* did_enqueue */);
928 DispatchPendingMessages(pending_messages
, channel_id
);
932 void MessageService::GotChannelID(scoped_ptr
<OpenChannelParams
> params
,
933 const std::string
& tls_channel_id
) {
934 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
936 params
->tls_channel_id
.assign(tls_channel_id
);
937 int channel_id
= GET_CHANNEL_ID(params
->receiver_port_id
);
939 PendingChannelMap::iterator pending_for_tls_channel_id
=
940 pending_tls_channel_id_channels_
.find(channel_id
);
941 if (pending_for_tls_channel_id
== pending_tls_channel_id_channels_
.end()) {
945 PendingMessagesQueue pending_messages
;
946 pending_messages
.swap(pending_for_tls_channel_id
->second
);
947 pending_tls_channel_id_channels_
.erase(pending_for_tls_channel_id
);
949 // Re-lookup the source process since it may no longer be valid.
950 content::RenderProcessHost
* source
=
951 content::RenderProcessHost::FromID(params
->source_process_id
);
956 BrowserContext
* context
= source
->GetBrowserContext();
957 ExtensionRegistry
* registry
= ExtensionRegistry::Get(context
);
958 const Extension
* target_extension
=
959 registry
->enabled_extensions().GetByID(params
->target_extension_id
);
960 if (!target_extension
) {
961 DispatchOnDisconnect(source
, params
->receiver_port_id
,
962 kReceivingEndDoesntExistError
);
966 if (!MaybeAddPendingLazyBackgroundPageOpenChannelTask(
967 context
, target_extension
, ¶ms
, pending_messages
)) {
968 OpenChannelImpl(context
, params
.Pass(), target_extension
,
969 false /* did_enqueue */);
970 DispatchPendingMessages(pending_messages
, channel_id
);
974 void MessageService::PendingLazyBackgroundPageOpenChannel(
975 scoped_ptr
<OpenChannelParams
> params
,
976 int source_process_id
,
977 ExtensionHost
* host
) {
978 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
981 return; // TODO(mpcomplete): notify source of disconnect?
983 params
->receiver
.reset(new ExtensionMessagePort(host
->render_process_host(),
985 params
->target_extension_id
));
986 OpenChannelImpl(host
->browser_context(), params
.Pass(), host
->extension(),
987 true /* did_enqueue */);
990 void MessageService::DispatchOnDisconnect(content::RenderProcessHost
* source
,
992 const std::string
& error_message
) {
993 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
995 ExtensionMessagePort
port(source
, MSG_ROUTING_CONTROL
, "");
996 port
.DispatchOnDisconnect(GET_OPPOSITE_PORT_ID(port_id
), error_message
);
999 void MessageService::DispatchPendingMessages(const PendingMessagesQueue
& queue
,
1001 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1003 MessageChannelMap::iterator channel_iter
= channels_
.find(channel_id
);
1004 if (channel_iter
!= channels_
.end()) {
1005 for (const PendingMessage
& message
: queue
) {
1006 DispatchMessage(message
.first
, channel_iter
->second
, message
.second
);
1011 } // namespace extensions