Home | History | Annotate | Download | only in declarative
      1 // Copyright (c) 2012 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/api/declarative/rules_registry_service.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/lazy_instance.h"
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "chrome/browser/chrome_notification_types.h"
     12 #include "chrome/browser/extensions/api/declarative/rules_cache_delegate.h"
     13 #include "chrome/browser/extensions/api/declarative_content/content_rules_registry.h"
     14 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
     15 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry.h"
     16 #include "chrome/browser/extensions/api/web_request/web_request_api.h"
     17 #include "content/public/browser/browser_thread.h"
     18 #include "content/public/browser/notification_details.h"
     19 #include "content/public/browser/notification_service.h"
     20 #include "content/public/browser/notification_source.h"
     21 #include "content/public/browser/render_process_host.h"
     22 #include "extensions/common/extension.h"
     23 
     24 namespace extensions {
     25 
     26 namespace {
     27 
     28 // Registers |web_request_rules_registry| on the IO thread.
     29 void RegisterToExtensionWebRequestEventRouterOnIO(
     30     void* profile,
     31     const RulesRegistryService::WebViewKey& webview_key,
     32     scoped_refptr<WebRequestRulesRegistry> web_request_rules_registry) {
     33   ExtensionWebRequestEventRouter::GetInstance()->RegisterRulesRegistry(
     34       profile, webview_key, web_request_rules_registry);
     35 }
     36 
     37 bool IsWebView(const RulesRegistryService::WebViewKey& webview_key) {
     38   return webview_key.embedder_process_id && webview_key.webview_instance_id;
     39 }
     40 
     41 }  // namespace
     42 
     43 RulesRegistryService::RulesRegistryService(Profile* profile)
     44     : content_rules_registry_(NULL),
     45       profile_(profile) {
     46   if (profile) {
     47     registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
     48         content::Source<Profile>(profile->GetOriginalProfile()));
     49     registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
     50         content::Source<Profile>(profile->GetOriginalProfile()));
     51     registrar_.Add(this,
     52                    chrome::NOTIFICATION_EXTENSION_LOADED,
     53                    content::Source<Profile>(profile_->GetOriginalProfile()));
     54     registrar_.Add(
     55         this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
     56         content::NotificationService::AllBrowserContextsAndSources());
     57     EnsureDefaultRulesRegistriesRegistered(WebViewKey(0, 0));
     58   }
     59 }
     60 
     61 RulesRegistryService::~RulesRegistryService() {}
     62 
     63 void RulesRegistryService::EnsureDefaultRulesRegistriesRegistered(
     64     const WebViewKey& webview_key) {
     65   if (!profile_)
     66     return;
     67 
     68   RulesRegistryKey key(declarative_webrequest_constants::kOnRequest,
     69                        webview_key);
     70   // If we can find the key in the |rule_registries_| then we have already
     71   // installed the default registries.
     72   if (ContainsKey(rule_registries_, key))
     73     return;
     74 
     75 
     76   RulesCacheDelegate* web_request_cache_delegate = NULL;
     77   if (!IsWebView(webview_key)) {
     78     web_request_cache_delegate =
     79         new RulesCacheDelegate(true /*log_storage_init_delay*/);
     80     cache_delegates_.push_back(web_request_cache_delegate);
     81   }
     82   scoped_refptr<WebRequestRulesRegistry> web_request_rules_registry(
     83       new WebRequestRulesRegistry(profile_,
     84                                   web_request_cache_delegate,
     85                                   webview_key));
     86 
     87   RegisterRulesRegistry(web_request_rules_registry);
     88   content::BrowserThread::PostTask(
     89       content::BrowserThread::IO, FROM_HERE,
     90       base::Bind(&RegisterToExtensionWebRequestEventRouterOnIO,
     91           profile_, webview_key, web_request_rules_registry));
     92 
     93 #if defined(ENABLE_EXTENSIONS)
     94   // Only create a ContentRulesRegistry for regular pages and not webviews.
     95   if (!IsWebView(webview_key)) {
     96     RulesCacheDelegate* content_rules_cache_delegate =
     97         new RulesCacheDelegate(false /*log_storage_init_delay*/);
     98     cache_delegates_.push_back(content_rules_cache_delegate);
     99     scoped_refptr<ContentRulesRegistry> content_rules_registry(
    100         new ContentRulesRegistry(profile_, content_rules_cache_delegate));
    101     RegisterRulesRegistry(content_rules_registry);
    102     content_rules_registry_ = content_rules_registry.get();
    103   }
    104 #endif  // defined(ENABLE_EXTENSIONS)
    105 }
    106 
    107 void RulesRegistryService::Shutdown() {
    108   // Release the references to all registries. This would happen soon during
    109   // destruction of |*this|, but we need the ExtensionWebRequestEventRouter to
    110   // be the last to reference the WebRequestRulesRegistry objects, so that
    111   // the posted task below causes their destruction on the IO thread, not on UI
    112   // where the destruction of |*this| takes place.
    113   // TODO(vabr): Remove once http://crbug.com/218451#c6 gets addressed.
    114   rule_registries_.clear();
    115   content::BrowserThread::PostTask(
    116       content::BrowserThread::IO, FROM_HERE,
    117       base::Bind(&RegisterToExtensionWebRequestEventRouterOnIO,
    118           profile_, WebViewKey(0, 0),
    119           scoped_refptr<WebRequestRulesRegistry>(NULL)));
    120 }
    121 
    122 static base::LazyInstance<ProfileKeyedAPIFactory<RulesRegistryService> >
    123 g_factory = LAZY_INSTANCE_INITIALIZER;
    124 
    125 // static
    126 ProfileKeyedAPIFactory<RulesRegistryService>*
    127 RulesRegistryService::GetFactoryInstance() {
    128   return &g_factory.Get();
    129 }
    130 
    131 // static
    132 RulesRegistryService* RulesRegistryService::Get(Profile* profile) {
    133   return ProfileKeyedAPIFactory<RulesRegistryService>::GetForProfile(profile);
    134 }
    135 
    136 void RulesRegistryService::RegisterRulesRegistry(
    137     scoped_refptr<RulesRegistry> rule_registry) {
    138   const std::string event_name(rule_registry->event_name());
    139   RulesRegistryKey key(event_name, rule_registry->webview_key());
    140   DCHECK(rule_registries_.find(key) == rule_registries_.end());
    141   rule_registries_[key] = rule_registry;
    142 }
    143 
    144 scoped_refptr<RulesRegistry> RulesRegistryService::GetRulesRegistry(
    145     const WebViewKey& webview_key,
    146     const std::string& event_name) {
    147   EnsureDefaultRulesRegistriesRegistered(webview_key);
    148 
    149   RulesRegistryKey key(event_name, webview_key);
    150   RulesRegistryMap::const_iterator i = rule_registries_.find(key);
    151   if (i == rule_registries_.end())
    152     return scoped_refptr<RulesRegistry>();
    153   return i->second;
    154 }
    155 
    156 void RulesRegistryService::RemoveWebViewRulesRegistries(int process_id) {
    157   DCHECK_NE(0, process_id);
    158 
    159   std::set<RulesRegistryKey> registries_to_delete;
    160   for (RulesRegistryMap::iterator it = rule_registries_.begin();
    161        it != rule_registries_.end(); ++it) {
    162     const RulesRegistryKey& key = it->first;
    163     const WebViewKey& webview_key = key.webview_key;
    164     int embedder_process_id = webview_key.embedder_process_id;
    165     // |process_id| will always be non-zero.
    166     // |embedder_process_id| will only be non-zero if the key corresponds to a
    167     // webview registry.
    168     // Thus, |embedder_process_id| == |process_id| ==> the process ID is a
    169     // webview embedder.
    170     if (embedder_process_id != process_id)
    171       continue;
    172 
    173     // Modifying the container while iterating is bad so we'll save the keys we
    174     // wish to delete in another container, and delete them in another loop.
    175     registries_to_delete.insert(key);
    176   }
    177   for (std::set<RulesRegistryKey>::iterator it = registries_to_delete.begin();
    178        it != registries_to_delete.end(); ++it) {
    179     rule_registries_.erase(*it);
    180   }
    181 }
    182 
    183 void RulesRegistryService::SimulateExtensionUninstalled(
    184     const std::string& extension_id) {
    185   NotifyRegistriesHelper(&RulesRegistry::OnExtensionUninstalled, extension_id);
    186 }
    187 
    188 void RulesRegistryService::NotifyRegistriesHelper(
    189     void (RulesRegistry::*notification_callback)(const std::string&),
    190     const std::string& extension_id) {
    191   RulesRegistryMap::iterator i;
    192   for (i = rule_registries_.begin(); i != rule_registries_.end(); ++i) {
    193     scoped_refptr<RulesRegistry> registry = i->second;
    194     if (content::BrowserThread::CurrentlyOn(registry->owner_thread())) {
    195       (registry->*notification_callback)(extension_id);
    196     } else {
    197       content::BrowserThread::PostTask(
    198           registry->owner_thread(),
    199           FROM_HERE,
    200           base::Bind(notification_callback, registry, extension_id));
    201     }
    202   }
    203 }
    204 
    205 void RulesRegistryService::Observe(
    206     int type,
    207     const content::NotificationSource& source,
    208     const content::NotificationDetails& details) {
    209   switch (type) {
    210     case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
    211       const Extension* extension =
    212           content::Details<UnloadedExtensionInfo>(details)->extension;
    213       NotifyRegistriesHelper(&RulesRegistry::OnExtensionUnloaded,
    214                              extension->id());
    215       break;
    216     }
    217     case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
    218       const Extension* extension =
    219           content::Details<const Extension>(details).ptr();
    220       NotifyRegistriesHelper(&RulesRegistry::OnExtensionUninstalled,
    221                              extension->id());
    222       break;
    223     }
    224     case chrome::NOTIFICATION_EXTENSION_LOADED: {
    225       const Extension* extension =
    226           content::Details<const Extension>(details).ptr();
    227       NotifyRegistriesHelper(&RulesRegistry::OnExtensionLoaded,
    228                              extension->id());
    229       break;
    230     }
    231     case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
    232       content::RenderProcessHost* process =
    233           content::Source<content::RenderProcessHost>(source).ptr();
    234       RemoveWebViewRulesRegistries(process->GetID());
    235       break;
    236     }
    237     default:
    238       NOTREACHED();
    239       break;
    240   }
    241 }
    242 
    243 }  // namespace extensions
    244