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