// 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/web_contents.h" #include "content/public/browser/web_contents_observer.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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(BrowserThread::IO)); RenderId render_id(render_process_host_id, routing_id); map_.erase(render_id); } bool ExtensionRendererState::GetTabAndWindowId( int render_process_host_id, int routing_id, int* tab_id, int* window_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); RenderId render_id(render_process_host_id, routing_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(BrowserThread::CurrentlyOn(BrowserThread::IO)); for (WebViewInfoMap::iterator i = webview_info_map_.begin(); i != webview_info_map_.end(); ++i) { if (i->first.first == render_process_id) return true; } return false; } void ExtensionRendererState::AddWebView(int guest_process_id, int guest_routing_id, const WebViewInfo& webview_info) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); RenderId render_id(guest_process_id, guest_routing_id); webview_info_map_[render_id] = webview_info; } void ExtensionRendererState::RemoveWebView(int guest_process_id, int guest_routing_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); RenderId render_id(guest_process_id, guest_routing_id); webview_info_map_.erase(render_id); } bool ExtensionRendererState::GetWebViewInfo(int guest_process_id, int guest_routing_id, WebViewInfo* webview_info) { DCHECK(BrowserThread::CurrentlyOn(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; }