Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / messaging / message_service.cc
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.
4
5 #include "chrome/browser/extensions/api/messaging/message_service.h"
6
7 #include "base/atomic_sequence_num.h"
8 #include "base/bind.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/stl_util.h"
14 #include "base/values.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 "content/public/browser/notification_service.h"
26 #include "content/public/browser/render_process_host.h"
27 #include "content/public/browser/render_view_host.h"
28 #include "content/public/browser/render_widget_host.h"
29 #include "content/public/browser/render_widget_host_view.h"
30 #include "content/public/browser/site_instance.h"
31 #include "content/public/browser/web_contents.h"
32 #include "extensions/browser/extension_host.h"
33 #include "extensions/browser/extension_system.h"
34 #include "extensions/browser/extensions_browser_client.h"
35 #include "extensions/browser/lazy_background_task_queue.h"
36 #include "extensions/browser/process_manager.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/common/extension_messages.h"
39 #include "extensions/common/manifest_constants.h"
40 #include "extensions/common/manifest_handlers/background_info.h"
41 #include "extensions/common/manifest_handlers/externally_connectable.h"
42 #include "extensions/common/manifest_handlers/incognito_info.h"
43 #include "net/base/completion_callback.h"
44 #include "url/gurl.h"
45
46 using content::BrowserContext;
47 using content::SiteInstance;
48 using content::WebContents;
49
50 // Since we have 2 ports for every channel, we just index channels by half the
51 // port ID.
52 #define GET_CHANNEL_ID(port_id) ((port_id) / 2)
53 #define GET_CHANNEL_OPENER_ID(channel_id) ((channel_id) * 2)
54 #define GET_CHANNEL_RECEIVERS_ID(channel_id) ((channel_id) * 2 + 1)
55
56 // Port1 is always even, port2 is always odd.
57 #define IS_OPENER_PORT_ID(port_id) (((port_id) & 1) == 0)
58
59 // Change even to odd and vice versa, to get the other side of a given channel.
60 #define GET_OPPOSITE_PORT_ID(source_port_id) ((source_port_id) ^ 1)
61
62 namespace extensions {
63
64 const char kReceivingEndDoesntExistError[] =
65     "Could not establish connection. Receiving end does not exist.";
66 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
67 const char kMissingPermissionError[] =
68     "Access to native messaging requires nativeMessaging permission.";
69 const char kProhibitedByPoliciesError[] =
70     "Access to the native messaging host was disabled by the system "
71     "administrator.";
72 #endif
73
74 struct MessageService::MessageChannel {
75   scoped_ptr<MessagePort> opener;
76   scoped_ptr<MessagePort> receiver;
77 };
78
79 struct MessageService::OpenChannelParams {
80   content::RenderProcessHost* source;
81   base::DictionaryValue source_tab;
82   scoped_ptr<MessagePort> receiver;
83   int receiver_port_id;
84   std::string source_extension_id;
85   std::string target_extension_id;
86   GURL source_url;
87   std::string channel_name;
88   bool include_tls_channel_id;
89   std::string tls_channel_id;
90
91   // Takes ownership of receiver.
92   OpenChannelParams(content::RenderProcessHost* source,
93                     scoped_ptr<base::DictionaryValue> source_tab,
94                     MessagePort* receiver,
95                     int receiver_port_id,
96                     const std::string& source_extension_id,
97                     const std::string& target_extension_id,
98                     const GURL& source_url,
99                     const std::string& channel_name,
100                     bool include_tls_channel_id)
101       : source(source),
102         receiver(receiver),
103         receiver_port_id(receiver_port_id),
104         source_extension_id(source_extension_id),
105         target_extension_id(target_extension_id),
106         source_url(source_url),
107         channel_name(channel_name),
108         include_tls_channel_id(include_tls_channel_id) {
109     if (source_tab)
110       this->source_tab.Swap(source_tab.get());
111   }
112
113  private:
114   DISALLOW_COPY_AND_ASSIGN(OpenChannelParams);
115 };
116
117 namespace {
118
119 static base::StaticAtomicSequenceNumber g_next_channel_id;
120 static base::StaticAtomicSequenceNumber g_channel_id_overflow_count;
121
122 static content::RenderProcessHost* GetExtensionProcess(
123     BrowserContext* context,
124     const std::string& extension_id) {
125   SiteInstance* site_instance =
126       ExtensionSystem::Get(context)->process_manager()->GetSiteInstanceForURL(
127           Extension::GetBaseURLFromExtensionId(extension_id));
128   return site_instance->HasProcess() ? site_instance->GetProcess() : NULL;
129 }
130
131 }  // namespace
132
133 content::RenderProcessHost*
134     MessageService::MessagePort::GetRenderProcessHost() {
135   return NULL;
136 }
137
138 // static
139 void MessageService::AllocatePortIdPair(int* port1, int* port2) {
140   unsigned channel_id =
141       static_cast<unsigned>(g_next_channel_id.GetNext()) % (kint32max/2);
142
143   if (channel_id == 0) {
144     int overflow_count = g_channel_id_overflow_count.GetNext();
145     if (overflow_count > 0)
146       UMA_HISTOGRAM_BOOLEAN("Extensions.AllocatePortIdPairOverflow", true);
147   }
148
149   unsigned port1_id = channel_id * 2;
150   unsigned port2_id = channel_id * 2 + 1;
151
152   // Sanity checks to make sure our channel<->port converters are correct.
153   DCHECK(IS_OPENER_PORT_ID(port1_id));
154   DCHECK_EQ(GET_OPPOSITE_PORT_ID(port1_id), port2_id);
155   DCHECK_EQ(GET_OPPOSITE_PORT_ID(port2_id), port1_id);
156   DCHECK_EQ(GET_CHANNEL_ID(port1_id), GET_CHANNEL_ID(port2_id));
157   DCHECK_EQ(GET_CHANNEL_ID(port1_id), channel_id);
158   DCHECK_EQ(GET_CHANNEL_OPENER_ID(channel_id), port1_id);
159   DCHECK_EQ(GET_CHANNEL_RECEIVERS_ID(channel_id), port2_id);
160
161   *port1 = port1_id;
162   *port2 = port2_id;
163 }
164
165 MessageService::MessageService(BrowserContext* context)
166     : lazy_background_task_queue_(
167           ExtensionSystem::Get(context)->lazy_background_task_queue()),
168       weak_factory_(this) {
169   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
170                  content::NotificationService::AllBrowserContextsAndSources());
171   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
172                  content::NotificationService::AllBrowserContextsAndSources());
173 }
174
175 MessageService::~MessageService() {
176   STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end());
177   channels_.clear();
178 }
179
180 static base::LazyInstance<BrowserContextKeyedAPIFactory<MessageService> >
181     g_factory = LAZY_INSTANCE_INITIALIZER;
182
183 // static
184 BrowserContextKeyedAPIFactory<MessageService>*
185 MessageService::GetFactoryInstance() {
186   return g_factory.Pointer();
187 }
188
189 // static
190 MessageService* MessageService::Get(BrowserContext* context) {
191   return BrowserContextKeyedAPIFactory<MessageService>::Get(context);
192 }
193
194 void MessageService::OpenChannelToExtension(
195     int source_process_id, int source_routing_id, int receiver_port_id,
196     const std::string& source_extension_id,
197     const std::string& target_extension_id,
198     const GURL& source_url,
199     const std::string& channel_name,
200     bool include_tls_channel_id) {
201   content::RenderProcessHost* source =
202       content::RenderProcessHost::FromID(source_process_id);
203   if (!source)
204     return;
205   BrowserContext* context = source->GetBrowserContext();
206
207   ExtensionSystem* extension_system = ExtensionSystem::Get(context);
208   DCHECK(extension_system);
209   const Extension* target_extension = extension_system->extension_service()->
210       extensions()->GetByID(target_extension_id);
211   if (!target_extension) {
212     DispatchOnDisconnect(
213         source, receiver_port_id, kReceivingEndDoesntExistError);
214     return;
215   }
216
217   // Only running ephemeral apps can receive messages. Idle cached ephemeral
218   // apps are invisible and should not be connectable.
219   if (target_extension->is_ephemeral() &&
220       util::IsExtensionIdle(target_extension_id, context)) {
221     DispatchOnDisconnect(
222         source, receiver_port_id, kReceivingEndDoesntExistError);
223     return;
224   }
225
226   bool is_web_connection = false;
227
228   if (source_extension_id != target_extension_id) {
229     // It's an external connection. Check the externally_connectable manifest
230     // key if it's present. If it's not, we allow connection from any extension
231     // but not webpages.
232     ExternallyConnectableInfo* externally_connectable =
233         static_cast<ExternallyConnectableInfo*>(
234             target_extension->GetManifestData(
235                 manifest_keys::kExternallyConnectable));
236     bool is_externally_connectable = false;
237
238     if (externally_connectable) {
239       if (source_extension_id.empty()) {
240         // No source extension ID so the source was a web page. Check that the
241         // URL matches.
242         is_web_connection = true;
243         is_externally_connectable =
244             externally_connectable->matches.MatchesURL(source_url);
245         // Only include the TLS channel ID for externally connected web pages.
246         include_tls_channel_id &=
247             is_externally_connectable &&
248             externally_connectable->accepts_tls_channel_id;
249       } else {
250         // Source extension ID so the source was an extension. Check that the
251         // extension matches.
252         is_externally_connectable =
253             externally_connectable->IdCanConnect(source_extension_id);
254       }
255     } else {
256       // Default behaviour. Any extension, no webpages.
257       is_externally_connectable = !source_extension_id.empty();
258     }
259
260     if (!is_externally_connectable) {
261       // Important: use kReceivingEndDoesntExistError here so that we don't
262       // leak information about this extension to callers. This way it's
263       // indistinguishable from the extension just not existing.
264       DispatchOnDisconnect(
265           source, receiver_port_id, kReceivingEndDoesntExistError);
266       return;
267     }
268   }
269
270   WebContents* source_contents = tab_util::GetWebContentsByID(
271       source_process_id, source_routing_id);
272
273   if (context->IsOffTheRecord() &&
274       !util::IsIncognitoEnabled(target_extension_id, context)) {
275     // Give the user a chance to accept an incognito connection from the web if
276     // they haven't already, with the conditions:
277     // - Only for spanning-mode incognito. We don't want the complication of
278     //   spinning up an additional process here which might need to do some
279     //   setup that we're not expecting.
280     // - Only for extensions that can't normally be enabled in incognito, since
281     //   that surface (e.g. chrome://extensions) should be the only one for
282     //   enabling in incognito. In practice this means platform apps only.
283     if (!is_web_connection || IncognitoInfo::IsSplitMode(target_extension) ||
284         target_extension->can_be_incognito_enabled() ||
285         // This check may show a dialog.
286         !IncognitoConnectability::Get(context)
287              ->Query(target_extension, source_contents, source_url)) {
288       DispatchOnDisconnect(
289           source, receiver_port_id, kReceivingEndDoesntExistError);
290       return;
291     }
292   }
293
294   // Note: we use the source's profile here. If the source is an incognito
295   // process, we will use the incognito EPM to find the right extension process,
296   // which depends on whether the extension uses spanning or split mode.
297   MessagePort* receiver = new ExtensionMessagePort(
298       GetExtensionProcess(context, target_extension_id),
299       MSG_ROUTING_CONTROL,
300       target_extension_id);
301
302   // Include info about the opener's tab (if it was a tab).
303   scoped_ptr<base::DictionaryValue> source_tab;
304   GURL source_url_for_tab;
305
306   if (source_contents && ExtensionTabUtil::GetTabId(source_contents) >= 0) {
307     // Only the tab id is useful to platform apps for internal use. The
308     // unnecessary bits will be stripped out in
309     // MessagingBindings::DispatchOnConnect().
310     source_tab.reset(ExtensionTabUtil::CreateTabValue(source_contents));
311     source_url_for_tab = source_url;
312   }
313
314   OpenChannelParams* params = new OpenChannelParams(source,
315                                                     source_tab.Pass(),
316                                                     receiver,
317                                                     receiver_port_id,
318                                                     source_extension_id,
319                                                     target_extension_id,
320                                                     source_url_for_tab,
321                                                     channel_name,
322                                                     include_tls_channel_id);
323
324   // If the target requests the TLS channel id, begin the lookup for it.
325   // The target might also be a lazy background page, checked next, but the
326   // loading of lazy background pages continues asynchronously, so enqueue
327   // messages awaiting TLS channel ID first.
328   if (include_tls_channel_id) {
329     pending_tls_channel_id_channels_[GET_CHANNEL_ID(params->receiver_port_id)]
330         = PendingMessagesQueue();
331     property_provider_.GetDomainBoundCert(
332         Profile::FromBrowserContext(context),
333         source_url,
334         base::Bind(&MessageService::GotDomainBoundCert,
335                    weak_factory_.GetWeakPtr(),
336                    base::Passed(make_scoped_ptr(params))));
337     return;
338   }
339
340   // The target might be a lazy background page. In that case, we have to check
341   // if it is loaded and ready, and if not, queue up the task and load the
342   // page.
343   if (MaybeAddPendingLazyBackgroundPageOpenChannelTask(
344           context, target_extension, params)) {
345     return;
346   }
347
348   OpenChannelImpl(make_scoped_ptr(params));
349 }
350
351 void MessageService::OpenChannelToNativeApp(
352     int source_process_id,
353     int source_routing_id,
354     int receiver_port_id,
355     const std::string& source_extension_id,
356     const std::string& native_app_name) {
357   content::RenderProcessHost* source =
358       content::RenderProcessHost::FromID(source_process_id);
359   if (!source)
360     return;
361
362 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
363   Profile* profile = Profile::FromBrowserContext(source->GetBrowserContext());
364   ExtensionService* extension_service =
365       ExtensionSystem::Get(profile)->extension_service();
366   bool has_permission = false;
367   if (extension_service) {
368     const Extension* extension =
369         extension_service->GetExtensionById(source_extension_id, false);
370     has_permission = extension && extension->HasAPIPermission(
371         APIPermission::kNativeMessaging);
372   }
373
374   if (!has_permission) {
375     DispatchOnDisconnect(source, receiver_port_id, kMissingPermissionError);
376     return;
377   }
378
379   PrefService* pref_service = profile->GetPrefs();
380
381   // Verify that the host is not blocked by policies.
382   NativeMessageProcessHost::PolicyPermission policy_permission =
383       NativeMessageProcessHost::IsHostAllowed(pref_service, native_app_name);
384   if (policy_permission == NativeMessageProcessHost::DISALLOW) {
385     DispatchOnDisconnect(source, receiver_port_id, kProhibitedByPoliciesError);
386     return;
387   }
388
389   scoped_ptr<MessageChannel> channel(new MessageChannel());
390   channel->opener.reset(new ExtensionMessagePort(source, MSG_ROUTING_CONTROL,
391                                                  source_extension_id));
392
393   // Get handle of the native view and pass it to the native messaging host.
394   gfx::NativeView native_view =
395       content::RenderWidgetHost::FromID(source_process_id, source_routing_id)->
396           GetView()->GetNativeView();
397
398   scoped_ptr<NativeMessageProcessHost> native_process =
399       NativeMessageProcessHost::Create(
400           native_view,
401           base::WeakPtr<NativeMessageProcessHost::Client>(
402               weak_factory_.GetWeakPtr()),
403           source_extension_id, native_app_name, receiver_port_id,
404           policy_permission == NativeMessageProcessHost::ALLOW_ALL);
405
406   // Abandon the channel.
407   if (!native_process.get()) {
408     LOG(ERROR) << "Failed to create native process.";
409     DispatchOnDisconnect(
410         source, receiver_port_id, kReceivingEndDoesntExistError);
411     return;
412   }
413   channel->receiver.reset(new NativeMessagePort(native_process.release()));
414
415   // Keep the opener alive until the channel is closed.
416   channel->opener->IncrementLazyKeepaliveCount();
417
418   AddChannel(channel.release(), receiver_port_id);
419 #else  // !(defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX))
420   const char kNativeMessagingNotSupportedError[] =
421       "Native Messaging is not supported on this platform.";
422   DispatchOnDisconnect(
423       source, receiver_port_id, kNativeMessagingNotSupportedError);
424 #endif  // !(defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX))
425 }
426
427 void MessageService::OpenChannelToTab(
428     int source_process_id, int source_routing_id, int receiver_port_id,
429     int tab_id, const std::string& extension_id,
430     const std::string& channel_name) {
431   content::RenderProcessHost* source =
432       content::RenderProcessHost::FromID(source_process_id);
433   if (!source)
434     return;
435   Profile* profile = Profile::FromBrowserContext(source->GetBrowserContext());
436
437   WebContents* contents = NULL;
438   scoped_ptr<MessagePort> receiver;
439   if (ExtensionTabUtil::GetTabById(tab_id, profile, true,
440                                    NULL, NULL, &contents, NULL)) {
441     receiver.reset(new ExtensionMessagePort(
442         contents->GetRenderProcessHost(),
443         contents->GetRenderViewHost()->GetRoutingID(),
444         extension_id));
445   }
446
447   if (contents && contents->GetController().NeedsReload()) {
448     // The tab isn't loaded yet. Don't attempt to connect.
449     DispatchOnDisconnect(
450         source, receiver_port_id, kReceivingEndDoesntExistError);
451     return;
452   }
453
454   scoped_ptr<OpenChannelParams> params(new OpenChannelParams(
455         source,
456         scoped_ptr<base::DictionaryValue>(),  // Source tab doesn't make sense
457                                               // for opening to tabs.
458         receiver.release(),
459         receiver_port_id,
460         extension_id,
461         extension_id,
462         GURL(),  // Source URL doesn't make sense for opening to tabs.
463         channel_name,
464         false));  // Connections to tabs don't get TLS channel IDs.
465   OpenChannelImpl(params.Pass());
466 }
467
468 bool MessageService::OpenChannelImpl(scoped_ptr<OpenChannelParams> params) {
469   if (!params->source)
470     return false;  // Closed while in flight.
471
472   if (!params->receiver || !params->receiver->GetRenderProcessHost()) {
473     DispatchOnDisconnect(params->source,
474                          params->receiver_port_id,
475                          kReceivingEndDoesntExistError);
476     return false;
477   }
478
479   // Add extra paranoid CHECKs, since we have crash reports of this being NULL.
480   // http://code.google.com/p/chromium/issues/detail?id=19067
481   CHECK(params->receiver->GetRenderProcessHost());
482
483   MessageChannel* channel(new MessageChannel);
484   channel->opener.reset(new ExtensionMessagePort(params->source,
485                                                  MSG_ROUTING_CONTROL,
486                                                  params->source_extension_id));
487   channel->receiver.reset(params->receiver.release());
488
489   CHECK(channel->receiver->GetRenderProcessHost());
490
491   AddChannel(channel, params->receiver_port_id);
492
493   CHECK(channel->receiver->GetRenderProcessHost());
494
495   // Send the connect event to the receiver.  Give it the opener's port ID (the
496   // opener has the opposite port ID).
497   channel->receiver->DispatchOnConnect(params->receiver_port_id,
498                                        params->channel_name,
499                                        params->source_tab,
500                                        params->source_extension_id,
501                                        params->target_extension_id,
502                                        params->source_url,
503                                        params->tls_channel_id);
504
505   // Keep both ends of the channel alive until the channel is closed.
506   channel->opener->IncrementLazyKeepaliveCount();
507   channel->receiver->IncrementLazyKeepaliveCount();
508   return true;
509 }
510
511 void MessageService::AddChannel(MessageChannel* channel, int receiver_port_id) {
512   int channel_id = GET_CHANNEL_ID(receiver_port_id);
513   CHECK(channels_.find(channel_id) == channels_.end());
514   channels_[channel_id] = channel;
515   pending_lazy_background_page_channels_.erase(channel_id);
516 }
517
518 void MessageService::CloseChannel(int port_id,
519                                   const std::string& error_message) {
520   // Note: The channel might be gone already, if the other side closed first.
521   int channel_id = GET_CHANNEL_ID(port_id);
522   MessageChannelMap::iterator it = channels_.find(channel_id);
523   if (it == channels_.end()) {
524     PendingLazyBackgroundPageChannelMap::iterator pending =
525         pending_lazy_background_page_channels_.find(channel_id);
526     if (pending != pending_lazy_background_page_channels_.end()) {
527       lazy_background_task_queue_->AddPendingTask(
528           pending->second.first, pending->second.second,
529           base::Bind(&MessageService::PendingLazyBackgroundPageCloseChannel,
530                      weak_factory_.GetWeakPtr(), port_id, error_message));
531     }
532     return;
533   }
534   CloseChannelImpl(it, port_id, error_message, true);
535 }
536
537 void MessageService::CloseChannelImpl(
538     MessageChannelMap::iterator channel_iter,
539     int closing_port_id,
540     const std::string& error_message,
541     bool notify_other_port) {
542   MessageChannel* channel = channel_iter->second;
543
544   // Notify the other side.
545   if (notify_other_port) {
546     MessagePort* port = IS_OPENER_PORT_ID(closing_port_id) ?
547         channel->receiver.get() : channel->opener.get();
548     port->DispatchOnDisconnect(GET_OPPOSITE_PORT_ID(closing_port_id),
549                                error_message);
550   }
551
552   // Balance the IncrementLazyKeepaliveCount() in OpenChannelImpl.
553   channel->opener->DecrementLazyKeepaliveCount();
554   channel->receiver->DecrementLazyKeepaliveCount();
555
556   delete channel_iter->second;
557   channels_.erase(channel_iter);
558 }
559
560 void MessageService::PostMessage(int source_port_id, const Message& message) {
561   int channel_id = GET_CHANNEL_ID(source_port_id);
562   MessageChannelMap::iterator iter = channels_.find(channel_id);
563   if (iter == channels_.end()) {
564     // If this channel is pending, queue up the PostMessage to run once
565     // the channel opens.
566     EnqueuePendingMessage(source_port_id, channel_id, message);
567     return;
568   }
569
570   DispatchMessage(source_port_id, iter->second, message);
571 }
572
573 void MessageService::PostMessageFromNativeProcess(int port_id,
574                                                   const std::string& message) {
575   PostMessage(port_id, Message(message, false /* user_gesture */));
576 }
577
578 void MessageService::Observe(int type,
579                              const content::NotificationSource& source,
580                              const content::NotificationDetails& details) {
581   switch (type) {
582     case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
583     case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
584       content::RenderProcessHost* renderer =
585           content::Source<content::RenderProcessHost>(source).ptr();
586       OnProcessClosed(renderer);
587       break;
588     }
589     default:
590       NOTREACHED();
591       return;
592   }
593 }
594
595 void MessageService::OnProcessClosed(content::RenderProcessHost* process) {
596   // Close any channels that share this renderer.  We notify the opposite
597   // port that his pair has closed.
598   for (MessageChannelMap::iterator it = channels_.begin();
599        it != channels_.end(); ) {
600     MessageChannelMap::iterator current = it++;
601
602     content::RenderProcessHost* opener_process =
603         current->second->opener->GetRenderProcessHost();
604     content::RenderProcessHost* receiver_process =
605         current->second->receiver->GetRenderProcessHost();
606
607     // Only notify the other side if it has a different porocess host.
608     bool notify_other_port = opener_process && receiver_process &&
609         opener_process != receiver_process;
610
611     if (opener_process == process) {
612       CloseChannelImpl(current, GET_CHANNEL_OPENER_ID(current->first),
613                        std::string(), notify_other_port);
614     } else if (receiver_process == process) {
615       CloseChannelImpl(current, GET_CHANNEL_RECEIVERS_ID(current->first),
616                        std::string(), notify_other_port);
617     }
618   }
619 }
620
621 void MessageService::EnqueuePendingMessage(int source_port_id,
622                                            int channel_id,
623                                            const Message& message) {
624   PendingTlsChannelIdMap::iterator pending_for_tls_channel_id =
625       pending_tls_channel_id_channels_.find(channel_id);
626   if (pending_for_tls_channel_id != pending_tls_channel_id_channels_.end()) {
627     pending_for_tls_channel_id->second.push_back(
628         PendingMessage(source_port_id, message));
629     // Pending messages must only be pending the TLS channel ID or lazy
630     // background page loading, never both.
631     return;
632   }
633   EnqueuePendingMessageForLazyBackgroundLoad(source_port_id,
634                                              channel_id,
635                                              message);
636 }
637
638 void MessageService::EnqueuePendingMessageForLazyBackgroundLoad(
639     int source_port_id,
640     int channel_id,
641     const Message& message) {
642   PendingLazyBackgroundPageChannelMap::iterator pending =
643       pending_lazy_background_page_channels_.find(channel_id);
644   if (pending != pending_lazy_background_page_channels_.end()) {
645     lazy_background_task_queue_->AddPendingTask(
646         pending->second.first, pending->second.second,
647         base::Bind(&MessageService::PendingLazyBackgroundPagePostMessage,
648                    weak_factory_.GetWeakPtr(), source_port_id, message));
649   }
650 }
651
652 void MessageService::DispatchMessage(int source_port_id,
653                                      MessageChannel* channel,
654                                      const Message& message) {
655   // Figure out which port the ID corresponds to.
656   int dest_port_id = GET_OPPOSITE_PORT_ID(source_port_id);
657   MessagePort* port = IS_OPENER_PORT_ID(dest_port_id) ?
658       channel->opener.get() : channel->receiver.get();
659
660   port->DispatchOnMessage(message, dest_port_id);
661 }
662
663 bool MessageService::MaybeAddPendingLazyBackgroundPageOpenChannelTask(
664     BrowserContext* context,
665     const Extension* extension,
666     OpenChannelParams* params) {
667   if (!BackgroundInfo::HasLazyBackgroundPage(extension))
668     return false;
669
670   // If the extension uses spanning incognito mode, make sure we're always
671   // using the original profile since that is what the extension process
672   // will use.
673   if (!IncognitoInfo::IsSplitMode(extension))
674     context = ExtensionsBrowserClient::Get()->GetOriginalContext(context);
675
676   if (!lazy_background_task_queue_->ShouldEnqueueTask(context, extension))
677     return false;
678
679   pending_lazy_background_page_channels_
680       [GET_CHANNEL_ID(params->receiver_port_id)] =
681           PendingLazyBackgroundPageChannel(context, extension->id());
682   scoped_ptr<OpenChannelParams> scoped_params(params);
683   lazy_background_task_queue_->AddPendingTask(
684       context,
685       extension->id(),
686       base::Bind(&MessageService::PendingLazyBackgroundPageOpenChannel,
687                  weak_factory_.GetWeakPtr(),
688                  base::Passed(&scoped_params),
689                  params->source->GetID()));
690   return true;
691 }
692
693 void MessageService::GotDomainBoundCert(scoped_ptr<OpenChannelParams> params,
694                                         const std::string& tls_channel_id) {
695   params->tls_channel_id.assign(tls_channel_id);
696   int channel_id = GET_CHANNEL_ID(params->receiver_port_id);
697
698   PendingTlsChannelIdMap::iterator pending_for_tls_channel_id =
699       pending_tls_channel_id_channels_.find(channel_id);
700   if (pending_for_tls_channel_id == pending_tls_channel_id_channels_.end()) {
701     NOTREACHED();
702     return;
703   }
704
705   BrowserContext* context = params->source->GetBrowserContext();
706
707   const Extension* target_extension =
708       ExtensionSystem::Get(context)->extension_service()->extensions()->GetByID(
709           params->target_extension_id);
710   if (!target_extension) {
711     pending_tls_channel_id_channels_.erase(channel_id);
712     DispatchOnDisconnect(
713         params->source, params->receiver_port_id,
714         kReceivingEndDoesntExistError);
715     return;
716   }
717   PendingMessagesQueue& pending_messages = pending_for_tls_channel_id->second;
718   if (MaybeAddPendingLazyBackgroundPageOpenChannelTask(
719           context, target_extension, params.get())) {
720     // Lazy background queue took ownership. Release ours.
721     ignore_result(params.release());
722     // Messages queued up waiting for the TLS channel ID now need to be queued
723     // up for the lazy background page to load.
724     for (PendingMessagesQueue::iterator it = pending_messages.begin();
725          it != pending_messages.end();
726          it++) {
727       EnqueuePendingMessageForLazyBackgroundLoad(it->first, channel_id,
728                                                  it->second);
729     }
730   } else {
731     OpenChannelImpl(params.Pass());
732     // Messages queued up waiting for the TLS channel ID can be posted now.
733     MessageChannelMap::iterator channel_iter = channels_.find(channel_id);
734     if (channel_iter != channels_.end()) {
735       for (PendingMessagesQueue::iterator it = pending_messages.begin();
736            it != pending_messages.end();
737            it++) {
738         DispatchMessage(it->first, channel_iter->second, it->second);
739       }
740     }
741   }
742   pending_tls_channel_id_channels_.erase(channel_id);
743 }
744
745 void MessageService::PendingLazyBackgroundPageOpenChannel(
746     scoped_ptr<OpenChannelParams> params,
747     int source_process_id,
748     ExtensionHost* host) {
749   if (!host)
750     return;  // TODO(mpcomplete): notify source of disconnect?
751
752   // Re-lookup the source process since it may no longer be valid.
753   content::RenderProcessHost* source =
754       content::RenderProcessHost::FromID(source_process_id);
755   if (!source)
756     return;
757
758   params->source = source;
759   params->receiver.reset(new ExtensionMessagePort(host->render_process_host(),
760                                                   MSG_ROUTING_CONTROL,
761                                                   params->target_extension_id));
762   OpenChannelImpl(params.Pass());
763 }
764
765 void MessageService::DispatchOnDisconnect(content::RenderProcessHost* source,
766                                           int port_id,
767                                           const std::string& error_message) {
768   ExtensionMessagePort port(source, MSG_ROUTING_CONTROL, "");
769   port.DispatchOnDisconnect(GET_OPPOSITE_PORT_ID(port_id), error_message);
770 }
771
772 }  // namespace extensions