diff options
author | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-25 23:45:58 +0000 |
---|---|---|
committer | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-25 23:45:58 +0000 |
commit | eaee0069eb73370de3c540096ef88632da15ff49 (patch) | |
tree | d9b331cf4d566c792b3ffa765909abdb30bc6139 | |
parent | 75b44e4c2e995aa14c918ebe12ae74621b40d864 (diff) | |
download | chromium_src-eaee0069eb73370de3c540096ef88632da15ff49.zip chromium_src-eaee0069eb73370de3c540096ef88632da15ff49.tar.gz chromium_src-eaee0069eb73370de3c540096ef88632da15ff49.tar.bz2 |
Delay request loading until all user scripts that request depends on are ready.
BUG=11547
TEST=See bug.
Review URL: http://codereview.chromium.org/173239
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24372 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 1 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_listener.cc | 166 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_listener.h | 90 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_listener_unittest.cc | 306 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_master.h | 4 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_dispatcher_host.cc | 10 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_dispatcher_host.h | 3 | ||||
-rw-r--r-- | chrome/chrome.gyp | 3 |
8 files changed, 580 insertions, 3 deletions
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 377423f..9b19ba9 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -585,7 +585,6 @@ void ExtensionsServiceBackend::LoadSingleExtension( ReportExtensionLoaded(extension); } - void ExtensionsServiceBackend::ReportExtensionLoadError( const FilePath& extension_path, const std::string &error) { ::ReportExtensionLoadError(extension_path, error, alert_on_error_); diff --git a/chrome/browser/extensions/user_script_listener.cc b/chrome/browser/extensions/user_script_listener.cc new file mode 100644 index 0000000..94b58bf --- /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(); + } +} diff --git a/chrome/browser/extensions/user_script_listener.h b/chrome/browser/extensions/user_script_listener.h new file mode 100644 index 0000000..a742d3a --- /dev/null +++ b/chrome/browser/extensions/user_script_listener.h @@ -0,0 +1,90 @@ +// 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_LISTENER_H_ +#define CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_LISTENER_H_ + +#include <list> + +#include "base/ref_counted.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" +#include "chrome/common/extensions/url_pattern.h" +#include "chrome/common/notification_registrar.h" + +class Extension; +class MessageLoop; +class URLRequest; + +// This class handles delaying of resource loads that depend on unloaded user +// scripts. For each request that comes in, we check if it depends on a user +// script, and if so, whether that user script is ready; if not, we delay the +// request. +// +// This class lives mostly on the IO thread. It listens on the UI thread for +// updates to loaded extensions. +class UserScriptListener + : public base::RefCountedThreadSafe<UserScriptListener>, + public NotificationObserver { + public: + UserScriptListener(MessageLoop* ui_loop, + MessageLoop* io_loop, + ResourceDispatcherHost* rdh); + + void OnResourceDispatcherHostGone() { resource_dispatcher_host_ = NULL; } + + // Returns true if we're ready to service the request. Otherwise, if the + // request URL depends on any user scripts that haven't been loaded yet, we + // will delay the request until we're ready. + bool ShouldStartRequest(URLRequest* request); + + private: + typedef std::list<URLPattern> URLPatterns; + + // Resume any requests that we delayed in order to wait for user scripts. + void StartDelayedRequests(); + + // Appends new url patterns to our list, also setting user_scripts_ready_ + // to false. + void AppendNewURLPatterns(const URLPatterns& new_patterns); + + // Replaces our url pattern list. This is only used when patterns have been + // deleted, so user_scripts_ready_ remains unchanged. + void ReplaceURLPatterns(const URLPatterns& patterns); + + MessageLoop* ui_loop_; + MessageLoop* io_loop_; + ResourceDispatcherHost* resource_dispatcher_host_; + + // A list of every request that we delayed. Will be flushed when user scripts + // are ready. + typedef std::list<ResourceDispatcherHost::GlobalRequestID> DelayedRequests; + DelayedRequests delayed_request_ids_; + + // TODO(mpcomplete): the rest of this stuff should really be per-profile, but + // the complexity doesn't seem worth it at this point. + + // True if the user scripts contained in |url_patterns_| are ready for + // injection. + bool user_scripts_ready_; + + // A list of URL patterns that have will have user scripts applied to them. + URLPatterns url_patterns_; + + // --- UI thread: + + // Helper to collect the extension's user script URL patterns in a list and + // return it. + void CollectURLPatterns(Extension* extension, URLPatterns* patterns); + + // NotificationObserver + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + NotificationRegistrar registrar_; + + DISALLOW_EVIL_CONSTRUCTORS(UserScriptListener); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_LISTENER_H_ diff --git a/chrome/browser/extensions/user_script_listener_unittest.cc b/chrome/browser/extensions/user_script_listener_unittest.cc new file mode 100644 index 0000000..db49886 --- /dev/null +++ b/chrome/browser/extensions/user_script_listener_unittest.cc @@ -0,0 +1,306 @@ +// 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 <algorithm> +#include <vector> + +#include "base/message_loop.h" +#include "base/thread.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_plugin_lib.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/render_messages.h" +#include "chrome/test/testing_profile.h" +#include "ipc/ipc_message.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_test_job.h" +#include "testing/gtest/include/gtest/gtest.h" + +// A simple test URLRequestJob. We don't care what it does, only that whether it +// starts and finishes. +class SimpleTestJob : public URLRequestTestJob { + public: + explicit SimpleTestJob(URLRequest* request) + : URLRequestTestJob(request, test_headers(), test_data_1(), true) {} +}; + +class MockUserScriptMaster : public UserScriptMaster { +public: + MockUserScriptMaster(MessageLoop* worker, const FilePath& script_dir) : + UserScriptMaster(worker, script_dir) { } + + virtual void StartScan() { + // Do nothing. We want to manually control when scans occur. + } + void TestStartScan() { + UserScriptMaster::StartScan(); + } +}; + +class MockIOThread : public base::Thread { + public: + MockIOThread() : base::Thread("IO") {} + virtual ~MockIOThread() { Stop(); } + + private: + virtual void Init() { + service_.reset(new NotificationService()); + } + virtual void CleanUp() { + ChromePluginLib::UnloadAllPlugins(); + service_.reset(); + } + + scoped_ptr<NotificationService> service_; +}; + +// A class to help with making and handling resource requests. +class ResourceDispatcherHostTester : + public ResourceDispatcherHost::Receiver, + public URLRequest::Interceptor, + public base::RefCountedThreadSafe<ResourceDispatcherHostTester> { + public: + ResourceDispatcherHostTester(MessageLoop* io_loop) + : Receiver(ChildProcessInfo::RENDER_PROCESS), + host_(io_loop), + ui_loop_(MessageLoop::current()), + io_loop_(io_loop) { + URLRequest::RegisterRequestInterceptor(this); + } + virtual ~ResourceDispatcherHostTester() { + URLRequest::UnregisterRequestInterceptor(this); + } + + void MakeTestRequest(int request_id, const GURL& url) { + io_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &ResourceDispatcherHostTester::MakeTestRequestOnIOThread, + request_id, url)); + MessageLoop::current()->Run(); // wait for Quit from IO thread + } + + void WaitForScan(MockUserScriptMaster* master) { + master->TestStartScan(); + MessageLoop::current()->RunAllPending(); // run the scan + io_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &ResourceDispatcherHostTester::RunPending)); + MessageLoop::current()->Run(); // wait for Quit from IO thread + } + + bool IsRequestStarted(int request_id) { + return std::find(started_requests_.begin(), started_requests_.end(), + request_id) != started_requests_.end(); + } + + bool IsRequestComplete(int request_id) { + return std::find(completed_requests_.begin(), completed_requests_.end(), + request_id) != completed_requests_.end(); + } + + private: + // URLRequest::Interceptor + virtual URLRequestJob* MaybeIntercept(URLRequest* request) { + return new SimpleTestJob(request); + } + + // ResourceDispatcherHost::Receiver implementation + virtual bool Send(IPC::Message* msg) { + IPC_BEGIN_MESSAGE_MAP(ResourceDispatcherHostTester, *msg) + IPC_MESSAGE_HANDLER(ViewMsg_Resource_ReceivedResponse, OnReceivedResponse) + IPC_MESSAGE_HANDLER(ViewMsg_Resource_RequestComplete, OnRequestComplete) + IPC_END_MESSAGE_MAP() + delete msg; + return true; + } + + URLRequestContext* GetRequestContext( + uint32 request_id, + const ViewHostMsg_Resource_Request& request_data) { + return NULL; + } + + // TODO(mpcomplete): Some of this stuff is copied from + // resource_dispatcher_host_unittest.cc. Consider sharing. + static ViewHostMsg_Resource_Request CreateResourceRequest(const char* method, + const GURL& url) { + ViewHostMsg_Resource_Request request; + request.method = std::string(method); + request.url = url; + request.first_party_for_cookies = url; // bypass third-party cookie + // blocking + request.resource_type = ResourceType::MAIN_FRAME; + request.load_flags = 0; + // init the rest to default values to prevent getting UMR. + request.frame_origin = "null"; + request.main_frame_origin = "null"; + request.origin_pid = 0; + request.request_context = 0; + return request; + } + + void RunPending() { + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->RunAllPending(); + MessageLoop::current()->SetNestableTasksAllowed(false); + + // return control to UI thread. + ui_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + + void MakeTestRequestOnIOThread(int request_id, const GURL& url) { + ViewHostMsg_Resource_Request request = CreateResourceRequest("GET", url); + ViewHostMsg_RequestResource msg(0, request_id, request); + bool msg_was_ok; + host_.OnMessageReceived(msg, this, &msg_was_ok); + RunPending(); + } + + void OnReceivedResponse(int request_id, + const ResourceResponseHead& response_head) { + started_requests_.push_back(request_id); + } + + void OnRequestComplete(int request_id, + const URLRequestStatus& status, + const std::string& security_info) { + completed_requests_.push_back(request_id); + } + + ResourceDispatcherHost host_; + MessageLoop* ui_loop_; + MessageLoop* io_loop_; + + // Note: these variables are accessed on both threads, but since we only + // one thread executes at a time, they are safe. + std::vector<int> started_requests_; + std::vector<int> completed_requests_; +}; + +class UserScriptListenerTest : public testing::Test { + public: + virtual void SetUp() { + io_thread_.reset(new MockIOThread()); + base::Thread::Options options(MessageLoop::TYPE_IO, 0); + io_thread_->StartWithOptions(options); + DCHECK(io_thread_->message_loop()); + DCHECK(io_thread_->IsRunning()); + + FilePath install_dir = profile_.GetPath() + .AppendASCII(ExtensionsService::kInstallDirectoryName); + + resource_tester_ = + new ResourceDispatcherHostTester(io_thread_->message_loop()); + + master_ = new MockUserScriptMaster(&loop_, install_dir); + + service_ = new ExtensionsService(&profile_, + CommandLine::ForCurrentProcess(), + profile_.GetPrefs(), + install_dir, + &loop_, + &loop_, + false); + service_->set_extensions_enabled(true); + service_->set_show_extensions_prompts(false); + service_->ClearProvidersForTesting(); + service_->Init(); + } + + virtual void TearDown() { + io_thread_.reset(); + resource_tester_ = NULL; + } + + protected: + TestingProfile profile_; + MessageLoopForUI loop_; + scoped_ptr<MockIOThread> io_thread_; + scoped_refptr<ResourceDispatcherHostTester> resource_tester_; + scoped_refptr<MockUserScriptMaster> master_; + scoped_refptr<ExtensionsService> service_; +}; + +// Loads a single extension and ensures that requests to URLs with content +// scripts are delayed. +TEST_F(UserScriptListenerTest, SingleExtension) { + FilePath extensions_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path)); + extensions_path = extensions_path.AppendASCII("extensions"); + FilePath ext1 = extensions_path + .AppendASCII("good") + .AppendASCII("Extensions") + .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") + .AppendASCII("1.0.0.0"); + service_->LoadExtension(ext1); + loop_.RunAllPending(); + ASSERT_EQ(1u, service_->extensions()->size()); + + // Our extension has a content script on google.com. That request should be + // delayed. + resource_tester_->MakeTestRequest(0, GURL("http://google.com/")); + resource_tester_->MakeTestRequest(1, GURL("http://yahoo.com/")); + + EXPECT_FALSE(resource_tester_->IsRequestStarted(0)); + EXPECT_TRUE(resource_tester_->IsRequestStarted(1)); + EXPECT_TRUE(resource_tester_->IsRequestComplete(1)); + + // After scanning, the user scripts should be ready and the request can + // go through. + resource_tester_->WaitForScan(master_.get()); + + EXPECT_TRUE(resource_tester_->IsRequestStarted(0)); + EXPECT_TRUE(resource_tester_->IsRequestComplete(0)); +} + +// Loads a single extension and ensures that requests to URLs with content +// scripts are delayed. +TEST_F(UserScriptListenerTest, UnloadExtension) { + FilePath extensions_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path)); + extensions_path = extensions_path.AppendASCII("extensions"); + + FilePath ext1 = extensions_path + .AppendASCII("good") + .AppendASCII("Extensions") + .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") + .AppendASCII("1.0.0.0"); + service_->LoadExtension(ext1); + loop_.RunAllPending(); + ASSERT_EQ(1u, service_->extensions()->size()); + + FilePath ext2 = extensions_path + .AppendASCII("good") + .AppendASCII("Extensions") + .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa") + .AppendASCII("1.0"); + service_->LoadExtension(ext2); + loop_.RunAllPending(); + ASSERT_EQ(2u, service_->extensions()->size()); + + // Our extension has a content script on google.com. That request should be + // delayed. + resource_tester_->MakeTestRequest(0, GURL("http://google.com/")); + resource_tester_->MakeTestRequest(1, GURL("http://yahoo.com/")); + + EXPECT_FALSE(resource_tester_->IsRequestStarted(0)); + EXPECT_TRUE(resource_tester_->IsRequestStarted(1)); + EXPECT_TRUE(resource_tester_->IsRequestComplete(1)); + + // Unload the first extension and run a scan. Request should complete. + service_->UnloadExtension("behllobkkfkfnphdnhnkndlbkcpglgmj"); + resource_tester_->WaitForScan(master_.get()); + + EXPECT_TRUE(resource_tester_->IsRequestStarted(0)); + EXPECT_TRUE(resource_tester_->IsRequestComplete(0)); + + // Make the same requests, and they should complete instantly. + resource_tester_->MakeTestRequest(2, GURL("http://google.com/")); + resource_tester_->MakeTestRequest(3, GURL("http://yahoo.com/")); + + EXPECT_TRUE(resource_tester_->IsRequestStarted(2)); + EXPECT_TRUE(resource_tester_->IsRequestComplete(2)); + EXPECT_TRUE(resource_tester_->IsRequestStarted(3)); + EXPECT_TRUE(resource_tester_->IsRequestComplete(3)); +} diff --git a/chrome/browser/extensions/user_script_master.h b/chrome/browser/extensions/user_script_master.h index 557032e..56aab84 100644 --- a/chrome/browser/extensions/user_script_master.h +++ b/chrome/browser/extensions/user_script_master.h @@ -28,7 +28,7 @@ class UserScriptMaster : public base::RefCounted<UserScriptMaster>, // script-reloading worker on as well as the path the scripts live in. // These are normally the file thread and a directory inside the profile. UserScriptMaster(MessageLoop* worker, const FilePath& script_dir); - ~UserScriptMaster(); + virtual ~UserScriptMaster(); // Add a watched directory. All scripts will be reloaded when any file in // this directory changes. @@ -36,7 +36,7 @@ class UserScriptMaster : public base::RefCounted<UserScriptMaster>, // Kicks off a process on the file thread to reload scripts from disk // into a new chunk of shared memory and notify renderers. - void StartScan(); + virtual void StartScan(); // Gets the segment of shared memory for the scripts. base::SharedMemory* GetSharedMemory() const { diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index 0066ad6..b205fd0 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -21,6 +21,7 @@ #include "chrome/browser/download/download_manager.h" #include "chrome/browser/download/download_request_manager.h" #include "chrome/browser/download/save_file_manager.h" +#include "chrome/browser/extensions/user_script_listener.h" #include "chrome/browser/external_protocol_handler.h" #include "chrome/browser/in_process_webkit/webkit_thread.h" #include "chrome/browser/login_prompt.h" @@ -252,6 +253,8 @@ ResourceDispatcherHost::ResourceDispatcherHost(MessageLoop* io_loop) download_request_manager_(new DownloadRequestManager(io_loop, ui_loop_)), ALLOW_THIS_IN_INITIALIZER_LIST( save_file_manager_(new SaveFileManager(ui_loop_, io_loop, this))), + ALLOW_THIS_IN_INITIALIZER_LIST(user_script_listener_( + new UserScriptListener(ui_loop_, io_loop, this))), safe_browsing_(new SafeBrowsingService), webkit_thread_(new WebKitThread), request_id_(-1), @@ -282,6 +285,8 @@ ResourceDispatcherHost::~ResourceDispatcherHost() { iter != ids.end(); ++iter) { CancelBlockedRequestsForRoute(iter->first, iter->second); } + + user_script_listener_->OnResourceDispatcherHostGone(); } void ResourceDispatcherHost::Initialize() { @@ -1247,6 +1252,11 @@ void ResourceDispatcherHost::BeginRequestInternal(URLRequest* request) { // its content should be filtered) and start it itself. return; } + if (!user_script_listener_->ShouldStartRequest(request)) { + // This request depends on some user scripts that haven't loaded yet. The + // UserScriptListener will resume the request when they're ready. + return; + } request->Start(); // Make sure we have the load state monitor running diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.h b/chrome/browser/renderer_host/resource_dispatcher_host.h index f7dc92e..5fff4b9 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.h +++ b/chrome/browser/renderer_host/resource_dispatcher_host.h @@ -37,6 +37,7 @@ class PluginService; class SafeBrowsingService; class SaveFileManager; class SSLClientAuthHandler; +class UserScriptListener; class URLRequestContext; class WebKitThread; struct ViewHostMsg_Resource_Request; @@ -537,6 +538,8 @@ class ResourceDispatcherHost : public URLRequest::Delegate { // We own the save file manager. scoped_refptr<SaveFileManager> save_file_manager_; + scoped_refptr<UserScriptListener> user_script_listener_; + scoped_refptr<SafeBrowsingService> safe_browsing_; // We own the WebKit thread and see to its destruction. diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index c53efdc..6fc8676 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1116,6 +1116,8 @@ 'browser/extensions/sandboxed_extension_unpacker.h', 'browser/extensions/theme_preview_infobar_delegate.cc', 'browser/extensions/theme_preview_infobar_delegate.h', + 'browser/extensions/user_script_listener.cc', + 'browser/extensions/user_script_listener.h', 'browser/extensions/user_script_master.cc', 'browser/extensions/user_script_master.h', 'browser/external_protocol_handler.cc', @@ -3970,6 +3972,7 @@ 'browser/extensions/extension_ui_unittest.cc', 'browser/extensions/extension_updater_unittest.cc', 'browser/extensions/extensions_service_unittest.cc', + 'browser/extensions/user_script_listener_unittest.cc', 'browser/extensions/user_script_master_unittest.cc', 'browser/find_backend_unittest.cc', 'browser/first_run_migration_mac_unittest.mm', |