Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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/user_script_listener.h"
      6 
      7 #include "chrome/browser/extensions/extension_service.h"
      8 #include "chrome/browser/profiles/profile.h"
      9 #include "chrome/common/extensions/extension.h"
     10 #include "chrome/common/extensions/url_pattern.h"
     11 #include "content/browser/browser_thread.h"
     12 #include "content/browser/renderer_host/global_request_id.h"
     13 #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
     14 #include "content/common/notification_service.h"
     15 #include "net/url_request/url_request.h"
     16 
     17 UserScriptListener::UserScriptListener()
     18     : resource_queue_(NULL),
     19       user_scripts_ready_(false) {
     20   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     21 
     22   registrar_.Add(this, NotificationType::EXTENSION_LOADED,
     23                  NotificationService::AllSources());
     24   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
     25                  NotificationService::AllSources());
     26   registrar_.Add(this, NotificationType::USER_SCRIPTS_UPDATED,
     27                  NotificationService::AllSources());
     28   AddRef();  // Will be balanced in Cleanup().
     29 }
     30 
     31 void UserScriptListener::Initialize(ResourceQueue* resource_queue) {
     32   resource_queue_ = resource_queue;
     33 }
     34 
     35 bool UserScriptListener::ShouldDelayRequest(
     36     net::URLRequest* request,
     37     const ResourceDispatcherHostRequestInfo& request_info,
     38     const GlobalRequestID& request_id) {
     39   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     40 
     41   // If it's a frame load, then we need to check the URL against the list of
     42   // user scripts to see if we need to wait.
     43   if (request_info.resource_type() != ResourceType::MAIN_FRAME &&
     44       request_info.resource_type() != ResourceType::SUB_FRAME) {
     45     return false;
     46   }
     47 
     48   if (user_scripts_ready_)
     49     return false;
     50 
     51   for (URLPatterns::iterator it = url_patterns_.begin();
     52        it != url_patterns_.end(); ++it) {
     53     if ((*it).MatchesUrl(request->url())) {
     54       // One of the user scripts wants to inject into this request, but the
     55       // script isn't ready yet. Delay the request.
     56       delayed_request_ids_.push_front(request_id);
     57       return true;
     58     }
     59   }
     60 
     61   return false;
     62 }
     63 
     64 void UserScriptListener::WillShutdownResourceQueue() {
     65   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     66   resource_queue_ = NULL;
     67 
     68   BrowserThread::PostTask(
     69       BrowserThread::UI, FROM_HERE,
     70       NewRunnableMethod(this, &UserScriptListener::Cleanup));
     71 }
     72 
     73 UserScriptListener::~UserScriptListener() {
     74 }
     75 
     76 void UserScriptListener::StartDelayedRequests() {
     77   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     78 
     79   user_scripts_ready_ = true;
     80 
     81   if (resource_queue_) {
     82     for (DelayedRequests::iterator it = delayed_request_ids_.begin();
     83          it != delayed_request_ids_.end(); ++it) {
     84       resource_queue_->StartDelayedRequest(this, *it);
     85     }
     86   }
     87 
     88   delayed_request_ids_.clear();
     89 }
     90 
     91 void UserScriptListener::AppendNewURLPatterns(const URLPatterns& new_patterns) {
     92   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     93 
     94   user_scripts_ready_ = false;
     95   url_patterns_.insert(url_patterns_.end(),
     96                        new_patterns.begin(), new_patterns.end());
     97 }
     98 
     99 void UserScriptListener::ReplaceURLPatterns(const URLPatterns& patterns) {
    100   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    101   url_patterns_ = patterns;
    102 }
    103 
    104 void UserScriptListener::Cleanup() {
    105   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    106   registrar_.RemoveAll();
    107   Release();
    108 }
    109 
    110 void UserScriptListener::CollectURLPatterns(const Extension* extension,
    111                                             URLPatterns* patterns) {
    112   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    113 
    114   const UserScriptList& scripts = extension->content_scripts();
    115   for (UserScriptList::const_iterator iter = scripts.begin();
    116        iter != scripts.end(); ++iter) {
    117     patterns->insert(patterns->end(),
    118                      (*iter).url_patterns().begin(),
    119                      (*iter).url_patterns().end());
    120   }
    121 }
    122 
    123 void UserScriptListener::Observe(NotificationType type,
    124                                  const NotificationSource& source,
    125                                  const NotificationDetails& details) {
    126   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    127 
    128   switch (type.value) {
    129     case NotificationType::EXTENSION_LOADED: {
    130       const Extension* extension = Details<const Extension>(details).ptr();
    131       if (extension->content_scripts().empty())
    132         return;  // no new patterns from this extension.
    133 
    134       URLPatterns new_patterns;
    135       CollectURLPatterns(Details<const Extension>(details).ptr(),
    136                          &new_patterns);
    137       if (!new_patterns.empty()) {
    138         BrowserThread::PostTask(
    139             BrowserThread::IO, FROM_HERE,
    140             NewRunnableMethod(
    141                 this, &UserScriptListener::AppendNewURLPatterns, new_patterns));
    142       }
    143       break;
    144     }
    145 
    146     case NotificationType::EXTENSION_UNLOADED: {
    147       const Extension* unloaded_extension =
    148           Details<UnloadedExtensionInfo>(details)->extension;
    149       if (unloaded_extension->content_scripts().empty())
    150         return;  // no patterns to delete for this extension.
    151 
    152       // Clear all our patterns and reregister all the still-loaded extensions.
    153       URLPatterns new_patterns;
    154       ExtensionService* service =
    155           Source<Profile>(source).ptr()->GetExtensionService();
    156       for (ExtensionList::const_iterator it = service->extensions()->begin();
    157            it != service->extensions()->end(); ++it) {
    158         if (*it != unloaded_extension)
    159           CollectURLPatterns(*it, &new_patterns);
    160       }
    161       BrowserThread::PostTask(
    162           BrowserThread::IO, FROM_HERE,
    163           NewRunnableMethod(
    164               this, &UserScriptListener::ReplaceURLPatterns, new_patterns));
    165       break;
    166     }
    167 
    168     case NotificationType::USER_SCRIPTS_UPDATED: {
    169       BrowserThread::PostTask(
    170           BrowserThread::IO, FROM_HERE,
    171           NewRunnableMethod(this, &UserScriptListener::StartDelayedRequests));
    172       break;
    173     }
    174 
    175     default:
    176       NOTREACHED();
    177   }
    178 }
    179