普通文本  |  285行  |  10.02 KB

// Copyright (c) 2013 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/extension_renderer_state.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/browser/tab_contents/retargeting_details.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/process_type.h"

using content::BrowserThread;
using content::RenderProcessHost;
using content::RenderViewHost;
using content::WebContents;

//
// ExtensionRendererState::RenderViewHostObserver
//

class ExtensionRendererState::RenderViewHostObserver
    : public content::WebContentsObserver {
 public:
  RenderViewHostObserver(RenderViewHost* host, WebContents* web_contents)
      : content::WebContentsObserver(web_contents),
        render_view_host_(host) {
  }

  virtual void RenderViewDeleted(content::RenderViewHost* host) OVERRIDE {
    if (host != render_view_host_)
      return;
    BrowserThread::PostTask(
        BrowserThread::IO, FROM_HERE,
        base::Bind(
            &ExtensionRendererState::ClearTabAndWindowId,
            base::Unretained(ExtensionRendererState::GetInstance()),
            host->GetProcess()->GetID(), host->GetRoutingID()));

    delete this;
  }

 private:
  RenderViewHost* render_view_host_;

  DISALLOW_COPY_AND_ASSIGN(RenderViewHostObserver);
};

//
// ExtensionRendererState::TabObserver
//

// This class listens for notifications about changes in renderer state on the
// UI thread, and notifies the ExtensionRendererState on the IO thread. It
// should only ever be accessed on the UI thread.
class ExtensionRendererState::TabObserver
    : public content::NotificationObserver {
 public:
  TabObserver();
  virtual ~TabObserver();

 private:
  // content::NotificationObserver interface.
  virtual void Observe(int type,
                       const content::NotificationSource& source,
                       const content::NotificationDetails& details) OVERRIDE;

  content::NotificationRegistrar registrar_;
};

ExtensionRendererState::TabObserver::TabObserver() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  registrar_.Add(this,
                 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
                 content::NotificationService::AllBrowserContextsAndSources());
  registrar_.Add(this, chrome::NOTIFICATION_TAB_PARENTED,
                 content::NotificationService::AllBrowserContextsAndSources());
  registrar_.Add(this, chrome::NOTIFICATION_RETARGETING,
                 content::NotificationService::AllBrowserContextsAndSources());
}

ExtensionRendererState::TabObserver::~TabObserver() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
}

void ExtensionRendererState::TabObserver::Observe(
    int type, const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  switch (type) {
    case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: {
      WebContents* web_contents = content::Source<WebContents>(source).ptr();
      SessionTabHelper* session_tab_helper =
          SessionTabHelper::FromWebContents(web_contents);
      if (!session_tab_helper)
        return;
      RenderViewHost* host = content::Details<RenderViewHost>(details).ptr();
      // TODO(mpcomplete): How can we tell if window_id is bogus? It may not
      // have been set yet.
      BrowserThread::PostTask(
          BrowserThread::IO, FROM_HERE,
          base::Bind(
              &ExtensionRendererState::SetTabAndWindowId,
              base::Unretained(ExtensionRendererState::GetInstance()),
              host->GetProcess()->GetID(), host->GetRoutingID(),
              session_tab_helper->session_id().id(),
              session_tab_helper->window_id().id()));

      // The observer deletes itself.
      new ExtensionRendererState::RenderViewHostObserver(host, web_contents);

      break;
    }
    case chrome::NOTIFICATION_TAB_PARENTED: {
      WebContents* web_contents = content::Source<WebContents>(source).ptr();
      SessionTabHelper* session_tab_helper =
          SessionTabHelper::FromWebContents(web_contents);
      if (!session_tab_helper)
        return;
      RenderViewHost* host = web_contents->GetRenderViewHost();
      BrowserThread::PostTask(
          BrowserThread::IO, FROM_HERE,
          base::Bind(
              &ExtensionRendererState::SetTabAndWindowId,
              base::Unretained(ExtensionRendererState::GetInstance()),
              host->GetProcess()->GetID(), host->GetRoutingID(),
              session_tab_helper->session_id().id(),
              session_tab_helper->window_id().id()));
      break;
    }
    case chrome::NOTIFICATION_RETARGETING: {
      RetargetingDetails* retargeting_details =
          content::Details<RetargetingDetails>(details).ptr();
      WebContents* web_contents = retargeting_details->target_web_contents;
      SessionTabHelper* session_tab_helper =
          SessionTabHelper::FromWebContents(web_contents);
      if (!session_tab_helper)
        return;
      RenderViewHost* host = web_contents->GetRenderViewHost();
      BrowserThread::PostTask(
          BrowserThread::IO, FROM_HERE,
          base::Bind(
              &ExtensionRendererState::SetTabAndWindowId,
              base::Unretained(ExtensionRendererState::GetInstance()),
              host->GetProcess()->GetID(), host->GetRoutingID(),
              session_tab_helper->session_id().id(),
              session_tab_helper->window_id().id()));
      break;
    }
    default:
      NOTREACHED();
      return;
  }
}

