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/extensions_browser_client.h"
41 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
42 #include "extensions/browser/lazy_background_task_queue.h"
43 #include "extensions/browser/pref_names.h"
44 #include "extensions/browser/process_manager.h"
45 #include "extensions/common/extension.h"
46 #include "extensions/common/manifest_constants.h"
47 #include "extensions/common/manifest_handlers/background_info.h"
48 #include "extensions/common/manifest_handlers/externally_connectable.h"
49 #include "extensions/common/manifest_handlers/incognito_info.h"
50 #include "extensions/common/permissions/permissions_data.h"
51 #include "net/base/completion_callback.h"
54 using content::BrowserContext
;
55 using content::BrowserThread
;
56 using content::SiteInstance
;
57 using content::WebContents
;
59 // Since we have 2 ports for every channel, we just index channels by half the
61 #define GET_CHANNEL_ID(port_id) ((port_id) / 2)
62 #define GET_CHANNEL_OPENER_ID(channel_id) ((channel_id) * 2)
63 #define GET_CHANNEL_RECEIVERS_ID(channel_id) ((channel_id) * 2 + 1)
65 // Port1 is always even, port2 is always odd.
66 #define IS_OPENER_PORT_ID(port_id) (((port_id) & 1) == 0)
68 // Change even to odd and vice versa, to get the other side of a given channel.
69 #define GET_OPPOSITE_PORT_ID(source_port_id) ((source_port_id) ^ 1)
71 namespace extensions
{
73 MessageService::PolicyPermission
MessageService::IsNativeMessagingHostAllowed(
74 const PrefService
* pref_service
,
75 const std::string
& native_host_name
) {
76 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
78 PolicyPermission allow_result
= ALLOW_ALL
;
79 if (pref_service
->IsManagedPreference(
80 pref_names::kNativeMessagingUserLevelHosts
)) {
81 if (!pref_service
->GetBoolean(pref_names::kNativeMessagingUserLevelHosts
))
82 allow_result
= ALLOW_SYSTEM_ONLY
;
85 // All native messaging hosts are allowed if there is no blacklist.
86 if (!pref_service
->IsManagedPreference(pref_names::kNativeMessagingBlacklist
))
88 const base::ListValue
* blacklist
=
89 pref_service
->GetList(pref_names::kNativeMessagingBlacklist
);
93 // Check if the name or the wildcard is in the blacklist.
94 base::StringValue
name_value(native_host_name
);
95 base::StringValue
wildcard_value("*");
96 if (blacklist
->Find(name_value
) == blacklist
->end() &&
97 blacklist
->Find(wildcard_value
) == blacklist
->end()) {
101 // The native messaging host is blacklisted. Check the whitelist.
102 if (pref_service
->IsManagedPreference(
103 pref_names::kNativeMessagingWhitelist
)) {
104 const base::ListValue
* whitelist
=
105 pref_service
->GetList(pref_names::kNativeMessagingWhitelist
);
106 if (whitelist
&& whitelist
->Find(name_value
) != whitelist
->end())
113 const char kReceivingEndDoesntExistError
[] =
114 "Could not establish connection. Receiving end does not exist.";
115 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
116 const char kMissingPermissionError
[] =
117 "Access to native messaging requires nativeMessaging permission.";
118 const char kProhibitedByPoliciesError
[] =
119 "Access to the native messaging host was disabled by the system "
123 struct MessageService::MessageChannel
{
124 scoped_ptr
<MessagePort
> opener
;
125 scoped_ptr
<MessagePort
> receiver
;
128 struct MessageService::OpenChannelParams
{
129 int source_process_id
;
130 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
,
150 MessagePort
* receiver
,
151 int receiver_port_id
,
152 const std::string
& source_extension_id
,
153 const std::string
& target_extension_id
,
154 const GURL
& source_url
,
155 const std::string
& channel_name
,
156 bool include_tls_channel_id
,
157 bool include_guest_process_info
)
158 : source_process_id(source_process_id
),
159 source_frame_id(source_frame_id
),
160 target_tab_id(target_tab_id
),
161 target_frame_id(target_frame_id
),
163 receiver_port_id(receiver_port_id
),
164 source_extension_id(source_extension_id
),
165 target_extension_id(target_extension_id
),
166 source_url(source_url
),
167 channel_name(channel_name
),
168 include_tls_channel_id(include_tls_channel_id
),
169 include_guest_process_info(include_guest_process_info
) {
171 this->source_tab
= source_tab
.Pass();
175 DISALLOW_COPY_AND_ASSIGN(OpenChannelParams
);
180 static base::StaticAtomicSequenceNumber g_next_channel_id
;
182 static content::RenderProcessHost
* GetExtensionProcess(
183 BrowserContext
* context
,
184 const std::string
& extension_id
) {
185 scoped_refptr
<SiteInstance
> site_instance
=
186 ProcessManager::Get(context
)->GetSiteInstanceForURL(
187 Extension::GetBaseURLFromExtensionId(extension_id
));
188 return site_instance
->HasProcess() ? site_instance
->GetProcess() : NULL
;
193 content::RenderProcessHost
*
194 MessageService::MessagePort::GetRenderProcessHost() {
199 void MessageService::AllocatePortIdPair(int* port1
, int* port2
) {
200 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
202 unsigned channel_id
=
203 static_cast<unsigned>(g_next_channel_id
.GetNext()) % (kint32max
/2);
204 unsigned port1_id
= channel_id
* 2;
205 unsigned port2_id
= channel_id
* 2 + 1;
207 // Sanity checks to make sure our channel<->port converters are correct.
208 DCHECK(IS_OPENER_PORT_ID(port1_id
));
209 DCHECK_EQ(GET_OPPOSITE_PORT_ID(port1_id
), port2_id
);
210 DCHECK_EQ(GET_OPPOSITE_PORT_ID(port2_id
), port1_id
);
211 DCHECK_EQ(GET_CHANNEL_ID(port1_id
), GET_CHANNEL_ID(port2_id
));
212 DCHECK_EQ(GET_CHANNEL_ID(port1_id
), channel_id
);
213 DCHECK_EQ(GET_CHANNEL_OPENER_ID(channel_id
), port1_id
);
214 DCHECK_EQ(GET_CHANNEL_RECEIVERS_ID(channel_id
), port2_id
);
220 MessageService::MessageService(BrowserContext
* context
)
221 : lazy_background_task_queue_(
222 LazyBackgroundTaskQueue::Get(context
)),
223 weak_factory_(this) {
224 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
226 registrar_
.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
,
227 content::NotificationService::AllBrowserContextsAndSources());
228 registrar_
.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
229 content::NotificationService::AllBrowserContextsAndSources());
232 MessageService::~MessageService() {
233 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
235 STLDeleteContainerPairSecondPointers(channels_
.begin(), channels_
.end());
239 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<MessageService
> >
240 g_factory
= LAZY_INSTANCE_INITIALIZER
;
243 BrowserContextKeyedAPIFactory
<MessageService
>*
244 MessageService::GetFactoryInstance() {
245 return g_factory
.Pointer();
249 MessageService
* MessageService::Get(BrowserContext
* context
) {
250 return BrowserContextKeyedAPIFactory
<MessageService
>::Get(context
);
253 void MessageService::OpenChannelToExtension(
254 int source_process_id
, int source_routing_id
, int receiver_port_id
,
255 const std::string
& source_extension_id
,
256 const std::string
& target_extension_id
,
257 const GURL
& source_url
,
258 const std::string
& channel_name
,
259 bool include_tls_channel_id
) {
260 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
262 content::RenderProcessHost
* source
=
263 content::RenderProcessHost::FromID(source_process_id
);
266 BrowserContext
* context
= source
->GetBrowserContext();
268 ExtensionRegistry
* registry
= ExtensionRegistry::Get(context
);
269 const Extension
* target_extension
=
270 registry
->enabled_extensions().GetByID(target_extension_id
);
271 if (!target_extension
) {
272 DispatchOnDisconnect(
273 source
, receiver_port_id
, kReceivingEndDoesntExistError
);
277 bool is_web_connection
= false;
279 if (source_extension_id
!= target_extension_id
) {
280 // It's an external connection. Check the externally_connectable manifest
281 // key if it's present. If it's not, we allow connection from any extension
283 ExternallyConnectableInfo
* externally_connectable
=
284 static_cast<ExternallyConnectableInfo
*>(
285 target_extension
->GetManifestData(
286 manifest_keys::kExternallyConnectable
));
287 bool is_externally_connectable
= false;
289 if (externally_connectable
) {
290 if (source_extension_id
.empty()) {
291 // No source extension ID so the source was a web page. Check that the
293 is_web_connection
= true;
294 is_externally_connectable
=
295 externally_connectable
->matches
.MatchesURL(source_url
);
296 // Only include the TLS channel ID for externally connected web pages.
297 include_tls_channel_id
&=
298 is_externally_connectable
&&
299 externally_connectable
->accepts_tls_channel_id
;
301 // Source extension ID so the source was an extension. Check that the
302 // extension matches.
303 is_externally_connectable
=
304 externally_connectable
->IdCanConnect(source_extension_id
);
307 // Default behaviour. Any extension, no webpages.
308 is_externally_connectable
= !source_extension_id
.empty();
311 if (!is_externally_connectable
) {
312 // Important: use kReceivingEndDoesntExistError here so that we don't
313 // leak information about this extension to callers. This way it's
314 // indistinguishable from the extension just not existing.
315 DispatchOnDisconnect(
316 source
, receiver_port_id
, kReceivingEndDoesntExistError
);
321 WebContents
* source_contents
= tab_util::GetWebContentsByFrameID(
322 source_process_id
, source_routing_id
);
324 bool include_guest_process_info
= false;
326 // Include info about the opener's tab (if it was a tab).
327 scoped_ptr
<base::DictionaryValue
> source_tab
;
328 int source_frame_id
= -1;
329 if (source_contents
&& ExtensionTabUtil::GetTabId(source_contents
) >= 0) {
330 // Only the tab id is useful to platform apps for internal use. The
331 // unnecessary bits will be stripped out in
332 // MessagingBindings::DispatchOnConnect().
333 source_tab
.reset(ExtensionTabUtil::CreateTabValue(source_contents
));
335 content::RenderFrameHost
* rfh
=
336 content::RenderFrameHost::FromID(source_process_id
, source_routing_id
);
337 // Main frame's frameId is 0.
339 source_frame_id
= !rfh
->GetParent() ? 0 : source_routing_id
;
341 // Check to see if it was a WebView making the request.
342 // Sending messages from WebViews to extensions breaks webview isolation,
343 // so only allow component extensions to receive messages from WebViews.
344 bool is_web_view
= !!WebViewGuest::FromWebContents(source_contents
);
345 if (is_web_view
&& extensions::Manifest::IsComponentLocation(
346 target_extension
->location())) {
347 include_guest_process_info
= true;
348 auto* rfh
= content::RenderFrameHost::FromID(source_process_id
,
350 // Include |source_frame_id| so that we can retrieve the guest's frame
351 // routing id in OpenChannelImpl.
353 source_frame_id
= source_routing_id
;
357 scoped_ptr
<OpenChannelParams
> params(new OpenChannelParams(
358 source_process_id
, source_tab
.Pass(), source_frame_id
, -1,
359 -1, // no target_tab_id/target_frame_id for connections to extensions
360 nullptr, receiver_port_id
, source_extension_id
, target_extension_id
,
361 source_url
, channel_name
, include_tls_channel_id
,
362 include_guest_process_info
));
364 pending_incognito_channels_
[GET_CHANNEL_ID(params
->receiver_port_id
)] =
365 PendingMessagesQueue();
366 if (context
->IsOffTheRecord() &&
367 !util::IsIncognitoEnabled(target_extension_id
, context
)) {
368 // Give the user a chance to accept an incognito connection from the web if
369 // they haven't already, with the conditions:
370 // - Only for spanning-mode incognito. We don't want the complication of
371 // spinning up an additional process here which might need to do some
372 // setup that we're not expecting.
373 // - Only for extensions that can't normally be enabled in incognito, since
374 // that surface (e.g. chrome://extensions) should be the only one for
375 // enabling in incognito. In practice this means platform apps only.
376 if (!is_web_connection
|| IncognitoInfo::IsSplitMode(target_extension
) ||
377 target_extension
->can_be_incognito_enabled()) {
378 OnOpenChannelAllowed(params
.Pass(), false);
382 // If the target extension isn't even listening for connect/message events,
383 // there is no need to go any further and the connection should be
384 // rejected without showing a prompt. See http://crbug.com/442497
385 EventRouter
* event_router
= EventRouter::Get(context
);
386 const char* const events
[] = {"runtime.onConnectExternal",
387 "runtime.onMessageExternal",
388 "extension.onRequestExternal",
390 bool has_event_listener
= false;
391 for (const char* const* event
= events
; *event
; event
++) {
392 has_event_listener
|=
393 event_router
->ExtensionHasEventListener(target_extension_id
, *event
);
395 if (!has_event_listener
) {
396 OnOpenChannelAllowed(params
.Pass(), false);
400 // This check may show a dialog.
401 IncognitoConnectability::Get(context
)
402 ->Query(target_extension
, source_contents
, source_url
,
403 base::Bind(&MessageService::OnOpenChannelAllowed
,
404 weak_factory_
.GetWeakPtr(), base::Passed(¶ms
)));
408 OnOpenChannelAllowed(params
.Pass(), true);
411 void MessageService::OpenChannelToNativeApp(
412 int source_process_id
,
413 int source_routing_id
,
414 int receiver_port_id
,
415 const std::string
& source_extension_id
,
416 const std::string
& native_app_name
) {
417 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
419 content::RenderProcessHost
* source
=
420 content::RenderProcessHost::FromID(source_process_id
);
424 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
425 Profile
* profile
= Profile::FromBrowserContext(source
->GetBrowserContext());
426 ExtensionService
* extension_service
=
427 ExtensionSystem::Get(profile
)->extension_service();
428 bool has_permission
= false;
429 if (extension_service
) {
430 const Extension
* extension
=
431 extension_service
->GetExtensionById(source_extension_id
, false);
432 has_permission
= extension
&&
433 extension
->permissions_data()->HasAPIPermission(
434 APIPermission::kNativeMessaging
);
437 if (!has_permission
) {
438 DispatchOnDisconnect(source
, receiver_port_id
, kMissingPermissionError
);
442 PrefService
* pref_service
= profile
->GetPrefs();
444 // Verify that the host is not blocked by policies.
445 PolicyPermission policy_permission
=
446 IsNativeMessagingHostAllowed(pref_service
, native_app_name
);
447 if (policy_permission
== DISALLOW
) {
448 DispatchOnDisconnect(source
, receiver_port_id
, kProhibitedByPoliciesError
);
452 scoped_ptr
<MessageChannel
> channel(new MessageChannel());
453 channel
->opener
.reset(new ExtensionMessagePort(source
, MSG_ROUTING_CONTROL
,
454 source_extension_id
));
456 // Get handle of the native view and pass it to the native messaging host.
457 content::RenderFrameHost
* render_frame_host
=
458 content::RenderFrameHost::FromID(source_process_id
, source_routing_id
);
459 gfx::NativeView native_view
=
460 render_frame_host
? render_frame_host
->GetNativeView() : nullptr;
462 std::string error
= kReceivingEndDoesntExistError
;
463 scoped_ptr
<NativeMessageHost
> native_host
= NativeMessageHost::Create(
467 policy_permission
== ALLOW_ALL
,
470 // Abandon the channel.
471 if (!native_host
.get()) {
472 LOG(ERROR
) << "Failed to create native process.";
473 DispatchOnDisconnect(source
, receiver_port_id
, error
);
476 channel
->receiver
.reset(new NativeMessagePort(
477 weak_factory_
.GetWeakPtr(), receiver_port_id
, native_host
.Pass()));
479 // Keep the opener alive until the channel is closed.
480 channel
->opener
->IncrementLazyKeepaliveCount();
482 AddChannel(channel
.release(), receiver_port_id
);
483 #else // !(defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX))
484 const char kNativeMessagingNotSupportedError
[] =
485 "Native Messaging is not supported on this platform.";
486 DispatchOnDisconnect(
487 source
, receiver_port_id
, kNativeMessagingNotSupportedError
);
488 #endif // !(defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX))
491 void MessageService::OpenChannelToTab(int source_process_id
,
492 int receiver_port_id
,
495 const std::string
& extension_id
,
496 const std::string
& channel_name
) {
497 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
499 content::RenderProcessHost
* source
=
500 content::RenderProcessHost::FromID(source_process_id
);
503 Profile
* profile
= Profile::FromBrowserContext(source
->GetBrowserContext());
505 WebContents
* contents
= NULL
;
506 scoped_ptr
<MessagePort
> receiver
;
507 if (!ExtensionTabUtil::GetTabById(tab_id
, profile
, true, NULL
, NULL
,
509 contents
->GetController().NeedsReload()) {
510 // The tab isn't loaded yet. Don't attempt to connect.
511 DispatchOnDisconnect(
512 source
, receiver_port_id
, kReceivingEndDoesntExistError
);
516 int receiver_routing_id
;
518 // Positive frame ID is child frame.
519 int receiver_process_id
= contents
->GetRenderProcessHost()->GetID();
520 if (!content::RenderFrameHost::FromID(receiver_process_id
, frame_id
)) {
521 // Frame does not exist.
522 DispatchOnDisconnect(
523 source
, receiver_port_id
, kReceivingEndDoesntExistError
);
526 receiver_routing_id
= frame_id
;
527 } else if (frame_id
== 0) {
528 // Frame ID 0 is main frame.
529 receiver_routing_id
= contents
->GetMainFrame()->GetRoutingID();
531 DCHECK_EQ(-1, frame_id
);
532 // If the frame ID is not set (i.e. -1), then the channel has to be opened
534 // TODO(robwu): Update logic so that frames that are not hosted in the main
535 // frame's process can also receive the port.
536 receiver_routing_id
= MSG_ROUTING_CONTROL
;
538 receiver
.reset(new ExtensionMessagePort(contents
->GetRenderProcessHost(),
539 receiver_routing_id
, extension_id
));
541 const Extension
* extension
= nullptr;
542 if (!extension_id
.empty()) {
543 // Source extension == target extension so the extension must exist, or
544 // where did the IPC come from?
545 extension
= ExtensionRegistry::Get(profile
)->enabled_extensions().GetByID(
550 scoped_ptr
<OpenChannelParams
> params(new OpenChannelParams(
552 scoped_ptr
<base::DictionaryValue
>(), // Source tab doesn't make sense
553 // for opening to tabs.
554 -1, // If there is no tab, then there is no frame either.
555 tab_id
, frame_id
, receiver
.release(), receiver_port_id
, extension_id
,
557 GURL(), // Source URL doesn't make sense for opening to tabs.
559 false, // Connections to tabs don't get TLS channel IDs.
560 false)); // Connections to tabs aren't webview guests.
561 OpenChannelImpl(contents
->GetBrowserContext(), params
.Pass(), extension
,
562 false /* did_enqueue */);
565 void MessageService::OpenChannelImpl(BrowserContext
* browser_context
,
566 scoped_ptr
<OpenChannelParams
> params
,
567 const Extension
* target_extension
,
569 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
570 DCHECK_EQ(target_extension
!= nullptr, !params
->target_extension_id
.empty());
572 content::RenderProcessHost
* source
=
573 content::RenderProcessHost::FromID(params
->source_process_id
);
575 return; // Closed while in flight.
577 if (!params
->receiver
|| !params
->receiver
->GetRenderProcessHost()) {
578 DispatchOnDisconnect(source
, params
->receiver_port_id
,
579 kReceivingEndDoesntExistError
);
583 MessageChannel
* channel(new MessageChannel
);
584 channel
->opener
.reset(new ExtensionMessagePort(source
, MSG_ROUTING_CONTROL
,
585 params
->source_extension_id
));
586 channel
->receiver
.reset(params
->receiver
.release());
587 AddChannel(channel
, params
->receiver_port_id
);
589 int guest_process_id
= content::ChildProcessHost::kInvalidUniqueID
;
590 int guest_render_frame_routing_id
= MSG_ROUTING_NONE
;
591 if (params
->include_guest_process_info
) {
592 guest_process_id
= params
->source_process_id
;
593 guest_render_frame_routing_id
= params
->source_frame_id
;
594 auto* guest_rfh
= content::RenderFrameHost::FromID(
595 guest_process_id
, guest_render_frame_routing_id
);
596 // Reset the |source_frame_id| parameter.
597 params
->source_frame_id
= -1;
599 DCHECK(guest_rfh
== nullptr ||
600 WebViewGuest::FromWebContents(
601 WebContents::FromRenderFrameHost(guest_rfh
)) != nullptr);
604 // Send the connect event to the receiver. Give it the opener's port ID (the
605 // opener has the opposite port ID).
606 channel
->receiver
->DispatchOnConnect(
607 params
->receiver_port_id
, params
->channel_name
, params
->source_tab
.Pass(),
608 params
->source_frame_id
, params
->target_tab_id
, params
->target_frame_id
,
609 guest_process_id
, guest_render_frame_routing_id
,
610 params
->source_extension_id
, params
->target_extension_id
,
611 params
->source_url
, params
->tls_channel_id
);
613 // Report the event to the event router, if the target is an extension.
615 // First, determine what event this will be (runtime.onConnect vs
616 // runtime.onMessage etc), and what the event target is (view vs background
619 // Yes - even though this is opening a channel, they may actually be
620 // runtime.onRequest/onMessage events because those single-use events are
621 // built using the connect framework (see messaging.js).
623 // Likewise, if you're wondering about native messaging events, these are
624 // only initiated *by* the extension, so aren't really events, just the
625 // endpoint of a communication channel.
626 if (target_extension
) {
627 events::HistogramValue histogram_value
= events::UNKNOWN
;
629 params
->source_extension_id
!= params
->target_extension_id
;
630 if (params
->channel_name
== "chrome.runtime.onRequest") {
631 histogram_value
= is_external
? events::RUNTIME_ON_REQUEST_EXTERNAL
632 : events::RUNTIME_ON_REQUEST
;
633 } else if (params
->channel_name
== "chrome.runtime.onMessage") {
634 histogram_value
= is_external
? events::RUNTIME_ON_MESSAGE_EXTERNAL
635 : events::RUNTIME_ON_MESSAGE
;
637 histogram_value
= is_external
? events::RUNTIME_ON_CONNECT_EXTERNAL
638 : events::RUNTIME_ON_CONNECT
;
640 EventRouter::Get(browser_context
)
641 ->ReportEvent(histogram_value
, target_extension
, did_enqueue
);
644 // Keep both ends of the channel alive until the channel is closed.
645 channel
->opener
->IncrementLazyKeepaliveCount();
646 channel
->receiver
->IncrementLazyKeepaliveCount();
649 void MessageService::AddChannel(MessageChannel
* channel
, int receiver_port_id
) {
650 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
652 int channel_id
= GET_CHANNEL_ID(receiver_port_id
);
653 CHECK(channels_
.find(channel_id
) == channels_
.end());
654 channels_
[channel_id
] = channel
;
655 pending_lazy_background_page_channels_
.erase(channel_id
);
658 void MessageService::CloseChannel(int port_id
,
659 const std::string
& error_message
) {
660 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
662 // Note: The channel might be gone already, if the other side closed first.
663 int channel_id
= GET_CHANNEL_ID(port_id
);
664 MessageChannelMap::iterator it
= channels_
.find(channel_id
);
665 if (it
== channels_
.end()) {
666 PendingLazyBackgroundPageChannelMap::iterator pending
=
667 pending_lazy_background_page_channels_
.find(channel_id
);
668 if (pending
!= pending_lazy_background_page_channels_
.end()) {
669 lazy_background_task_queue_
->AddPendingTask(
670 pending
->second
.first
, pending
->second
.second
,
671 base::Bind(&MessageService::PendingLazyBackgroundPageCloseChannel
,
672 weak_factory_
.GetWeakPtr(), port_id
, error_message
));
676 CloseChannelImpl(it
, port_id
, error_message
, true);
679 void MessageService::CloseChannelImpl(
680 MessageChannelMap::iterator channel_iter
,
682 const std::string
& error_message
,
683 bool notify_other_port
) {
684 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
686 MessageChannel
* channel
= channel_iter
->second
;
688 // Notify the other side.
689 if (notify_other_port
) {
690 MessagePort
* port
= IS_OPENER_PORT_ID(closing_port_id
) ?
691 channel
->receiver
.get() : channel
->opener
.get();
692 port
->DispatchOnDisconnect(GET_OPPOSITE_PORT_ID(closing_port_id
),
696 // Balance the IncrementLazyKeepaliveCount() in OpenChannelImpl.
697 channel
->opener
->DecrementLazyKeepaliveCount();
698 channel
->receiver
->DecrementLazyKeepaliveCount();
700 delete channel_iter
->second
;
701 channels_
.erase(channel_iter
);
704 void MessageService::PostMessage(int source_port_id
, const Message
& message
) {
705 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
707 int channel_id
= GET_CHANNEL_ID(source_port_id
);
708 MessageChannelMap::iterator iter
= channels_
.find(channel_id
);
709 if (iter
== channels_
.end()) {
710 // If this channel is pending, queue up the PostMessage to run once
711 // the channel opens.
712 EnqueuePendingMessage(source_port_id
, channel_id
, message
);
716 DispatchMessage(source_port_id
, iter
->second
, message
);
719 void MessageService::Observe(int type
,
720 const content::NotificationSource
& source
,
721 const content::NotificationDetails
& details
) {
722 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
725 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
:
726 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED
: {
727 content::RenderProcessHost
* renderer
=
728 content::Source
<content::RenderProcessHost
>(source
).ptr();
729 OnProcessClosed(renderer
);
738 void MessageService::OnProcessClosed(content::RenderProcessHost
* process
) {
739 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
741 // Close any channels that share this renderer. We notify the opposite
742 // port that its pair has closed.
743 for (MessageChannelMap::iterator it
= channels_
.begin();
744 it
!= channels_
.end(); ) {
745 MessageChannelMap::iterator current
= it
++;
747 content::RenderProcessHost
* opener_process
=
748 current
->second
->opener
->GetRenderProcessHost();
749 content::RenderProcessHost
* receiver_process
=
750 current
->second
->receiver
->GetRenderProcessHost();
752 // Only notify the other side if it has a different porocess host.
753 bool notify_other_port
= opener_process
&& receiver_process
&&
754 opener_process
!= receiver_process
;
756 if (opener_process
== process
) {
757 CloseChannelImpl(current
, GET_CHANNEL_OPENER_ID(current
->first
),
758 std::string(), notify_other_port
);
759 } else if (receiver_process
== process
) {
760 CloseChannelImpl(current
, GET_CHANNEL_RECEIVERS_ID(current
->first
),
761 std::string(), notify_other_port
);
766 void MessageService::EnqueuePendingMessage(int source_port_id
,
768 const Message
& message
) {
769 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
771 PendingChannelMap::iterator pending_for_incognito
=
772 pending_incognito_channels_
.find(channel_id
);
773 if (pending_for_incognito
!= pending_incognito_channels_
.end()) {
774 pending_for_incognito
->second
.push_back(
775 PendingMessage(source_port_id
, message
));
776 // A channel should only be holding pending messages because it is in one
778 DCHECK(!ContainsKey(pending_tls_channel_id_channels_
, channel_id
));
779 DCHECK(!ContainsKey(pending_lazy_background_page_channels_
, channel_id
));
782 PendingChannelMap::iterator pending_for_tls_channel_id
=
783 pending_tls_channel_id_channels_
.find(channel_id
);
784 if (pending_for_tls_channel_id
!= pending_tls_channel_id_channels_
.end()) {
785 pending_for_tls_channel_id
->second
.push_back(
786 PendingMessage(source_port_id
, message
));
787 // A channel should only be holding pending messages because it is in one
789 DCHECK(!ContainsKey(pending_lazy_background_page_channels_
, channel_id
));
792 EnqueuePendingMessageForLazyBackgroundLoad(source_port_id
,
797 void MessageService::EnqueuePendingMessageForLazyBackgroundLoad(
800 const Message
& message
) {
801 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
803 PendingLazyBackgroundPageChannelMap::iterator pending
=
804 pending_lazy_background_page_channels_
.find(channel_id
);
805 if (pending
!= pending_lazy_background_page_channels_
.end()) {
806 lazy_background_task_queue_
->AddPendingTask(
807 pending
->second
.first
, pending
->second
.second
,
808 base::Bind(&MessageService::PendingLazyBackgroundPagePostMessage
,
809 weak_factory_
.GetWeakPtr(), source_port_id
, message
));
813 void MessageService::DispatchMessage(int source_port_id
,
814 MessageChannel
* channel
,
815 const Message
& message
) {
816 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
818 // Figure out which port the ID corresponds to.
819 int dest_port_id
= GET_OPPOSITE_PORT_ID(source_port_id
);
820 MessagePort
* port
= IS_OPENER_PORT_ID(dest_port_id
) ?
821 channel
->opener
.get() : channel
->receiver
.get();
823 port
->DispatchOnMessage(message
, dest_port_id
);
826 bool MessageService::MaybeAddPendingLazyBackgroundPageOpenChannelTask(
827 BrowserContext
* context
,
828 const Extension
* extension
,
829 scoped_ptr
<OpenChannelParams
>* params
,
830 const PendingMessagesQueue
& pending_messages
) {
831 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
833 if (!BackgroundInfo::HasLazyBackgroundPage(extension
))
836 // If the extension uses spanning incognito mode, make sure we're always
837 // using the original profile since that is what the extension process
839 if (!IncognitoInfo::IsSplitMode(extension
))
840 context
= ExtensionsBrowserClient::Get()->GetOriginalContext(context
);
842 if (!lazy_background_task_queue_
->ShouldEnqueueTask(context
, extension
))
845 int channel_id
= GET_CHANNEL_ID((*params
)->receiver_port_id
);
846 pending_lazy_background_page_channels_
[channel_id
] =
847 PendingLazyBackgroundPageChannel(context
, extension
->id());
848 int source_id
= (*params
)->source_process_id
;
849 lazy_background_task_queue_
->AddPendingTask(
850 context
, extension
->id(),
851 base::Bind(&MessageService::PendingLazyBackgroundPageOpenChannel
,
852 weak_factory_
.GetWeakPtr(), base::Passed(params
), source_id
));
854 for (const PendingMessage
& message
: pending_messages
) {
855 EnqueuePendingMessageForLazyBackgroundLoad(message
.first
, channel_id
,
861 void MessageService::OnOpenChannelAllowed(scoped_ptr
<OpenChannelParams
> params
,
863 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
865 int channel_id
= GET_CHANNEL_ID(params
->receiver_port_id
);
867 PendingChannelMap::iterator pending_for_incognito
=
868 pending_incognito_channels_
.find(channel_id
);
869 if (pending_for_incognito
== pending_incognito_channels_
.end()) {
873 PendingMessagesQueue pending_messages
;
874 pending_messages
.swap(pending_for_incognito
->second
);
875 pending_incognito_channels_
.erase(pending_for_incognito
);
877 // Re-lookup the source process since it may no longer be valid.
878 content::RenderProcessHost
* source
=
879 content::RenderProcessHost::FromID(params
->source_process_id
);
885 DispatchOnDisconnect(source
, params
->receiver_port_id
,
886 kReceivingEndDoesntExistError
);
890 BrowserContext
* context
= source
->GetBrowserContext();
892 // Note: we use the source's profile here. If the source is an incognito
893 // process, we will use the incognito EPM to find the right extension process,
894 // which depends on whether the extension uses spanning or split mode.
895 params
->receiver
.reset(new ExtensionMessagePort(
896 GetExtensionProcess(context
, params
->target_extension_id
),
897 MSG_ROUTING_CONTROL
, params
->target_extension_id
));
899 // If the target requests the TLS channel id, begin the lookup for it.
900 // The target might also be a lazy background page, checked next, but the
901 // loading of lazy background pages continues asynchronously, so enqueue
902 // messages awaiting TLS channel ID first.
903 if (params
->include_tls_channel_id
) {
904 // Transfer pending messages to the next pending channel list.
905 pending_tls_channel_id_channels_
[channel_id
].swap(pending_messages
);
906 // Capture this reference before params is invalidated by base::Passed().
907 const GURL
& source_url
= params
->source_url
;
908 property_provider_
.GetChannelID(
909 Profile::FromBrowserContext(context
), source_url
,
910 base::Bind(&MessageService::GotChannelID
, weak_factory_
.GetWeakPtr(),
911 base::Passed(¶ms
)));
915 ExtensionRegistry
* registry
= ExtensionRegistry::Get(context
);
916 const Extension
* target_extension
=
917 registry
->enabled_extensions().GetByID(params
->target_extension_id
);
918 if (!target_extension
) {
919 DispatchOnDisconnect(source
, params
->receiver_port_id
,
920 kReceivingEndDoesntExistError
);
924 // The target might be a lazy background page. In that case, we have to check
925 // if it is loaded and ready, and if not, queue up the task and load the
927 if (!MaybeAddPendingLazyBackgroundPageOpenChannelTask(
928 context
, target_extension
, ¶ms
, pending_messages
)) {
929 OpenChannelImpl(context
, params
.Pass(), target_extension
,
930 false /* did_enqueue */);
931 DispatchPendingMessages(pending_messages
, channel_id
);
935 void MessageService::GotChannelID(scoped_ptr
<OpenChannelParams
> params
,
936 const std::string
& tls_channel_id
) {
937 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
939 params
->tls_channel_id
.assign(tls_channel_id
);
940 int channel_id
= GET_CHANNEL_ID(params
->receiver_port_id
);
942 PendingChannelMap::iterator pending_for_tls_channel_id
=
943 pending_tls_channel_id_channels_
.find(channel_id
);
944 if (pending_for_tls_channel_id
== pending_tls_channel_id_channels_
.end()) {
948 PendingMessagesQueue pending_messages
;
949 pending_messages
.swap(pending_for_tls_channel_id
->second
);
950 pending_tls_channel_id_channels_
.erase(pending_for_tls_channel_id
);
952 // Re-lookup the source process since it may no longer be valid.
953 content::RenderProcessHost
* source
=
954 content::RenderProcessHost::FromID(params
->source_process_id
);
959 BrowserContext
* context
= source
->GetBrowserContext();
960 ExtensionRegistry
* registry
= ExtensionRegistry::Get(context
);
961 const Extension
* target_extension
=
962 registry
->enabled_extensions().GetByID(params
->target_extension_id
);
963 if (!target_extension
) {
964 DispatchOnDisconnect(source
, params
->receiver_port_id
,
965 kReceivingEndDoesntExistError
);
969 if (!MaybeAddPendingLazyBackgroundPageOpenChannelTask(
970 context
, target_extension
, ¶ms
, pending_messages
)) {
971 OpenChannelImpl(context
, params
.Pass(), target_extension
,
972 false /* did_enqueue */);
973 DispatchPendingMessages(pending_messages
, channel_id
);
977 void MessageService::PendingLazyBackgroundPageOpenChannel(
978 scoped_ptr
<OpenChannelParams
> params
,
979 int source_process_id
,
980 ExtensionHost
* host
) {
981 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
984 return; // TODO(mpcomplete): notify source of disconnect?
986 params
->receiver
.reset(new ExtensionMessagePort(host
->render_process_host(),
988 params
->target_extension_id
));
989 OpenChannelImpl(host
->browser_context(), params
.Pass(), host
->extension(),
990 true /* did_enqueue */);
993 void MessageService::DispatchOnDisconnect(content::RenderProcessHost
* source
,
995 const std::string
& error_message
) {
996 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
998 ExtensionMessagePort
port(source
, MSG_ROUTING_CONTROL
, "");
999 port
.DispatchOnDisconnect(GET_OPPOSITE_PORT_ID(port_id
), error_message
);
1002 void MessageService::DispatchPendingMessages(const PendingMessagesQueue
& queue
,
1004 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1006 MessageChannelMap::iterator channel_iter
= channels_
.find(channel_id
);
1007 if (channel_iter
!= channels_
.end()) {
1008 for (const PendingMessage
& message
: queue
) {
1009 DispatchMessage(message
.first
, channel_iter
->second
, message
.second
);
1014 } // namespace extensions