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