text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chrome/browser/extensions/api/messaging/message_service.h"
  
  #include "base/atomic_sequence_num.h"
  #include "base/bind.h"
  #include "base/callback.h"
  #include "base/json/json_writer.h"
  #include "base/lazy_instance.h"
  #include "base/metrics/histogram.h"
  #include "base/stl_util.h"
  #include "base/values.h"
  #include "chrome/browser/chrome_notification_types.h"
  #include "chrome/browser/extensions/api/messaging/extension_message_port.h"
  #include "chrome/browser/extensions/api/messaging/incognito_connectability.h"
  #include "chrome/browser/extensions/api/messaging/native_message_port.h"
  #include "chrome/browser/extensions/extension_host.h"
  #include "chrome/browser/extensions/extension_process_manager.h"
  #include "chrome/browser/extensions/extension_service.h"
  #include "chrome/browser/extensions/extension_system.h"
  #include "chrome/browser/extensions/extension_tab_util.h"
  #include "chrome/browser/extensions/extension_util.h"
  #include "chrome/browser/extensions/process_map.h"
  #include "chrome/browser/profiles/profile.h"
  #include "chrome/browser/tab_contents/tab_util.h"
  #include "chrome/common/extensions/background_info.h"
  #include "chrome/common/extensions/extension.h"
  #include "chrome/common/extensions/extension_messages.h"
  #include "chrome/common/extensions/incognito_handler.h"
  #include "chrome/common/extensions/manifest_handlers/externally_connectable.h"
  #include "content/public/browser/notification_service.h"
  #include "content/public/browser/render_process_host.h"
  #include "content/public/browser/render_view_host.h"
  #include "content/public/browser/render_widget_host.h"
  #include "content/public/browser/render_widget_host_view.h"
  #include "content/public/browser/site_instance.h"
  #include "content/public/browser/web_contents.h"
  #include "extensions/browser/lazy_background_task_queue.h"
  #include "extensions/common/manifest_constants.h"
  #include "net/base/completion_callback.h"
  #include "url/gurl.h"
  
  using content::SiteInstance;
  using content::WebContents;
  
  // Since we have 2 ports for every channel, we just index channels by half the
  // port ID.
  #define GET_CHANNEL_ID(port_id) ((port_id) / 2)
  #define GET_CHANNEL_OPENER_ID(channel_id) ((channel_id) * 2)
  #define GET_CHANNEL_RECEIVERS_ID(channel_id) ((channel_id) * 2 + 1)
  
  // Port1 is always even, port2 is always odd.
  #define IS_OPENER_PORT_ID(port_id) (((port_id) & 1) == 0)
  
  // Change even to odd and vice versa, to get the other side of a given channel.
  #define GET_OPPOSITE_PORT_ID(source_port_id) ((source_port_id) ^ 1)
  
  namespace extensions {
  
  const char kReceivingEndDoesntExistError[] =
      "Could not establish connection. Receiving end does not exist.";
  #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
  const char kMissingPermissionError[] =
      "Access to native messaging requires nativeMessaging permission.";
  #endif
  
  struct MessageService::MessageChannel {
    scoped_ptr<MessagePort> opener;
    scoped_ptr<MessagePort> receiver;
  };
  
  struct MessageService::OpenChannelParams {
    content::RenderProcessHost* source;
    base::DictionaryValue source_tab;
    scoped_ptr<MessagePort> receiver;
    int receiver_port_id;
    std::string source_extension_id;
    std::string target_extension_id;
    GURL source_url;
    std::string channel_name;
    bool include_tls_channel_id;
    std::string tls_channel_id;
  
    // Takes ownership of receiver.
    OpenChannelParams(content::RenderProcessHost* source,
                      scoped_ptr<base::DictionaryValue> source_tab,
                      MessagePort* receiver,
                      int receiver_port_id,
                      const std::string& source_extension_id,
                      const std::string& target_extension_id,
                      const GURL& source_url,
                      const std::string& channel_name,
                      bool include_tls_channel_id)
        : source(source),
          receiver(receiver),
          receiver_port_id(receiver_port_id),
          source_extension_id(source_extension_id),
          target_extension_id(target_extension_id),
          source_url(source_url),
          channel_name(channel_name),
          include_tls_channel_id(include_tls_channel_id) {
      if (source_tab)
        this->source_tab.Swap(source_tab.get());
    }
  
   private:
    DISALLOW_COPY_AND_ASSIGN(OpenChannelParams);
  };
  
  namespace {
  
  static base::StaticAtomicSequenceNumber g_next_channel_id;
  static base::StaticAtomicSequenceNumber g_channel_id_overflow_count;
  
  static content::RenderProcessHost* GetExtensionProcess(
      Profile* profile, const std::string& extension_id) {
    SiteInstance* site_instance =
        ExtensionSystem::Get(profile)->process_manager()->
            GetSiteInstanceForURL(
                Extension::GetBaseURLFromExtensionId(extension_id));
  
    if (!site_instance->HasProcess())
      return NULL;
  
    return site_instance->GetProcess();
  }
  
  }  // namespace
  
  content::RenderProcessHost*
      MessageService::MessagePort::GetRenderProcessHost() {
    return NULL;
  }
  
  // static
  void MessageService::AllocatePortIdPair(int* port1, int* port2) {
    unsigned channel_id =
        static_cast<unsigned>(g_next_channel_id.GetNext()) % (kint32max/2);
  
    if (channel_id == 0) {
      int overflow_count = g_channel_id_overflow_count.GetNext();
      if (overflow_count > 0)
        UMA_HISTOGRAM_BOOLEAN("Extensions.AllocatePortIdPairOverflow", true);
    }
  
    unsigned port1_id = channel_id * 2;
    unsigned port2_id = channel_id * 2 + 1;
  
    // Sanity checks to make sure our channel<->port converters are correct.
    DCHECK(IS_OPENER_PORT_ID(port1_id));
    DCHECK(GET_OPPOSITE_PORT_ID(port1_id) == port2_id);
    DCHECK(GET_OPPOSITE_PORT_ID(port2_id) == port1_id);
    DCHECK(GET_CHANNEL_ID(port1_id) == GET_CHANNEL_ID(port2_id));
    DCHECK(GET_CHANNEL_ID(port1_id) == channel_id);
    DCHECK(GET_CHANNEL_OPENER_ID(channel_id) == port1_id);
    DCHECK(GET_CHANNEL_RECEIVERS_ID(channel_id) == port2_id);
  
    *port1 = port1_id;
    *port2 = port2_id;
  }
  
  MessageService::MessageService(Profile* profile)
      : lazy_background_task_queue_(
            ExtensionSystem::Get(profile)->lazy_background_task_queue()),
        weak_factory_(this) {
    registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
                   content::NotificationService::AllBrowserContextsAndSources());
    registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
                   content::NotificationService::AllBrowserContextsAndSources());
  }
  
  MessageService::~MessageService() {
    STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end());
    channels_.clear();
  }
  
  static base::LazyInstance<ProfileKeyedAPIFactory<MessageService> >
  g_factory = LAZY_INSTANCE_INITIALIZER;
  
  // static
  ProfileKeyedAPIFactory<MessageService>* MessageService::GetFactoryInstance() {
    return &g_factory.Get();
  }
  
  // static
  MessageService* MessageService::Get(Profile* profile) {
    return ProfileKeyedAPIFactory<MessageService>::GetForProfile(profile);
  }
  
  void MessageService::OpenChannelToExtension(
      int source_process_id, int source_routing_id, int receiver_port_id,
      const std::string& source_extension_id,
      const std::string& target_extension_id,
      const GURL& source_url,
      const std::string& channel_name,
      bool include_tls_channel_id) {
    content::RenderProcessHost* source =
        content::RenderProcessHost::FromID(source_process_id);
    if (!source)
      return;
    Profile* profile = Profile::FromBrowserContext(source->GetBrowserContext());
  
    const Extension* target_extension = ExtensionSystem::Get(profile)->
        extension_service()->extensions()->GetByID(target_extension_id);
    if (!target_extension) {
      DispatchOnDisconnect(
          source, receiver_port_id, kReceivingEndDoesntExistError);
      return;
    }
  
    bool is_web_connection = false;
  
    if (source_extension_id != target_extension_id) {
      // It's an external connection. Check the externally_connectable manifest
      // key if it's present. If it's not, we allow connection from any extension
      // but not webpages.
      ExternallyConnectableInfo* externally_connectable =
          static_cast<ExternallyConnectableInfo*>(
              target_extension->GetManifestData(
                  manifest_keys::kExternallyConnectable));
      bool is_externally_connectable = false;
  
      if (externally_connectable) {
        if (source_extension_id.empty()) {
          // No source extension ID so the source was a web page. Check that the
          // URL matches.
          is_web_connection = true;
          is_externally_connectable =
              externally_connectable->matches.MatchesURL(source_url);
          // Only include the TLS channel ID for externally connected web pages.
          include_tls_channel_id &=
              is_externally_connectable &&
              externally_connectable->accepts_tls_channel_id;
        } else {
          // Source extension ID so the source was an extension. Check that the
          // extension matches.
          is_externally_connectable =
              externally_connectable->IdCanConnect(source_extension_id);
        }
      } else {
        // Default behaviour. Any extension, no webpages.
        is_externally_connectable = !source_extension_id.empty();
      }
  
      if (!is_externally_connectable) {
        // Important: use kReceivingEndDoesntExistError here so that we don't
        // leak information about this extension to callers. This way it's
        // indistinguishable from the extension just not existing.
        DispatchOnDisconnect(
            source, receiver_port_id, kReceivingEndDoesntExistError);
        return;
      }
    }
  
    ExtensionService* extension_service =
        ExtensionSystem::Get(profile)->extension_service();
    WebContents* source_contents = tab_util::GetWebContentsByID(
        source_process_id, source_routing_id);
  
    if (profile->IsOffTheRecord() &&
        !extension_util::IsIncognitoEnabled(target_extension_id,
                                            extension_service)) {
      // Give the user a chance to accept an incognito connection if they haven't
      // already - but only for spanning-mode incognito. We don't want the
      // complication of spinning up an additional process here which might need
      // to do some setup that we're not expecting.
      if (!is_web_connection ||
          IncognitoInfo::IsSplitMode(target_extension) ||
          !IncognitoConnectability::Get(profile)->Query(target_extension,
                                                        source_contents,
                                                        source_url)) {
        DispatchOnDisconnect(
            source, receiver_port_id, kReceivingEndDoesntExistError);
        return;
      }
    }
  
    // Note: we use the source's profile here. If the source is an incognito
    // process, we will use the incognito EPM to find the right extension process,
    // which depends on whether the extension uses spanning or split mode.
    MessagePort* receiver = new ExtensionMessagePort(
        GetExtensionProcess(profile, target_extension_id), MSG_ROUTING_CONTROL,
        target_extension_id);
  
    // Include info about the opener's tab (if it was a tab).
    scoped_ptr<base::DictionaryValue> source_tab;
    GURL source_url_for_tab;
  
    if (source_contents && ExtensionTabUtil::GetTabId(source_contents) >= 0) {
      // Platform apps can be sent messages, but don't have a Tab concept.
      if (!target_extension->is_platform_app())
        source_tab.reset(ExtensionTabUtil::CreateTabValue(source_contents));
      source_url_for_tab = source_url;
    }
  
    OpenChannelParams* params = new OpenChannelParams(source,
                                                      source_tab.Pass(),
                                                      receiver,
                                                      receiver_port_id,
                                                      source_extension_id,
                                                      target_extension_id,
                                                      source_url_for_tab,
                                                      channel_name,
                                                      include_tls_channel_id);
  
    // If the target requests the TLS channel id, begin the lookup for it.
    // The target might also be a lazy background page, checked next, but the
    // loading of lazy background pages continues asynchronously, so enqueue
    // messages awaiting TLS channel ID first.
    if (include_tls_channel_id) {
      pending_tls_channel_id_channels_[GET_CHANNEL_ID(params->receiver_port_id)]
          = PendingMessagesQueue();
      property_provider_.GetDomainBoundCert(profile, params->source_url,
          base::Bind(&MessageService::GotDomainBoundCert,
                     weak_factory_.GetWeakPtr(),
                     base::Passed(make_scoped_ptr(params))));
      return;
    }
  
    // The target might be a lazy background page. In that case, we have to check
    // if it is loaded and ready, and if not, queue up the task and load the
    // page.
    if (MaybeAddPendingLazyBackgroundPageOpenChannelTask(profile,
                                                         target_extension,
                                                         params)) {
      return;
    }
  
    OpenChannelImpl(make_scoped_ptr(params));
  }
  
  void MessageService::OpenChannelToNativeApp(
      int source_process_id,
      int source_routing_id,
      int receiver_port_id,
      const std::string& source_extension_id,
      const std::string& native_app_name) {
    content::RenderProcessHost* source =
        content::RenderProcessHost::FromID(source_process_id);
    if (!source)
      return;
  
  #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
    Profile* profile = Profile::FromBrowserContext(source->GetBrowserContext());
    ExtensionService* extension_service =
        ExtensionSystem::Get(profile)->extension_service();
    bool has_permission = false;
    if (extension_service) {
      const Extension* extension =
          extension_service->GetExtensionById(source_extension_id, false);
      has_permission = extension && extension->HasAPIPermission(
          APIPermission::kNativeMessaging);
    }
  
    if (!has_permission) {
      DispatchOnDisconnect(source, receiver_port_id, kMissingPermissionError);
      return;
    }
  
    scoped_ptr<MessageChannel> channel(new MessageChannel());
    channel->opener.reset(new ExtensionMessagePort(source, MSG_ROUTING_CONTROL,
                                                   source_extension_id));
  
    // Get handle of the native view and pass it to the native messaging host.
    gfx::NativeView native_view =
        content::RenderWidgetHost::FromID(source_process_id, source_routing_id)->
            GetView()->GetNativeView();
  
    scoped_ptr<NativeMessageProcessHost> native_process =
        NativeMessageProcessHost::Create(
            native_view,
            base::WeakPtr<NativeMessageProcessHost::Client>(
                weak_factory_.GetWeakPtr()),
            source_extension_id, native_app_name, receiver_port_id);
  
    // Abandon the channel.
    if (!native_process.get()) {
      LOG(ERROR) << "Failed to create native process.";
      DispatchOnDisconnect(
          source, receiver_port_id, kReceivingEndDoesntExistError);
      return;
    }
    channel->receiver.reset(new NativeMessagePort(native_process.release()));
  
    // Keep the opener alive until the channel is closed.
    channel->opener->IncrementLazyKeepaliveCount();
  
    AddChannel(channel.release(), receiver_port_id);
  #else  // !(defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX))
    const char kNativeMessagingNotSupportedError[] =
        "Native Messaging is not supported on this platform.";
    DispatchOnDisconnect(
        source, receiver_port_id, kNativeMessagingNotSupportedError);
  #endif  // !(defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX))
  }
  
  void MessageService::OpenChannelToTab(
      int source_process_id, int source_routing_id, int receiver_port_id,
      int tab_id, const std::string& extension_id,
      const std::string& channel_name) {
    content::RenderProcessHost* source =
        content::RenderProcessHost::FromID(source_process_id);
    if (!source)
      return;
    Profile* profile = Profile::FromBrowserContext(source->GetBrowserContext());
  
    WebContents* contents = NULL;
    scoped_ptr<MessagePort> receiver;
    if (ExtensionTabUtil::GetTabById(tab_id, profile, true,
                                     NULL, NULL, &contents, NULL)) {
      receiver.reset(new ExtensionMessagePort(
          contents->GetRenderProcessHost(),
          contents->GetRenderViewHost()->GetRoutingID(),
          extension_id));
    }
  
    if (contents && contents->GetController().NeedsReload()) {
      // The tab isn't loaded yet. Don't attempt to connect.
      DispatchOnDisconnect(
          source, receiver_port_id, kReceivingEndDoesntExistError);
      return;
    }
  
    scoped_ptr<OpenChannelParams> params(new OpenChannelParams(
          source,
          scoped_ptr<base::DictionaryValue>(),  // Source tab doesn't make sense
                                                // for opening to tabs.
          receiver.release(),
          receiver_port_id,
          extension_id,
          extension_id,
          GURL(),  // Source URL doesn't make sense for opening to tabs.
          channel_name,
          false));  // Connections to tabs don't get TLS channel IDs.
    OpenChannelImpl(params.Pass());
  }
  
  bool MessageService::OpenChannelImpl(scoped_ptr<OpenChannelParams> params) {
    if (!params->source)
      return false;  // Closed while in flight.
  
    if (!params->receiver || !params->receiver->GetRenderProcessHost()) {
      DispatchOnDisconnect(params->source,
                           params->receiver_port_id,
                           kReceivingEndDoesntExistError);
      return false;
    }
  
    // Add extra paranoid CHECKs, since we have crash reports of this being NULL.
    // http://code.google.com/p/chromium/issues/detail?id=19067
    CHECK(params->receiver->GetRenderProcessHost());
  
    MessageChannel* channel(new MessageChannel);
    channel->opener.reset(new ExtensionMessagePort(params->source,
                                                   MSG_ROUTING_CONTROL,
                                                   params->source_extension_id));
    channel->receiver.reset(params->receiver.release());
  
    CHECK(channel->receiver->GetRenderProcessHost());
  
    AddChannel(channel, params->receiver_port_id);
  
    CHECK(channel->receiver->GetRenderProcessHost());
  
    // Send the connect event to the receiver.  Give it the opener's port ID (the
    // opener has the opposite port ID).
    channel->receiver->DispatchOnConnect(params->receiver_port_id,
                                         params->channel_name,
                                         params->source_tab,
                                         params->source_extension_id,
                                         params->target_extension_id,
                                         params->source_url,
                                         params->tls_channel_id);
  
    // Keep both ends of the channel alive until the channel is closed.
    channel->opener->IncrementLazyKeepaliveCount();
    channel->receiver->IncrementLazyKeepaliveCount();
    return true;
  }
  
  void MessageService::AddChannel(MessageChannel* channel, int receiver_port_id) {
    int channel_id = GET_CHANNEL_ID(receiver_port_id);
    CHECK(channels_.find(channel_id) == channels_.end());
    channels_[channel_id] = channel;
    pending_lazy_background_page_channels_.erase(channel_id);
  }
  
  void MessageService::CloseChannel(int port_id,
                                    const std::string& error_message) {
    // Note: The channel might be gone already, if the other side closed first.
    int channel_id = GET_CHANNEL_ID(port_id);
    MessageChannelMap::iterator it = channels_.find(channel_id);
    if (it == channels_.end()) {
      PendingLazyBackgroundPageChannelMap::iterator pending =
          pending_lazy_background_page_channels_.find(channel_id);
      if (pending != pending_lazy_background_page_channels_.end()) {
        lazy_background_task_queue_->AddPendingTask(
            pending->second.first, pending->second.second,
            base::Bind(&MessageService::PendingLazyBackgroundPageCloseChannel,
                       weak_factory_.GetWeakPtr(), port_id, error_message));
      }
      return;
    }
    CloseChannelImpl(it, port_id, error_message, true);
  }
  
  void MessageService::CloseChannelImpl(
      MessageChannelMap::iterator channel_iter,
      int closing_port_id,
      const std::string& error_message,
      bool notify_other_port) {
    MessageChannel* channel = channel_iter->second;
  
    // Notify the other side.
    if (notify_other_port) {
      MessagePort* port = IS_OPENER_PORT_ID(closing_port_id) ?
          channel->receiver.get() : channel->opener.get();
      port->DispatchOnDisconnect(GET_OPPOSITE_PORT_ID(closing_port_id),
                                 error_message);
    }
  
    // Balance the IncrementLazyKeepaliveCount() in OpenChannelImpl.
    channel->opener->DecrementLazyKeepaliveCount();
    channel->receiver->DecrementLazyKeepaliveCount();
  
    delete channel_iter->second;
    channels_.erase(channel_iter);
  }
  
  void MessageService::PostMessage(
      int source_port_id, const Message& message) {
    int channel_id = GET_CHANNEL_ID(source_port_id);
    MessageChannelMap::iterator iter = channels_.find(channel_id);
    if (iter == channels_.end()) {
      // If this channel is pending, queue up the PostMessage to run once
      // the channel opens.
      EnqueuePendingMessage(source_port_id, channel_id, message);
      return;
    }
  
    DispatchMessage(source_port_id, iter->second, message);
  }
  
  void MessageService::PostMessageFromNativeProcess(int port_id,
                                                    const std::string& message) {
    PostMessage(port_id, Message(message, false /* user_gesture */));
  }
  
  void MessageService::Observe(int type,
                               const content::NotificationSource& source,
                               const content::NotificationDetails& details) {
    switch (type) {
      case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
      case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
        content::RenderProcessHost* renderer =
            content::Source<content::RenderProcessHost>(source).ptr();
        OnProcessClosed(renderer);
        break;
      }
      default:
        NOTREACHED();
        return;
    }
  }
  
  void MessageService::OnProcessClosed(content::RenderProcessHost* process) {
    // Close any channels that share this renderer.  We notify the opposite
    // port that his pair has closed.
    for (MessageChannelMap::iterator it = channels_.begin();
         it != channels_.end(); ) {
      MessageChannelMap::iterator current = it++;
  
      content::RenderProcessHost* opener_process =
          current->second->opener->GetRenderProcessHost();
      content::RenderProcessHost* receiver_process =
          current->second->receiver->GetRenderProcessHost();
  
      // Only notify the other side if it has a different porocess host.
      bool notify_other_port = opener_process && receiver_process &&
          opener_process != receiver_process;
  
      if (opener_process == process) {
        CloseChannelImpl(current, GET_CHANNEL_OPENER_ID(current->first),
                         std::string(), notify_other_port);
      } else if (receiver_process == process) {
        CloseChannelImpl(current, GET_CHANNEL_RECEIVERS_ID(current->first),
                         std::string(), notify_other_port);
      }
    }
  }
  
  void MessageService::EnqueuePendingMessage(int source_port_id,
                                             int channel_id,
                                             const Message& message) {
    PendingTlsChannelIdMap::iterator pending_for_tls_channel_id =
        pending_tls_channel_id_channels_.find(channel_id);
    if (pending_for_tls_channel_id != pending_tls_channel_id_channels_.end()) {
      pending_for_tls_channel_id->second.push_back(
          PendingMessage(source_port_id, message));
      // Pending messages must only be pending the TLS channel ID or lazy
      // background page loading, never both.
      return;
    }
    EnqueuePendingMessageForLazyBackgroundLoad(source_port_id,
                                               channel_id,
                                               message);
  }
  
  void MessageService::EnqueuePendingMessageForLazyBackgroundLoad(
      int source_port_id,
      int channel_id,
      const Message& message) {
    PendingLazyBackgroundPageChannelMap::iterator pending =
        pending_lazy_background_page_channels_.find(channel_id);
    if (pending != pending_lazy_background_page_channels_.end()) {
      lazy_background_task_queue_->AddPendingTask(
          pending->second.first, pending->second.second,
          base::Bind(&MessageService::PendingLazyBackgroundPagePostMessage,
                     weak_factory_.GetWeakPtr(), source_port_id, message));
    }
  }
  
  void MessageService::DispatchMessage(int source_port_id,
                                       MessageChannel* channel,
                                       const Message& message) {
    // Figure out which port the ID corresponds to.
    int dest_port_id = GET_OPPOSITE_PORT_ID(source_port_id);
    MessagePort* port = IS_OPENER_PORT_ID(dest_port_id) ?
        channel->opener.get() : channel->receiver.get();
  
    port->DispatchOnMessage(message, dest_port_id);
  }
  
  bool MessageService::MaybeAddPendingLazyBackgroundPageOpenChannelTask(
      Profile* profile,
      const Extension* extension,
      OpenChannelParams* params) {
    if (!BackgroundInfo::HasLazyBackgroundPage(extension))
      return false;
  
    // If the extension uses spanning incognito mode, make sure we're always
    // using the original profile since that is what the extension process
    // will use.
    if (!IncognitoInfo::IsSplitMode(extension))
      profile = profile->GetOriginalProfile();
  
    if (!lazy_background_task_queue_->ShouldEnqueueTask(profile, extension))
      return false;
  
    pending_lazy_background_page_channels_[
        GET_CHANNEL_ID(params->receiver_port_id)] =
        PendingLazyBackgroundPageChannel(profile, extension->id());
    scoped_ptr<OpenChannelParams> scoped_params(params);
    lazy_background_task_queue_->AddPendingTask(profile, extension->id(),
        base::Bind(&MessageService::PendingLazyBackgroundPageOpenChannel,
                   weak_factory_.GetWeakPtr(), base::Passed(&scoped_params),
                   params->source->GetID()));
    return true;
  }
  
  void MessageService::GotDomainBoundCert(scoped_ptr<OpenChannelParams> params,
                                          const std::string& tls_channel_id) {
    params->tls_channel_id.assign(tls_channel_id);
    int channel_id = GET_CHANNEL_ID(params->receiver_port_id);
  
    PendingTlsChannelIdMap::iterator pending_for_tls_channel_id =
        pending_tls_channel_id_channels_.find(channel_id);
    if (pending_for_tls_channel_id == pending_tls_channel_id_channels_.end()) {
      NOTREACHED();
      return;
    }
  
    Profile* profile = Profile::FromBrowserContext(
        params->source->GetBrowserContext());
  
    const Extension* target_extension = ExtensionSystem::Get(profile)->
        extension_service()->extensions()->GetByID(params->target_extension_id);
    if (!target_extension) {
      pending_tls_channel_id_channels_.erase(channel_id);
      DispatchOnDisconnect(
          params->source, params->receiver_port_id,
          kReceivingEndDoesntExistError);
      return;
    }
    PendingMessagesQueue& pending_messages = pending_for_tls_channel_id->second;
    if (MaybeAddPendingLazyBackgroundPageOpenChannelTask(profile,
                                                         target_extension,
                                                         params.get())) {
      // Lazy background queue took ownership. Release ours.
      ignore_result(params.release());
      // Messages queued up waiting for the TLS channel ID now need to be queued
      // up for the lazy background page to load.
      for (PendingMessagesQueue::iterator it = pending_messages.begin();
           it != pending_messages.end();
           it++) {
        EnqueuePendingMessageForLazyBackgroundLoad(it->first, channel_id,
                                                   it->second);
      }
    } else {
      OpenChannelImpl(params.Pass());
      // Messages queued up waiting for the TLS channel ID can be posted now.
      MessageChannelMap::iterator channel_iter = channels_.find(channel_id);
      if (channel_iter != channels_.end()) {
        for (PendingMessagesQueue::iterator it = pending_messages.begin();
             it != pending_messages.end();
             it++) {
          DispatchMessage(it->first, channel_iter->second, it->second);
        }
      }
    }
    pending_tls_channel_id_channels_.erase(channel_id);
  }
  
  void MessageService::PendingLazyBackgroundPageOpenChannel(
      scoped_ptr<OpenChannelParams> params,
      int source_process_id,
      ExtensionHost* host) {
    if (!host)
      return;  // TODO(mpcomplete): notify source of disconnect?
  
    // Re-lookup the source process since it may no longer be valid.
    content::RenderProcessHost* source =
        content::RenderProcessHost::FromID(source_process_id);
    if (!source)
      return;
  
    params->source = source;
    params->receiver.reset(new ExtensionMessagePort(host->render_process_host(),
                                                    MSG_ROUTING_CONTROL,
                                                    params->target_extension_id));
    OpenChannelImpl(params.Pass());
  }
  
  void MessageService::DispatchOnDisconnect(content::RenderProcessHost* source,
                                            int port_id,
                                            const std::string& error_message) {
    ExtensionMessagePort port(source, MSG_ROUTING_CONTROL, "");
    port.DispatchOnDisconnect(GET_OPPOSITE_PORT_ID(port_id), error_message);
  }
  
  }  // namespace extensions
  