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