| // 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; |
| } |