Index: chrome/browser/extensions/user_script_listener.cc |
diff --git a/chrome/browser/extensions/user_script_listener.cc b/chrome/browser/extensions/user_script_listener.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..94b58bf0da897f32227e5d39812515519706b4a7 |
--- /dev/null |
+++ b/chrome/browser/extensions/user_script_listener.cc |
@@ -0,0 +1,166 @@ |
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/extensions/user_script_listener.h" |
+ |
+#include "base/message_loop.h" |
+#include "chrome/browser/extensions/extensions_service.h" |
+#include "chrome/common/extensions/extension.h" |
+#include "chrome/common/notification_service.h" |
+#include "net/url_request/url_request.h" |
+ |
+UserScriptListener::UserScriptListener(MessageLoop* ui_loop, |
+ MessageLoop* io_loop, |
+ ResourceDispatcherHost* rdh) |
+ : ui_loop_(ui_loop), |
+ io_loop_(io_loop), |
+ resource_dispatcher_host_(rdh), |
+ user_scripts_ready_(false) { |
+ DCHECK(ui_loop_); |
+ DCHECK_EQ(ui_loop_, MessageLoop::current()); |
+ DCHECK(resource_dispatcher_host_); |
+ |
+ // IO loop can be NULL in unit tests. |
+ if (!io_loop_) |
+ io_loop_ = ui_loop; |
+ |
+ registrar_.Add(this, NotificationType::EXTENSION_LOADED, |
+ NotificationService::AllSources()); |
+ registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, |
+ NotificationService::AllSources()); |
+ registrar_.Add(this, NotificationType::USER_SCRIPTS_UPDATED, |
+ NotificationService::AllSources()); |
+} |
+ |
+bool UserScriptListener::ShouldStartRequest(URLRequest* request) { |
+ DCHECK_EQ(io_loop_, MessageLoop::current()); |
+ |
+ // If it's a frame load, then we need to check the URL against the list of |
+ // user scripts to see if we need to wait. |
+ ResourceDispatcherHost::ExtraRequestInfo* info = |
+ ResourceDispatcherHost::ExtraInfoForRequest(request); |
+ DCHECK(info); |
+ |
+ if (info->resource_type != ResourceType::MAIN_FRAME && |
+ info->resource_type != ResourceType::SUB_FRAME) { |
+ return true; |
+ } |
+ |
+ if (user_scripts_ready_) |
+ return true; |
+ |
+ // User scripts aren't ready yet. If one of them wants to inject into this |
+ // request, we'll need to wait for it before we can start this request. |
+ bool found_match = false; |
+ for (URLPatterns::iterator it = url_patterns_.begin(); |
+ it != url_patterns_.end(); ++it) { |
+ if ((*it).MatchesUrl(request->url())) { |
+ found_match = true; |
+ break; |
+ } |
+ } |
+ |
+ if (!found_match) |
+ return true; |
+ |
+ // Queue this request up. |
+ delayed_request_ids_.push_front(ResourceDispatcherHost::GlobalRequestID( |
+ info->process_id, info->request_id)); |
+ return false; |
+} |
+ |
+void UserScriptListener::StartDelayedRequests() { |
+ DCHECK_EQ(io_loop_, MessageLoop::current()); |
+ |
+ user_scripts_ready_ = true; |
+ |
+ if (resource_dispatcher_host_) { |
+ for (DelayedRequests::iterator it = delayed_request_ids_.begin(); |
+ it != delayed_request_ids_.end(); ++it) { |
+ URLRequest* request = resource_dispatcher_host_->GetURLRequest(*it); |
+ if (request) { |
+ // The request shouldn't have started (SUCCESS is the initial state). |
+ DCHECK(request->status().status() == URLRequestStatus::SUCCESS); |
+ request->Start(); |
+ } |
+ } |
+ } |
+ |
+ delayed_request_ids_.clear(); |
+} |
+ |
+void UserScriptListener::AppendNewURLPatterns(const URLPatterns& new_patterns) { |
+ DCHECK_EQ(io_loop_, MessageLoop::current()); |
+ |
+ user_scripts_ready_ = false; |
+ url_patterns_.insert(url_patterns_.end(), |
+ new_patterns.begin(), new_patterns.end()); |
+} |
+ |
+void UserScriptListener::ReplaceURLPatterns(const URLPatterns& patterns) { |
+ DCHECK_EQ(io_loop_, MessageLoop::current()); |
+ url_patterns_ = patterns; |
+} |
+ |
+void UserScriptListener::CollectURLPatterns(Extension* extension, |
+ URLPatterns* patterns) { |
+ DCHECK_EQ(ui_loop_, MessageLoop::current()); |
+ |
+ const UserScriptList& scripts = extension->content_scripts(); |
+ for (UserScriptList::const_iterator iter = scripts.begin(); |
+ iter != scripts.end(); ++iter) { |
+ patterns->insert(patterns->end(), |
+ (*iter).url_patterns().begin(), |
+ (*iter).url_patterns().end()); |
+ } |
+} |
+ |
+void UserScriptListener::Observe(NotificationType type, |
+ const NotificationSource& source, |
+ const NotificationDetails& details) { |
+ DCHECK_EQ(ui_loop_, MessageLoop::current()); |
+ |
+ switch (type.value) { |
+ case NotificationType::EXTENSION_LOADED: { |
+ Extension* extension = Details<Extension>(details).ptr(); |
+ if (extension->content_scripts().empty()) |
+ return; // no new patterns from this extension. |
+ |
+ URLPatterns new_patterns; |
+ CollectURLPatterns(Details<Extension>(details).ptr(), &new_patterns); |
+ if (!new_patterns.empty()) { |
+ io_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, |
+ &UserScriptListener::AppendNewURLPatterns, new_patterns)); |
+ } |
+ break; |
+ } |
+ |
+ case NotificationType::EXTENSION_UNLOADED: { |
+ Extension* unloaded_extension = Details<Extension>(details).ptr(); |
+ if (unloaded_extension->content_scripts().empty()) |
+ return; // no patterns to delete for this extension. |
+ |
+ // Clear all our patterns and reregister all the still-loaded extensions. |
+ URLPatterns new_patterns; |
+ ExtensionsService* service = Source<ExtensionsService>(source).ptr(); |
+ for (ExtensionList::const_iterator it = service->extensions()->begin(); |
+ it != service->extensions()->end(); ++it) { |
+ if (*it != unloaded_extension) |
+ CollectURLPatterns(*it, &new_patterns); |
+ } |
+ io_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, |
+ &UserScriptListener::ReplaceURLPatterns, new_patterns)); |
+ break; |
+ } |
+ |
+ case NotificationType::USER_SCRIPTS_UPDATED: { |
+ io_loop_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &UserScriptListener::StartDelayedRequests)); |
+ break; |
+ } |
+ |
+ default: |
+ NOTREACHED(); |
+ } |
+} |