Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2013 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/extension_renderer_state.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "chrome/browser/chrome_notification_types.h"
     10 #include "chrome/browser/sessions/session_tab_helper.h"
     11 #include "chrome/browser/tab_contents/retargeting_details.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "content/public/browser/navigation_details.h"
     14 #include "content/public/browser/notification_observer.h"
     15 #include "content/public/browser/notification_registrar.h"
     16 #include "content/public/browser/notification_service.h"
     17 #include "content/public/browser/notification_types.h"
     18 #include "content/public/browser/render_process_host.h"
     19 #include "content/public/browser/render_view_host.h"
     20 #include "content/public/browser/resource_request_info.h"
     21 #include "content/public/browser/web_contents.h"
     22 #include "content/public/browser/web_contents_observer.h"
     23 #include "content/public/common/process_type.h"
     24 
     25 using content::BrowserThread;
     26 using content::RenderProcessHost;
     27 using content::RenderViewHost;
     28 using content::WebContents;
     29 
     30 //
     31 // ExtensionRendererState::RenderViewHostObserver
     32 //
     33 
     34 class ExtensionRendererState::RenderViewHostObserver
     35     : public content::WebContentsObserver {
     36  public:
     37   RenderViewHostObserver(RenderViewHost* host, WebContents* web_contents)
     38       : content::WebContentsObserver(web_contents),
     39         render_view_host_(host) {
     40   }
     41 
     42   virtual void RenderViewDeleted(content::RenderViewHost* host) OVERRIDE {
     43     if (host != render_view_host_)
     44       return;
     45     BrowserThread::PostTask(
     46         BrowserThread::IO, FROM_HERE,
     47         base::Bind(
     48             &ExtensionRendererState::ClearTabAndWindowId,
     49             base::Unretained(ExtensionRendererState::GetInstance()),
     50             host->GetProcess()->GetID(), host->GetRoutingID()));
     51 
     52     delete this;
     53   }
     54 
     55  private:
     56   RenderViewHost* render_view_host_;
     57 
     58   DISALLOW_COPY_AND_ASSIGN(RenderViewHostObserver);
     59 };
     60 
     61 //
     62 // ExtensionRendererState::TabObserver
     63 //
     64 
     65 // This class listens for notifications about changes in renderer state on the
     66 // UI thread, and notifies the ExtensionRendererState on the IO thread. It
     67 // should only ever be accessed on the UI thread.
     68 class ExtensionRendererState::TabObserver
     69     : public content::NotificationObserver {
     70  public:
     71   TabObserver();
     72   virtual ~TabObserver();
     73 
     74  private:
     75   // content::NotificationObserver interface.
     76   virtual void Observe(int type,
     77                        const content::NotificationSource& source,
     78                        const content::NotificationDetails& details) OVERRIDE;
     79 
     80   content::NotificationRegistrar registrar_;
     81 };
     82 
     83 ExtensionRendererState::TabObserver::TabObserver() {
     84   DCHECK_CURRENTLY_ON(BrowserThread::UI);
     85   registrar_.Add(this,
     86                  content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
     87                  content::NotificationService::AllBrowserContextsAndSources());
     88   registrar_.Add(this, chrome::NOTIFICATION_TAB_PARENTED,
     89                  content::NotificationService::AllBrowserContextsAndSources());
     90   registrar_.Add(this, chrome::NOTIFICATION_RETARGETING,
     91                  content::NotificationService::AllBrowserContextsAndSources());
     92 }
     93 
     94 ExtensionRendererState::TabObserver::~TabObserver() {
     95   DCHECK_CURRENTLY_ON(BrowserThread::UI);
     96 }
     97 
     98 void ExtensionRendererState::TabObserver::Observe(
     99     int type, const content::NotificationSource& source,
    100     const content::NotificationDetails& details) {
    101   switch (type) {
    102     case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: {
    103       WebContents* web_contents = content::Source<WebContents>(source).ptr();
    104       SessionTabHelper* session_tab_helper =
    105           SessionTabHelper::FromWebContents(web_contents);
    106       if (!session_tab_helper)
    107         return;
    108       RenderViewHost* host = content::Details<RenderViewHost>(details).ptr();
    109       // TODO(mpcomplete): How can we tell if window_id is bogus? It may not
    110       // have been set yet.
    111       BrowserThread::PostTask(
    112           BrowserThread::IO, FROM_HERE,
    113           base::Bind(
    114               &ExtensionRendererState::SetTabAndWindowId,
    115               base::Unretained(ExtensionRendererState::GetInstance()),
    116               host->GetProcess()->GetID(), host->GetRoutingID(),
    117               session_tab_helper->session_id().id(),
    118               session_tab_helper->window_id().id()));
    119 
    120       // The observer deletes itself.
    121       new ExtensionRendererState::RenderViewHostObserver(host, web_contents);
    122 
    123       break;
    124     }
    125     case chrome::NOTIFICATION_TAB_PARENTED: {
    126       WebContents* web_contents = content::Source<WebContents>(source).ptr();
    127       SessionTabHelper* session_tab_helper =
    128           SessionTabHelper::FromWebContents(web_contents);
    129       if (!session_tab_helper)
    130         return;
    131       RenderViewHost* host = web_contents->GetRenderViewHost();
    132       BrowserThread::PostTask(
    133           BrowserThread::IO, FROM_HERE,
    134           base::Bind(
    135               &ExtensionRendererState::SetTabAndWindowId,
    136               base::Unretained(ExtensionRendererState::GetInstance()),
    137               host->GetProcess()->GetID(), host->GetRoutingID(),
    138               session_tab_helper->session_id().id(),
    139               session_tab_helper->window_id().id()));
    140       break;
    141     }
    142     case chrome::NOTIFICATION_RETARGETING: {
    143       RetargetingDetails* retargeting_details =
    144           content::Details<RetargetingDetails>(details).ptr();
    145       WebContents* web_contents = retargeting_details->target_web_contents;
    146       SessionTabHelper* session_tab_helper =
    147           SessionTabHelper::FromWebContents(web_contents);
    148       if (!session_tab_helper)
    149         return;
    150       RenderViewHost* host = web_contents->GetRenderViewHost();
    151       BrowserThread::PostTask(
    152           BrowserThread::IO, FROM_HERE,
    153           base::Bind(
    154               &ExtensionRendererState::SetTabAndWindowId,
    155               base::Unretained(ExtensionRendererState::GetInstance()),
    156               host->GetProcess()->GetID(), host->GetRoutingID(),
    157               session_tab_helper->session_id().id(),
    158               session_tab_helper->window_id().id()));
    159       break;
    160     }
    161     default:
    162       NOTREACHED();
    163       return;
    164   }
    165 }
    166 
    167 //
    168 // ExtensionRendererState
    169 //
    170 
    171 ExtensionRendererState::ExtensionRendererState() : observer_(NULL) {
    172 }
    173 
    174 ExtensionRendererState::~ExtensionRendererState() {
    175 }
    176 
    177 // static
    178 ExtensionRendererState* ExtensionRendererState::GetInstance() {
    179   return Singleton<ExtensionRendererState>::get();
    180 }
    181 
    182 void ExtensionRendererState::Init() {
    183   observer_ = new TabObserver;
    184 }
    185 
    186 void ExtensionRendererState::Shutdown() {
    187   delete observer_;
    188 }
    189 
    190 void ExtensionRendererState::SetTabAndWindowId(
    191     int render_process_host_id, int routing_id, int tab_id, int window_id) {
    192   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    193   RenderId render_id(render_process_host_id, routing_id);
    194   map_[render_id] = TabAndWindowId(tab_id, window_id);
    195 }
    196 
    197 void ExtensionRendererState::ClearTabAndWindowId(
    198     int render_process_host_id, int routing_id) {
    199   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    200   RenderId render_id(render_process_host_id, routing_id);
    201   map_.erase(render_id);
    202 }
    203 
    204 bool ExtensionRendererState::GetTabAndWindowId(
    205     const  content::ResourceRequestInfo* info, int* tab_id, int* window_id) {
    206   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    207   int render_process_id;
    208   if (info->GetProcessType() == content::PROCESS_TYPE_PLUGIN) {
    209     render_process_id = info->GetOriginPID();
    210   } else {
    211     render_process_id = info->GetChildID();
    212   }
    213   int render_view_id = info->GetRouteID();
    214   RenderId render_id(render_process_id, render_view_id);
    215   TabAndWindowIdMap::iterator iter = map_.find(render_id);
    216   if (iter != map_.end()) {
    217     *tab_id = iter->second.first;
    218     *window_id = iter->second.second;
    219     return true;
    220   }
    221   return false;
    222 }
    223 
    224 bool ExtensionRendererState::IsWebViewRenderer(int render_process_id) {
    225   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    226   return webview_partition_id_map_.find(render_process_id) !=
    227          webview_partition_id_map_.end();
    228 }
    229 
    230 void ExtensionRendererState::AddWebView(int guest_process_id,
    231                                         int guest_routing_id,
    232                                         const WebViewInfo& webview_info) {
    233   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    234   RenderId render_id(guest_process_id, guest_routing_id);
    235   webview_info_map_[render_id] = webview_info;
    236   WebViewPartitionIDMap::iterator iter =
    237       webview_partition_id_map_.find(guest_process_id);
    238   if (iter != webview_partition_id_map_.end()) {
    239     ++iter->second.web_view_count;
    240     return;
    241   }
    242   WebViewPartitionInfo partition_info(1, webview_info.partition_id);
    243   webview_partition_id_map_[guest_process_id] = partition_info;
    244 }
    245 
    246 void ExtensionRendererState::RemoveWebView(int guest_process_id,
    247                                            int guest_routing_id) {
    248   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    249   RenderId render_id(guest_process_id, guest_routing_id);
    250   webview_info_map_.erase(render_id);
    251   WebViewPartitionIDMap::iterator iter =
    252       webview_partition_id_map_.find(guest_process_id);
    253   if (iter != webview_partition_id_map_.end() &&
    254       iter->second.web_view_count > 1) {
    255     --iter->second.web_view_count;
    256     return;
    257   }
    258   webview_partition_id_map_.erase(guest_process_id);
    259 }
    260 
    261 bool ExtensionRendererState::GetWebViewInfo(int guest_process_id,
    262                                             int guest_routing_id,
    263                                             WebViewInfo* webview_info) {
    264   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    265   RenderId render_id(guest_process_id, guest_routing_id);
    266   WebViewInfoMap::iterator iter = webview_info_map_.find(render_id);
    267   if (iter != webview_info_map_.end()) {
    268     *webview_info = iter->second;
    269     return true;
    270   }
    271   return false;
    272 }
    273 
    274 bool ExtensionRendererState::GetWebViewPartitionID(int guest_process_id,
    275                                                    std::string* partition_id) {
    276   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    277   WebViewPartitionIDMap::iterator iter =
    278       webview_partition_id_map_.find(guest_process_id);
    279   if (iter != webview_partition_id_map_.end()) {
    280     *partition_id = iter->second.partition_id;
    281     return true;
    282   }
    283   return false;
    284 }
    285