• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
UserScriptListener()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 
Initialize(ResourceQueue * resource_queue)31 void UserScriptListener::Initialize(ResourceQueue* resource_queue) {
32   resource_queue_ = resource_queue;
33 }
34 
ShouldDelayRequest(net::URLRequest * request,const ResourceDispatcherHostRequestInfo & request_info,const GlobalRequestID & request_id)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 
WillShutdownResourceQueue()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 
~UserScriptListener()73 UserScriptListener::~UserScriptListener() {
74 }
75 
StartDelayedRequests()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 
AppendNewURLPatterns(const URLPatterns & new_patterns)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 
ReplaceURLPatterns(const URLPatterns & patterns)99 void UserScriptListener::ReplaceURLPatterns(const URLPatterns& patterns) {
100   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
101   url_patterns_ = patterns;
102 }
103 
Cleanup()104 void UserScriptListener::Cleanup() {
105   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
106   registrar_.RemoveAll();
107   Release();
108 }
109 
CollectURLPatterns(const Extension * extension,URLPatterns * patterns)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 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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