diff options
author | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-06 19:37:32 +0000 |
---|---|---|
committer | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-06 19:37:32 +0000 |
commit | 64ae283398225ef515fa951e356054f95c425a23 (patch) | |
tree | f2f708b56aa9edb0d426f99007f10d36ad8e3262 /chrome | |
parent | 14f5db0de0e01922dec5b6086f970b378939aacf (diff) | |
download | chromium_src-64ae283398225ef515fa951e356054f95c425a23.zip chromium_src-64ae283398225ef515fa951e356054f95c425a23.tar.gz chromium_src-64ae283398225ef515fa951e356054f95c425a23.tar.bz2 |
Better handling disagreement between 2+ extensions to a blocking webRequest
event.
The most recently installed extension wins. I have doubts this is the best
algorithm, but it is consistent with other systems in the extension code that
have to make precedence decisions.
BUG=60101
TEST=no
Review URL: http://codereview.chromium.org/6940004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@84483 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
8 files changed, 341 insertions, 99 deletions
diff --git a/chrome/browser/extensions/extension_event_router_forwarder.h b/chrome/browser/extensions/extension_event_router_forwarder.h index 1a6f19d..6805cc8 100644 --- a/chrome/browser/extensions/extension_event_router_forwarder.h +++ b/chrome/browser/extensions/extension_event_router_forwarder.h @@ -72,6 +72,15 @@ class ExtensionEventRouterForwarder // Protected for testing. virtual ~ExtensionEventRouterForwarder(); + // Helper function for {Broadcast,Dispatch}EventTo{Extension,Renderers}. + // Virtual for testing. + virtual void HandleEvent(const std::string& extension_id, + const std::string& event_name, + const std::string& event_args, + ProfileId profile_id, + bool use_profile_to_restrict_events, + const GURL& event_url); + // Calls DispatchEventToRenderers or DispatchEventToExtension (depending on // whether extension_id == "" or not) of |profile|'s ExtensionEventRouter. // |profile| may never be NULL. @@ -86,14 +95,6 @@ class ExtensionEventRouterForwarder private: friend class base::RefCountedThreadSafe<ExtensionEventRouterForwarder>; - // Helper function for {Broadcast,Dispatch}EventTo{Extension,Renderers}. - void HandleEvent(const std::string& extension_id, - const std::string& event_name, - const std::string& event_args, - ProfileId profile_id, - bool use_profile_to_restrict_events, - const GURL& event_url); - DISALLOW_COPY_AND_ASSIGN(ExtensionEventRouterForwarder); }; diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h index d725841..e236a07 100644 --- a/chrome/browser/extensions/extension_prefs.h +++ b/chrome/browser/extensions/extension_prefs.h @@ -324,6 +324,11 @@ class ExtensionPrefs { // for |pref_key| *and* it is specific to incognito mode. bool HasIncognitoPrefValue(const std::string& pref_key); + // Helper method to acquire the installation time of an extension. + // Returns base::Time() if the installation time could not be parsed or + // found. + base::Time GetInstallTime(const std::string& extension_id) const; + static void RegisterUserPrefs(PrefService* prefs); // The underlying PrefService. @@ -412,11 +417,6 @@ class ExtensionPrefs { // This is used to decide if an extension is blacklisted. bool IsBlacklistBitSet(DictionaryValue* ext); - // Helper method to acquire the installation time of an extension. - // Returns base::Time() if the installation time could not be parsed or - // found. - base::Time GetInstallTime(const std::string& extension_id) const; - // Fix missing preference entries in the extensions that are were introduced // in a later Chrome version. void FixMissingPrefs(const ExtensionIdSet& extension_ids); diff --git a/chrome/browser/extensions/extension_webrequest_api.cc b/chrome/browser/extensions/extension_webrequest_api.cc index f6ebd81..adb574d 100644 --- a/chrome/browser/extensions/extension_webrequest_api.cc +++ b/chrome/browser/extensions/extension_webrequest_api.cc @@ -11,12 +11,13 @@ #include "base/string_number_conversions.h" #include "base/values.h" #include "chrome/browser/extensions/extension_event_router_forwarder.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_tab_id_map.h" #include "chrome/browser/extensions/extension_webrequest_api_constants.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_error_utils.h" -#include "chrome/common/extensions/extension_extent.h" #include "chrome/common/extensions/url_pattern.h" #include "chrome/common/url_constants.h" #include "content/browser/browser_thread.h" @@ -130,45 +131,14 @@ static void EventHandledOnIOThread( const std::string& event_name, const std::string& sub_event_name, uint64 request_id, - bool cancel, - const GURL& new_url, - net::HttpRequestHeaders* request_headers) { + ExtensionWebRequestEventRouter::EventResponse* response) { ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled( profile_id, extension_id, event_name, sub_event_name, request_id, - cancel, new_url, request_headers); + response); } } // namespace -// Internal representation of the webRequest.RequestFilter type, used to -// filter what network events an extension cares about. -struct ExtensionWebRequestEventRouter::RequestFilter { - ExtensionExtent urls; - std::vector<ResourceType::Type> types; - int tab_id; - int window_id; - - RequestFilter() : tab_id(-1), window_id(-1) {} - // Returns false if there was an error initializing. If it is a user error, - // an error message is provided, otherwise the error is internal (and - // unexpected). - bool InitFromValue(const DictionaryValue& value, std::string* error); -}; - -// Internal representation of the extraInfoSpec parameter on webRequest events, -// used to specify extra information to be included with network events. -struct ExtensionWebRequestEventRouter::ExtraInfoSpec { - enum Flags { - REQUEST_LINE = 1<<0, - REQUEST_HEADERS = 1<<1, - STATUS_LINE = 1<<2, - RESPONSE_HEADERS = 1<<3, - BLOCKING = 1<<4, - }; - - static bool InitFromValue(const ListValue& value, int* extra_info_spec); -}; - // Represents a single unique listener to an event, along with whatever filter // parameters and extra_info_spec were specified at the time the listener was // added. @@ -188,6 +158,8 @@ struct ExtensionWebRequestEventRouter::EventListener { return true; return false; } + + EventListener() : extra_info_spec(0) {} }; // Contains info about requests that are blocked waiting for a response from @@ -213,12 +185,17 @@ struct ExtensionWebRequestEventRouter::BlockedRequest { // Time the request was paused. Used for logging purposes. base::Time blocking_time; - BlockedRequest() : - event(kInvalidEvent), - num_handlers_blocking(0), - callback(NULL), - new_url(NULL), - request_headers(NULL) {} + // If non-NULL, this is the response we have chosen so far. Once all responses + // are received, this is the one that will be used to determine how to proceed + // with the request. + linked_ptr<ExtensionWebRequestEventRouter::EventResponse> chosen_response; + + BlockedRequest() + : event(kInvalidEvent), + num_handlers_blocking(0), + callback(NULL), + new_url(NULL), + request_headers(NULL) {} }; bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue( @@ -291,6 +268,29 @@ bool ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( return true; } + +ExtensionWebRequestEventRouter::EventResponse::EventResponse( + const std::string& extension_id, const base::Time& extension_install_time) + : extension_id(extension_id), + extension_install_time(extension_install_time), + cancel(false) { +} + +ExtensionWebRequestEventRouter::EventResponse::~EventResponse() { +} + + +ExtensionWebRequestEventRouter::RequestFilter::RequestFilter() + : tab_id(-1), window_id(-1) { +} + +ExtensionWebRequestEventRouter::RequestFilter::~RequestFilter() { +} + +// +// ExtensionWebRequestEventRouter +// + // static ExtensionWebRequestEventRouter* ExtensionWebRequestEventRouter::GetInstance() { return Singleton<ExtensionWebRequestEventRouter>::get(); @@ -669,9 +669,7 @@ void ExtensionWebRequestEventRouter::OnEventHandled( const std::string& event_name, const std::string& sub_event_name, uint64 request_id, - bool cancel, - const GURL& new_url, - net::HttpRequestHeaders* request_headers) { + EventResponse* response) { EventListener listener; listener.extension_id = extension_id; listener.sub_event_name = sub_event_name; @@ -683,7 +681,7 @@ void ExtensionWebRequestEventRouter::OnEventHandled( if (found != listeners_[profile_id][event_name].end()) found->blocked_requests.erase(request_id); - DecrementBlockCount(request_id, cancel, new_url, request_headers); + DecrementBlockCount(request_id, response); } void ExtensionWebRequestEventRouter::AddEventListener( @@ -735,7 +733,7 @@ void ExtensionWebRequestEventRouter::RemoveEventListener( // Unblock any request that this event listener may have been blocking. for (std::set<uint64>::iterator it = found->blocked_requests.begin(); it != found->blocked_requests.end(); ++it) { - DecrementBlockCount(*it, false, GURL(), NULL); + DecrementBlockCount(*it, NULL); } listeners_[profile_id][event_name].erase(listener); @@ -793,10 +791,8 @@ ExtensionWebRequestEventRouter::GetMatchingListeners( void ExtensionWebRequestEventRouter::DecrementBlockCount( uint64 request_id, - bool cancel, - const GURL& new_url, - net::HttpRequestHeaders* request_headers) { - scoped_ptr<net::HttpRequestHeaders> request_headers_scoped(request_headers); + EventResponse* response) { + scoped_ptr<EventResponse> response_scoped(response); // It's possible that this request was deleted, or cancelled by a previous // event handler. If so, ignore this response. @@ -807,11 +803,19 @@ void ExtensionWebRequestEventRouter::DecrementBlockCount( int num_handlers_blocking = --blocked_request.num_handlers_blocking; CHECK_GE(num_handlers_blocking, 0); - // TODO(mpcomplete): handle conflicts more intelligently. Possibility: wait - // until all extensions respond, and process responses in order of - // extension_id. - if (num_handlers_blocking == 0 || cancel || - !new_url.is_empty() || request_headers) { + // If |response| is NULL, then we assume the extension does not care about + // this request. In that case, we will fall back to the previous chosen + // response, if any. More recently installed extensions have greater + // precedence. + if (response) { + if (!blocked_request.chosen_response.get() || + response->extension_install_time > + blocked_request.chosen_response->extension_install_time) { + blocked_request.chosen_response.reset(response_scoped.release()); + } + } + + if (num_handlers_blocking == 0) { // TODO(mpcomplete): it would be better if we accumulated the blocking times // for a given request over all events. HISTOGRAM_TIMES("Extensions.NetworkDelay", @@ -819,16 +823,21 @@ void ExtensionWebRequestEventRouter::DecrementBlockCount( if (blocked_request.event == kOnBeforeRequest) { CHECK(blocked_request.callback); - if (!new_url.is_empty()) { - CHECK(new_url.is_valid()); - *blocked_request.new_url = new_url; + if (blocked_request.chosen_response.get() && + !blocked_request.chosen_response->new_url.is_empty()) { + CHECK(blocked_request.chosen_response->new_url.is_valid()); + *blocked_request.new_url = blocked_request.chosen_response->new_url; } } else if (blocked_request.event == kOnBeforeSendHeaders) { // It's possible that the HttpTransaction was deleted before we could call // the callback. In that case, we've already NULLed out the callback and // headers, and we just drop the response on the floor. - if (request_headers && blocked_request.request_headers) - blocked_request.request_headers->Swap(request_headers); + if (blocked_request.chosen_response.get() && + blocked_request.chosen_response->request_headers.get() && + blocked_request.request_headers) { + blocked_request.request_headers->Swap( + blocked_request.chosen_response->request_headers.get()); + } } else { NOTREACHED(); } @@ -836,8 +845,12 @@ void ExtensionWebRequestEventRouter::DecrementBlockCount( // This signals a failed request to subscribers of onErrorOccurred in case // a request is cancelled because net::ERR_EMPTY_RESPONSE cannot be // distinguished from a regular failure. - if (blocked_request.callback) - blocked_request.callback->Run(cancel ? net::ERR_EMPTY_RESPONSE : net::OK); + if (blocked_request.callback) { + int rv = (blocked_request.chosen_response.get() && + blocked_request.chosen_response->cancel) ? + net::ERR_EMPTY_RESPONSE : net::OK; + blocked_request.callback->Run(rv); + } blocked_requests_.erase(request_id); } @@ -916,22 +929,38 @@ bool WebRequestEventHandled::RunImpl() { int64 request_id; EXTENSION_FUNCTION_VALIDATE(base::StringToInt64(request_id_str, &request_id)); - bool cancel = false; - GURL new_url; - scoped_ptr<net::HttpRequestHeaders> request_headers; + scoped_ptr<ExtensionWebRequestEventRouter::EventResponse> response; if (HasOptionalArgument(3)) { DictionaryValue* value = NULL; EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(3, &value)); - if (value->HasKey("cancel")) + if (!value->empty()) { + base::Time install_time = + profile()->GetExtensionService()->extension_prefs()-> + GetInstallTime(extension_id()); + response.reset(new ExtensionWebRequestEventRouter::EventResponse( + extension_id(), install_time)); + } + + if (value->HasKey("cancel")) { + bool cancel = false; EXTENSION_FUNCTION_VALIDATE(value->GetBoolean("cancel", &cancel)); + response->cancel = cancel; + } + + // Don't allow cancel mixed with other keys. + if (response->cancel && + (value->HasKey("redirectUrl") || value->HasKey("requestHeaders"))) { + error_ = keys::kInvalidBlockingResponse; + return false; + } if (value->HasKey("redirectUrl")) { std::string new_url_str; EXTENSION_FUNCTION_VALIDATE(value->GetString("redirectUrl", &new_url_str)); - new_url = GURL(new_url_str); - if (!new_url.is_valid()) { + response->new_url = GURL(new_url_str); + if (!response->new_url.is_valid()) { error_ = ExtensionErrorUtils::FormatErrorMessage( keys::kInvalidRedirectUrl, new_url_str); return false; @@ -940,7 +969,7 @@ bool WebRequestEventHandled::RunImpl() { if (value->HasKey("requestHeaders")) { ListValue* request_headers_value = NULL; - request_headers.reset(new net::HttpRequestHeaders()); + response->request_headers.reset(new net::HttpRequestHeaders()); EXTENSION_FUNCTION_VALIDATE(value->GetList(keys::kRequestHeadersKey, &request_headers_value)); for (size_t i = 0; i < request_headers_value->GetSize(); ++i) { @@ -954,7 +983,7 @@ bool WebRequestEventHandled::RunImpl() { EXTENSION_FUNCTION_VALIDATE( header_value->GetString(keys::kHeaderValueKey, &value)); - request_headers->SetHeader(name, value); + response->request_headers->SetHeader(name, value); } } } @@ -964,8 +993,7 @@ bool WebRequestEventHandled::RunImpl() { NewRunnableFunction( &EventHandledOnIOThread, profile()->GetRuntimeId(), extension_id(), - event_name, sub_event_name, request_id, - cancel, new_url, request_headers.release())); + event_name, sub_event_name, request_id, response.release())); return true; } diff --git a/chrome/browser/extensions/extension_webrequest_api.h b/chrome/browser/extensions/extension_webrequest_api.h index a894a9e..8459ccc 100644 --- a/chrome/browser/extensions/extension_webrequest_api.h +++ b/chrome/browser/extensions/extension_webrequest_api.h @@ -12,14 +12,18 @@ #include <vector> #include "base/memory/singleton.h" +#include "base/time.h" #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/extension_extent.h" #include "ipc/ipc_message.h" #include "net/base/completion_callback.h" #include "webkit/glue/resource_type.h" +class DictionaryValue; class ExtensionEventRouterForwarder; class GURL; +class ListValue; namespace net { class HostPortPair; @@ -43,8 +47,59 @@ class ExtensionWebRequestEventRouter { kOnCompleted = 1 << 6, }; - struct RequestFilter; - struct ExtraInfoSpec; + // Internal representation of the webRequest.RequestFilter type, used to + // filter what network events an extension cares about. + struct RequestFilter { + ExtensionExtent urls; + std::vector<ResourceType::Type> types; + int tab_id; + int window_id; + + RequestFilter(); + ~RequestFilter(); + + // Returns false if there was an error initializing. If it is a user error, + // an error message is provided, otherwise the error is internal (and + // unexpected). + bool InitFromValue(const DictionaryValue& value, std::string* error); + }; + + // Internal representation of the extraInfoSpec parameter on webRequest + // events, used to specify extra information to be included with network + // events. + struct ExtraInfoSpec { + enum Flags { + REQUEST_LINE = 1<<0, + REQUEST_HEADERS = 1<<1, + STATUS_LINE = 1<<2, + RESPONSE_HEADERS = 1<<3, + BLOCKING = 1<<4, + }; + + static bool InitFromValue(const ListValue& value, int* extra_info_spec); + }; + + // Contains an extension's response to a blocking event. + struct EventResponse { + // ID of the extension that sent this response. + std::string extension_id; + + // The time that the extension was installed. Used for deciding order of + // precedence in case multiple extensions respond with conflicting + // decisions. + base::Time extension_install_time; + + // Response values. These are mutually exclusive. + bool cancel; + GURL new_url; + scoped_ptr<net::HttpRequestHeaders> request_headers; + + EventResponse(const std::string& extension_id, + const base::Time& extension_install_time); + ~EventResponse(); + + DISALLOW_COPY_AND_ASSIGN(EventResponse); + }; static ExtensionWebRequestEventRouter* GetInstance(); @@ -102,16 +157,13 @@ class ExtensionWebRequestEventRouter { void OnHttpTransactionDestroyed(ProfileId profile_id, uint64 request_id); // Called when an event listener handles a blocking event and responds. - // TODO(mpcomplete): modify request void OnEventHandled( ProfileId profile_id, const std::string& extension_id, const std::string& event_name, const std::string& sub_event_name, uint64 request_id, - bool cancel, - const GURL& new_url, - net::HttpRequestHeaders* request_headers); + EventResponse* response); // Adds a listener to the given event. |event_name| specifies the event being // listened to. |sub_event_name| is an internal event uniquely generated in @@ -172,15 +224,11 @@ class ExtensionWebRequestEventRouter { int* extra_info_spec); // Decrements the count of event handlers blocking the given request. When the - // count reaches 0 (or immediately if the request is being cancelled or - // modified headers are provided), we stop blocking the request and either - // resume or cancel it. If |request_headers| is non-NULL, this method assumes - // ownership. - void DecrementBlockCount( - uint64 request_id, - bool cancel, - const GURL& new_url, - net::HttpRequestHeaders* request_headers); + // count reaches 0, we stop blocking the request and proceed it using the + // method requested by the extension with the highest precedence. Precedence + // is decided by extension install time. If |response| is non-NULL, this + // method assumes ownership. + void DecrementBlockCount(uint64 request_id, EventResponse* response); void OnRequestDeleted(net::URLRequest* request); diff --git a/chrome/browser/extensions/extension_webrequest_api_constants.cc b/chrome/browser/extensions/extension_webrequest_api_constants.cc index 64b633d..e487ae4 100644 --- a/chrome/browser/extensions/extension_webrequest_api_constants.cc +++ b/chrome/browser/extensions/extension_webrequest_api_constants.cc @@ -30,6 +30,8 @@ const char kOnResponseStarted[] = "experimental.webRequest.onResponseStarted"; const char kOnRequestSent[] = "experimental.webRequest.onRequestSent"; const char kInvalidRedirectUrl[] = "redirectUrl '*' is not a valid URL."; +const char kInvalidBlockingResponse[] = + "cancel cannot be true in the presence of other keys."; const char kInvalidRequestFilterUrl[] = "'*' is not a valid URL pattern."; } // namespace extension_webrequest_api_constants diff --git a/chrome/browser/extensions/extension_webrequest_api_constants.h b/chrome/browser/extensions/extension_webrequest_api_constants.h index 73b8664..2621c02 100644 --- a/chrome/browser/extensions/extension_webrequest_api_constants.h +++ b/chrome/browser/extensions/extension_webrequest_api_constants.h @@ -37,6 +37,7 @@ extern const char kOnRequestSent[]; // Error messages. extern const char kInvalidRedirectUrl[]; +extern const char kInvalidBlockingResponse[]; extern const char kInvalidRequestFilterUrl[]; } // namespace extension_webrequest_api_constants diff --git a/chrome/browser/extensions/extension_webrequest_api_unittest.cc b/chrome/browser/extensions/extension_webrequest_api_unittest.cc new file mode 100644 index 0000000..5207079 --- /dev/null +++ b/chrome/browser/extensions/extension_webrequest_api_unittest.cc @@ -0,0 +1,161 @@ +// Copyright (c) 2011 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 <queue> + +#include "base/file_util.h" +#include "base/path_service.h" + +#include "chrome/browser/extensions/extension_event_router_forwarder.h" +#include "chrome/browser/extensions/extension_webrequest_api.h" +#include "chrome/browser/extensions/extension_webrequest_api_constants.h" +#include "chrome/browser/net/chrome_network_delegate.h" +#include "chrome/browser/prefs/pref_member.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/testing_pref_service.h" +#include "chrome/test/testing_profile.h" +#include "net/base/net_util.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace keys = extension_webrequest_api_constants; + +namespace { +static void EventHandledOnIOThread( + ProfileId profile_id, + const std::string& extension_id, + const std::string& event_name, + const std::string& sub_event_name, + uint64 request_id, + ExtensionWebRequestEventRouter::EventResponse* response) { + ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled( + profile_id, extension_id, event_name, sub_event_name, request_id, + response); +} +} // namespace + +// A mock event router that responds to events with a pre-arranged queue of +// Tasks. +class TestEventRouter : public ExtensionEventRouterForwarder { +public: + // Adds a Task to the queue. We will fire these in order as events are + // dispatched. + void PushTask(Task* task) { + task_queue_.push(task); + } + + size_t GetNumTasks() { return task_queue_.size(); } + +private: + // ExtensionEventRouterForwarder: + virtual void HandleEvent(const std::string& extension_id, + const std::string& event_name, + const std::string& event_args, + ProfileId profile_id, + bool use_profile_to_restrict_events, + const GURL& event_url) { + ASSERT_FALSE(task_queue_.empty()); + MessageLoop::current()->PostTask(FROM_HERE, task_queue_.front()); + task_queue_.pop(); + } + + std::queue<Task*> task_queue_; +}; + +class ExtensionWebRequestTest : public testing::Test { +protected: + virtual void SetUp() { + event_router_ = new TestEventRouter(); + enable_referrers_.Init( + prefs::kEnableReferrers, profile_.GetTestingPrefService(), NULL); + network_delegate_.reset(new ChromeNetworkDelegate( + event_router_.get(), profile_.GetRuntimeId(), + &enable_referrers_, NULL)); + context_ = new TestURLRequestContext(); + context_->set_network_delegate(network_delegate_.get()); + } + + MessageLoopForIO io_loop_; + TestingProfile profile_; + TestDelegate delegate_; + BooleanPrefMember enable_referrers_; + scoped_refptr<TestEventRouter> event_router_; + scoped_ptr<ChromeNetworkDelegate> network_delegate_; + scoped_refptr<TestURLRequestContext> context_; +}; + +// Tests that we handle disagreements among extensions about responses to +// blocking events by choosing the response from the most-recently-installed +// extension. +TEST_F(ExtensionWebRequestTest, BlockingEventPrecedence) { + std::string extension1_id("1"); + std::string extension2_id("2"); + ExtensionWebRequestEventRouter::RequestFilter filter; + const std::string kEventName(keys::kOnBeforeRequest); + ExtensionWebRequestEventRouter::GetInstance()->AddEventListener( + profile_.GetRuntimeId(), extension1_id, kEventName, + kEventName + "/1", filter, + ExtensionWebRequestEventRouter::ExtraInfoSpec::BLOCKING); + ExtensionWebRequestEventRouter::GetInstance()->AddEventListener( + profile_.GetRuntimeId(), extension2_id, kEventName, + kEventName + "/2", filter, + ExtensionWebRequestEventRouter::ExtraInfoSpec::BLOCKING); + + net::URLRequest request(GURL("about:blank"), &delegate_); + request.set_context(context_); + + { + // onBeforeRequest will be dispatched twice initially. The second response - + // the redirect - should win, since it has a later |install_time|. The + // redirect will dispatch another pair of onBeforeRequest. There, the first + // response should win (later |install_time|). + GURL redirect_url("about:redirected"); + ExtensionWebRequestEventRouter::EventResponse* response = NULL; + + // Extension1 response. Arrives first, but ignored due to install_time. + response = new ExtensionWebRequestEventRouter::EventResponse( + extension1_id, base::Time::FromDoubleT(1)); + response->cancel = true; + event_router_->PushTask( + NewRunnableFunction(&EventHandledOnIOThread, + profile_.GetRuntimeId(), extension1_id, + kEventName, kEventName + "/1", request.identifier(), response)); + + // Extension2 response. Arrives second, and chosen because of install_time. + response = new ExtensionWebRequestEventRouter::EventResponse( + extension2_id, base::Time::FromDoubleT(2)); + response->new_url = redirect_url; + event_router_->PushTask( + NewRunnableFunction(&EventHandledOnIOThread, + profile_.GetRuntimeId(), extension2_id, + kEventName, kEventName + "/2", request.identifier(), response)); + + // Extension2 response to the redirected URL. Arrives first, and chosen. + response = new ExtensionWebRequestEventRouter::EventResponse( + extension2_id, base::Time::FromDoubleT(2)); + event_router_->PushTask( + NewRunnableFunction(&EventHandledOnIOThread, + profile_.GetRuntimeId(), extension2_id, + kEventName, kEventName + "/2", request.identifier(), response)); + + // Extension1 response to the redirected URL. Arrives second, and ignored. + response = new ExtensionWebRequestEventRouter::EventResponse( + extension1_id, base::Time::FromDoubleT(1)); + response->cancel = true; + event_router_->PushTask( + NewRunnableFunction(&EventHandledOnIOThread, + profile_.GetRuntimeId(), extension1_id, + kEventName, kEventName + "/1", request.identifier(), response)); + + request.Start(); + MessageLoop::current()->Run(); + + EXPECT_TRUE(!request.is_pending()); + EXPECT_EQ(net::URLRequestStatus::SUCCESS, request.status().status()); + EXPECT_EQ(0, request.status().os_error()); + EXPECT_EQ(redirect_url, request.url()); + EXPECT_EQ(2U, request.url_chain().size()); + EXPECT_EQ(0U, event_router_->GetNumTasks()); + } +} diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index def1e43..914f173 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1336,6 +1336,7 @@ 'browser/extensions/extension_ui_unittest.cc', 'browser/extensions/extension_updater_unittest.cc', 'browser/extensions/extension_webnavigation_unittest.cc', + 'browser/extensions/extension_webrequest_api_unittest.cc', 'browser/extensions/extensions_quota_service_unittest.cc', 'browser/extensions/external_policy_extension_loader_unittest.cc', 'browser/extensions/file_reader_unittest.cc', |