//
// ExtensionRendererState
//

ExtensionRendererState::ExtensionRendererState() : observer_(NULL) {
}

ExtensionRendererState::~ExtensionRendererState() {
}

// static
ExtensionRendererState* ExtensionRendererState::GetInstance() {
  return Singleton<ExtensionRendererState>::get();
}

void ExtensionRendererState::Init() {
  observer_ = new TabObserver;
}

void ExtensionRendererState::Shutdown() {
  delete observer_;
}

void ExtensionRendererState::SetTabAndWindowId(
    int render_process_host_id, int routing_id, int tab_id, int window_id) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  RenderId render_id(render_process_host_id, routing_id);
  map_[render_id] = TabAndWindowId(tab_id, window_id);
}

void ExtensionRendererState::ClearTabAndWindowId(
    int render_process_host_id, int routing_id) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  RenderId render_id(render_process_host_id, routing_id);
  map_.erase(render_id);
}

bool ExtensionRendererState::GetTabAndWindowId(
    const  content::ResourceRequestInfo* info, int* tab_id, int* window_id) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  int render_process_id;
  if (info->GetProcessType() == content::PROCESS_TYPE_PLUGIN) {
    render_process_id = info->GetOriginPID();
  } else {
    render_process_id = info->GetChildID();
  }
  int render_view_id = info->GetRouteID();
  RenderId render_id(render_process_id, render_view_id);
  TabAndWindowIdMap::iterator iter = map_.find(render_id);
  if (iter != map_.end()) {
    *tab_id = iter->second.first;
    *window_id = iter->second.second;
    return true;
  }
  return false;
}

bool ExtensionRendererState::IsWebViewRenderer(int render_process_id) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  return webview_partition_id_map_.find(render_process_id) !=
         webview_partition_id_map_.end();
}

void ExtensionRendererState::AddWebView(int guest_process_id,
                                        int guest_routing_id,
                                        const WebViewInfo& webview_info) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  RenderId render_id(guest_process_id, guest_routing_id);
  webview_info_map_[render_id] = webview_info;
  WebViewPartitionIDMap::iterator iter =
      webview_partition_id_map_.find(guest_process_id);
  if (iter != webview_partition_id_map_.end()) {
    ++iter->second.web_view_count;
    return;
  }
  WebViewPartitionInfo partition_info(1, webview_info.partition_id);
  webview_partition_id_map_[guest_process_id] = partition_info;
}

void ExtensionRendererState::RemoveWebView(int guest_process_id,
                                           int guest_routing_id) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  RenderId render_id(guest_process_id, guest_routing_id);
  webview_info_map_.erase(render_id);
  WebViewPartitionIDMap::iterator iter =
      webview_partition_id_map_.find(guest_process_id);
  if (iter != webview_partition_id_map_.end() &&
      iter->second.web_view_count > 1) {
    --iter->second.web_view_count;
    return;
  }
  webview_partition_id_map_.erase(guest_process_id);
}

bool ExtensionRendererState::GetWebViewInfo(int guest_process_id,
                                            int guest_routing_id,
                                            WebViewInfo* webview_info) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  RenderId render_id(guest_process_id, guest_routing_id);
  WebViewInfoMap::iterator iter = webview_info_map_.find(render_id);
  if (iter != webview_info_map_.end()) {
    *webview_info = iter->second;
    return true;
  }
  return false;
}

bool ExtensionRendererState::GetWebViewPartitionID(int guest_process_id,
                                                   std::string* partition_id) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  WebViewPartitionIDMap::iterator iter =
      webview_partition_id_map_.find(guest_process_id);
  if (iter != webview_partition_id_map_.end()) {
    *partition_id = iter->second.partition_id;
    return true;
  }
  return false;
}