diff options
author | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-08 21:29:48 +0000 |
---|---|---|
committer | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-08 21:29:48 +0000 |
commit | 05cc4e799b897a440333d3dbe6d18b904a49759f (patch) | |
tree | 28d5c1a1aed5c8d0a9648c7e19e595c96c49c0db | |
parent | b10392939246b06c48eb5debc961839591ebe72a (diff) | |
download | chromium_src-05cc4e799b897a440333d3dbe6d18b904a49759f.zip chromium_src-05cc4e799b897a440333d3dbe6d18b904a49759f.tar.gz chromium_src-05cc4e799b897a440333d3dbe6d18b904a49759f.tar.bz2 |
Implement blocking for webRequest.onBeforeRequest extension event.
I did some measurements with a Release build of chrome, both manually and via the page cycler tests. It seems that a simple empty blocking event listener can add anywhere from a 1 to 30ms delay to request times, largely depending on how many requests are in the queue (when many requests come at once, the last ones to be processed by the extension are delayed the longest).
From page cycler data (on my local machine), the average increase in page load time seems to be around 6ms. This is independent of total page load time (which makes sense).
BUG=60101
TEST=covered by apitests
Review URL: http://codereview.chromium.org/6574049
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77339 0039d316-1c4b-4281-b951-d872f2087c98
39 files changed, 631 insertions, 377 deletions
diff --git a/chrome/browser/extensions/extension_event_router.cc b/chrome/browser/extensions/extension_event_router.cc index 1b170e0..fc8bd87 100644 --- a/chrome/browser/extensions/extension_event_router.cc +++ b/chrome/browser/extensions/extension_event_router.cc @@ -34,6 +34,14 @@ static void DispatchEvent(RenderProcessHost* renderer, extension_id, kDispatchEvent, args, event_url)); } +static void NotifyEventListenerRemovedOnIOThread( + ProfileId profile_id, + const std::string& extension_id, + const std::string& sub_event_name) { + ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener( + profile_id, extension_id, sub_event_name); +} + } // namespace struct ExtensionEventRouter::EventListener { @@ -120,8 +128,11 @@ void ExtensionEventRouter::RemoveEventListener( if (event_name.compare(extension_processes_api_constants::kOnUpdated) == 0) ExtensionProcessesEventRouter::GetInstance()->ListenerRemoved(); - ExtensionWebRequestEventRouter::RemoveEventListenerOnUIThread( - listener.extension_id, event_name); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableFunction( + &NotifyEventListenerRemovedOnIOThread, + profile_->GetRuntimeId(), listener.extension_id, event_name)); } bool ExtensionEventRouter::HasEventListener(const std::string& event_name) { diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index 11abc26..4fb7af3 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -298,6 +298,7 @@ void FactoryRegistry::ResetFunctions() { // WebRequest. RegisterFunction<WebRequestAddEventListener>(); + RegisterFunction<WebRequestEventHandled>(); // Preferences. RegisterFunction<GetPreferenceFunction>(); diff --git a/chrome/browser/extensions/extension_info_map.cc b/chrome/browser/extensions/extension_info_map.cc index 254e7ec..e7a4b09 100644 --- a/chrome/browser/extensions/extension_info_map.cc +++ b/chrome/browser/extensions/extension_info_map.cc @@ -25,10 +25,6 @@ ExtensionInfoMap::~ExtensionInfoMap() { void ExtensionInfoMap::AddExtension(const Extension* extension) { CheckOnValidThread(); extension_info_[extension->id()] = extension; - - // Our map has already added a reference. Balance the reference given at the - // call-site. - extension->Release(); } void ExtensionInfoMap::RemoveExtension(const std::string& id) { diff --git a/chrome/browser/extensions/extension_info_map_unittest.cc b/chrome/browser/extensions/extension_info_map_unittest.cc index 7d82dd2..dd814d1 100644 --- a/chrome/browser/extensions/extension_info_map_unittest.cc +++ b/chrome/browser/extensions/extension_info_map_unittest.cc @@ -83,14 +83,9 @@ TEST_F(ExtensionInfoMapTest, RefCounting) { EXPECT_TRUE(extension2->HasOneRef()); EXPECT_TRUE(extension3->HasOneRef()); - // Add a ref to each extension and give it to the info map. The info map - // expects the caller to add a ref for it, but then assumes ownership of that - // reference. - extension1->AddRef(); + // Add a ref to each extension and give it to the info map. info_map->AddExtension(extension1); - extension2->AddRef(); info_map->AddExtension(extension2); - extension3->AddRef(); info_map->AddExtension(extension3); // Release extension1, and the info map should have the only ref. @@ -114,9 +109,7 @@ TEST_F(ExtensionInfoMapTest, Properties) { scoped_refptr<Extension> extension1(CreateExtension("extension1")); scoped_refptr<Extension> extension2(CreateExtension("extension2")); - extension1->AddRef(); info_map->AddExtension(extension1); - extension2->AddRef(); info_map->AddExtension(extension2); EXPECT_EQ(extension1->name(), @@ -143,9 +136,7 @@ TEST_F(ExtensionInfoMapTest, CheckPermissions) { ASSERT_TRUE(app->is_app()); ASSERT_TRUE(app->web_extent().ContainsURL(app_url)); - app->AddRef(); info_map->AddExtension(app); - extension->AddRef(); info_map->AddExtension(extension); // The app should have the notifications permission, either from a diff --git a/chrome/browser/extensions/extension_webnavigation_api.cc b/chrome/browser/extensions/extension_webnavigation_api.cc index 86c4579..5a52612 100644 --- a/chrome/browser/extensions/extension_webnavigation_api.cc +++ b/chrome/browser/extensions/extension_webnavigation_api.cc @@ -7,6 +7,7 @@ #include "chrome/browser/extensions/extension_webnavigation_api.h" #include "base/json/json_writer.h" +#include "base/string_number_conversions.h" #include "base/time.h" #include "base/values.h" #include "chrome/browser/extensions/extension_event_router.h" @@ -64,7 +65,8 @@ void DispatchOnBeforeNavigate(NavigationController* controller, ExtensionTabUtil::GetTabId(controller->tab_contents())); dict->SetString(keys::kUrlKey, details->url().spec()); dict->SetInteger(keys::kFrameIdKey, GetFrameId(details)); - dict->SetInteger(keys::kRequestIdKey, static_cast<int>(request_id)); + dict->SetString(keys::kRequestIdKey, + base::Uint64ToString(request_id)); dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now())); args.Append(dict); diff --git a/chrome/browser/extensions/extension_webrequest_api.cc b/chrome/browser/extensions/extension_webrequest_api.cc index a68f2b3..46d14c1 100644 --- a/chrome/browser/extensions/extension_webrequest_api.cc +++ b/chrome/browser/extensions/extension_webrequest_api.cc @@ -7,13 +7,18 @@ #include <algorithm> #include "base/json/json_writer.h" +#include "base/metrics/histogram.h" +#include "base/string_number_conversions.h" #include "base/values.h" #include "chrome/browser/extensions/extension_event_router_forwarder.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_extent.h" #include "chrome/common/extensions/url_pattern.h" #include "content/browser/browser_thread.h" +#include "net/base/net_errors.h" +#include "net/url_request/url_request.h" #include "googleurl/src/gurl.h" namespace keys = extension_webrequest_api_constants; @@ -56,19 +61,26 @@ static bool IsValidRequestFilterType(const std::string& type) { } static void AddEventListenerOnIOThread( + ProfileId profile_id, const std::string& extension_id, const std::string& event_name, const std::string& sub_event_name, const ExtensionWebRequestEventRouter::RequestFilter& filter, int extra_info_spec) { ExtensionWebRequestEventRouter::GetInstance()->AddEventListener( - extension_id, event_name, sub_event_name, filter, extra_info_spec); + profile_id, extension_id, event_name, sub_event_name, filter, + extra_info_spec); } -static void RemoveEventListenerOnIOThread( - const std::string& extension_id, const std::string& sub_event_name) { - ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener( - extension_id, sub_event_name); +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, + bool cancel) { + ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled( + profile_id, extension_id, event_name, sub_event_name, request_id, cancel); } } // namespace @@ -94,6 +106,7 @@ struct ExtensionWebRequestEventRouter::ExtraInfoSpec { RESPONSE_HEADERS = 1<<3, REDIRECT_REQUEST_LINE = 1<<4, REDIRECT_REQUEST_HEADERS = 1<<5, + BLOCKING = 1<<5, }; static bool InitFromValue(const ListValue& value, int* extra_info_spec); @@ -107,6 +120,7 @@ struct ExtensionWebRequestEventRouter::EventListener { std::string sub_event_name; RequestFilter filter; int extra_info_spec; + mutable std::set<uint64> blocked_requests; // Comparator to work with std::set. bool operator<(const EventListener& that) const { @@ -119,6 +133,21 @@ struct ExtensionWebRequestEventRouter::EventListener { } }; +// Contains info about requests that are blocked waiting for a response from +// an extension. +struct ExtensionWebRequestEventRouter::BlockedRequest { + // The number of event handlers that we are awaiting a response from. + int num_handlers_blocking; + + // The callback to call when we get a response from all event handlers. + net::CompletionCallback* callback; + + // Time the request was issued. Used for logging purposes. + base::Time request_time; + + BlockedRequest() : num_handlers_blocking(0), callback(NULL) {} +}; + bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue( const DictionaryValue& value) { for (DictionaryValue::key_iterator key = value.begin_keys(); @@ -182,6 +211,8 @@ bool ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( *extra_info_spec |= REDIRECT_REQUEST_LINE; else if (str == "redirectRequestHeaders") *extra_info_spec |= REDIRECT_REQUEST_HEADERS; + else if (str == "blocking") + *extra_info_spec |= BLOCKING; else return false; } @@ -199,36 +230,27 @@ ExtensionWebRequestEventRouter::ExtensionWebRequestEventRouter() { ExtensionWebRequestEventRouter::~ExtensionWebRequestEventRouter() { } -// static -void ExtensionWebRequestEventRouter::RemoveEventListenerOnUIThread( - const std::string& extension_id, const std::string& sub_event_name) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - NewRunnableFunction( - &RemoveEventListenerOnIOThread, - extension_id, sub_event_name)); -} - -void ExtensionWebRequestEventRouter::OnBeforeRequest( - ExtensionEventRouterForwarder* event_router, +bool ExtensionWebRequestEventRouter::OnBeforeRequest( ProfileId profile_id, - const GURL& url, - const std::string& method) { + ExtensionEventRouterForwarder* event_router, + net::URLRequest* request, + net::CompletionCallback* callback) { // TODO(jochen): Figure out what to do with events from the system context. if (profile_id == Profile::kInvalidProfileId) - return; + return false; std::vector<const EventListener*> listeners = - GetMatchingListeners(keys::kOnBeforeRequest, url); + GetMatchingListeners(profile_id, keys::kOnBeforeRequest, request->url()); if (listeners.empty()) - return; + return false; ListValue args; DictionaryValue* dict = new DictionaryValue(); - dict->SetString(keys::kUrlKey, url.spec()); - dict->SetString(keys::kMethodKey, method); + dict->SetString(keys::kUrlKey, request->url().spec()); + dict->SetString(keys::kMethodKey, request->method()); // TODO(mpcomplete): implement dict->SetInteger(keys::kTabIdKey, 0); - dict->SetInteger(keys::kRequestIdKey, 0); + dict->SetString(keys::kRequestIdKey, + base::Uint64ToString(request->identifier())); dict->SetString(keys::kTypeKey, "main_frame"); dict->SetInteger(keys::kTimeStampKey, 1); args.Append(dict); @@ -236,16 +258,58 @@ void ExtensionWebRequestEventRouter::OnBeforeRequest( std::string json_args; base::JSONWriter::Write(&args, false, &json_args); + // TODO(mpcomplete): Consider consolidating common (extension_id,json_args) + // pairs into a single message sent to a list of sub_event_names. + int num_handlers_blocking = 0; for (std::vector<const EventListener*>::iterator it = listeners.begin(); it != listeners.end(); ++it) { - event_router->DispatchEventToExtension( (*it)->extension_id, (*it)->sub_event_name, json_args, profile_id, true, GURL()); + if ((*it)->extra_info_spec & ExtraInfoSpec::BLOCKING) { + (*it)->blocked_requests.insert(request->identifier()); + ++num_handlers_blocking; + } + } + + if (num_handlers_blocking > 0) { + CHECK(blocked_requests_.find(request->identifier()) == + blocked_requests_.end()); + blocked_requests_[request->identifier()].num_handlers_blocking = + num_handlers_blocking; + blocked_requests_[request->identifier()].callback = callback; + blocked_requests_[request->identifier()].request_time = + request->request_time(); + + return true; } + + return false; +} + +void ExtensionWebRequestEventRouter::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) { + EventListener listener; + listener.extension_id = extension_id; + listener.sub_event_name = sub_event_name; + + // The listener may have been removed (e.g. due to the process going away) + // before we got here. + std::set<EventListener>::iterator found = + listeners_[profile_id][event_name].find(listener); + if (found != listeners_[profile_id][event_name].end()) + found->blocked_requests.erase(request_id); + + DecrementBlockCount(request_id, cancel); } void ExtensionWebRequestEventRouter::AddEventListener( + ProfileId profile_id, const std::string& extension_id, const std::string& event_name, const std::string& sub_event_name, @@ -260,12 +324,13 @@ void ExtensionWebRequestEventRouter::AddEventListener( listener.filter = filter; listener.extra_info_spec = extra_info_spec; - CHECK_EQ(listeners_[event_name].count(listener), 0u) << + CHECK_EQ(listeners_[profile_id][event_name].count(listener), 0u) << "extension=" << extension_id << " event=" << event_name; - listeners_[event_name].insert(listener); + listeners_[profile_id][event_name].insert(listener); } void ExtensionWebRequestEventRouter::RemoveEventListener( + ProfileId profile_id, const std::string& extension_id, const std::string& sub_event_name) { size_t slash_sep = sub_event_name.find('/'); @@ -278,16 +343,29 @@ void ExtensionWebRequestEventRouter::RemoveEventListener( listener.extension_id = extension_id; listener.sub_event_name = sub_event_name; - CHECK_EQ(listeners_[event_name].count(listener), 1u) << + CHECK_EQ(listeners_[profile_id][event_name].count(listener), 1u) << "extension=" << extension_id << " event=" << event_name; - listeners_[event_name].erase(listener); + + // Unblock any request that this event listener may have been blocking. + std::set<EventListener>::iterator found = + listeners_[profile_id][event_name].find(listener); + for (std::set<uint64>::iterator it = found->blocked_requests.begin(); + it != found->blocked_requests.end(); ++it) { + DecrementBlockCount(*it, false); + } + + listeners_[profile_id][event_name].erase(listener); } std::vector<const ExtensionWebRequestEventRouter::EventListener*> ExtensionWebRequestEventRouter::GetMatchingListeners( - const std::string& event_name, const GURL& url) { + ProfileId profile_id, + const std::string& event_name, + const GURL& url) { + // TODO(mpcomplete): handle profile_id == invalid (should collect all + // listeners). std::vector<const EventListener*> matching_listeners; - std::set<EventListener>& listeners = listeners_[event_name]; + std::set<EventListener>& listeners = listeners_[profile_id][event_name]; for (std::set<EventListener>::iterator it = listeners.begin(); it != listeners.end(); ++it) { if (it->filter.urls.is_empty() || it->filter.urls.ContainsURL(url)) @@ -296,6 +374,27 @@ ExtensionWebRequestEventRouter::GetMatchingListeners( return matching_listeners; } +void ExtensionWebRequestEventRouter::DecrementBlockCount(uint64 request_id, + bool cancel) { + // It's possible that this request was already cancelled by a previous event + // handler. If so, ignore this response. + if (blocked_requests_.find(request_id) == blocked_requests_.end()) + return; + + BlockedRequest& blocked_request = blocked_requests_[request_id]; + int num_handlers_blocking = --blocked_request.num_handlers_blocking; + CHECK_GE(num_handlers_blocking, 0); + + HISTOGRAM_TIMES("Extensions.NetworkDelay", + base::Time::Now() - blocked_request.request_time); + + if (num_handlers_blocking == 0 || cancel) { + CHECK(blocked_request.callback); + blocked_request.callback->Run(cancel ? net::ERR_EMPTY_RESPONSE : net::OK); + blocked_requests_.erase(request_id); + } +} + bool WebRequestAddEventListener::RunImpl() { // Argument 0 is the callback, which we don't use here. @@ -325,7 +424,40 @@ bool WebRequestAddEventListener::RunImpl() { BrowserThread::IO, FROM_HERE, NewRunnableFunction( &AddEventListenerOnIOThread, - extension_id(), event_name, sub_event_name, filter, extra_info_spec)); + profile()->GetRuntimeId(), extension_id(), + event_name, sub_event_name, filter, extra_info_spec)); + + return true; +} + +bool WebRequestEventHandled::RunImpl() { + std::string event_name; + EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &event_name)); + + std::string sub_event_name; + EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &sub_event_name)); + + std::string request_id_str; + EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &request_id_str)); + // TODO(mpcomplete): string-to-uint64? + int64 request_id; + EXTENSION_FUNCTION_VALIDATE(base::StringToInt64(request_id_str, &request_id)); + + bool cancel = false; + if (HasOptionalArgument(3)) { + DictionaryValue* value = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(3, &value)); + + if (value->HasKey("cancel")) + EXTENSION_FUNCTION_VALIDATE(value->GetBoolean("cancel", &cancel)); + } + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableFunction( + &EventHandledOnIOThread, + profile()->GetRuntimeId(), extension_id(), + event_name, sub_event_name, request_id, cancel)); return true; } diff --git a/chrome/browser/extensions/extension_webrequest_api.h b/chrome/browser/extensions/extension_webrequest_api.h index f04edc9..0cd52a3 100644 --- a/chrome/browser/extensions/extension_webrequest_api.h +++ b/chrome/browser/extensions/extension_webrequest_api.h @@ -12,13 +12,18 @@ #include <vector> #include "base/singleton.h" -#include "ipc/ipc_message.h" #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/profiles/profile.h" +#include "ipc/ipc_message.h" +#include "net/base/completion_callback.h" class ExtensionEventRouterForwarder; class GURL; +namespace net { +class URLRequest; +} + // This class observes network events and routes them to the appropriate // extensions listening to those events. All methods must be called on the IO // thread unless otherwise specified. @@ -29,24 +34,29 @@ class ExtensionWebRequestEventRouter { static ExtensionWebRequestEventRouter* GetInstance(); - // Removes the listener for the given sub-event. Can be called on the UI - // thread. - static void RemoveEventListenerOnUIThread( - const std::string& extension_id, - const std::string& sub_event_name); + // Dispatches the OnBeforeRequest event to any extensions whose filters match + // the given request. Returns true if an extension wants to pause the request. + bool OnBeforeRequest(ProfileId profile_id, + ExtensionEventRouterForwarder* event_router, + net::URLRequest* request, + net::CompletionCallback* callback); - // TODO(mpcomplete): additional params - void OnBeforeRequest( - ExtensionEventRouterForwarder* event_router, + // Called when an event listener handles a blocking event and responds. + // TODO(mpcomplete): modify request + void OnEventHandled( ProfileId profile_id, - const GURL& url, - const std::string& method); + const std::string& extension_id, + const std::string& event_name, + const std::string& sub_event_name, + uint64 request_id, + bool cancel); // 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 // the extension process to correspond to the given filter and // extra_info_spec. void AddEventListener( + ProfileId profile_id, const std::string& extension_id, const std::string& event_name, const std::string& sub_event_name, @@ -55,13 +65,17 @@ class ExtensionWebRequestEventRouter { // Removes the listener for the given sub-event. void RemoveEventListener( + ProfileId profile_id, const std::string& extension_id, const std::string& sub_event_name); private: friend struct DefaultSingletonTraits<ExtensionWebRequestEventRouter>; struct EventListener; - typedef std::map<std::string, std::set<EventListener> > ListenerMap; + struct BlockedRequest; + typedef std::map<std::string, std::set<EventListener> > ListenerMapForProfile; + typedef std::map<ProfileId, ListenerMapForProfile> ListenerMap; + typedef std::map<uint64, BlockedRequest> BlockedRequestMap; ExtensionWebRequestEventRouter(); ~ExtensionWebRequestEventRouter(); @@ -69,12 +83,21 @@ class ExtensionWebRequestEventRouter { // Returns a list of event listeners that care about the given event, based // on their filter parameters. std::vector<const EventListener*> GetMatchingListeners( - const std::string& event_name, const GURL& url); + ProfileId profile_id, const std::string& event_name, const GURL& url); - // A map between an event name and a set of extensions that are listening - // to that event. + // Decrements the count of event handlers blocking the given request. When the + // count reaches 0 (or immediately if the request is being cancelled), we + // stop blocking the request and either resume or cancel it. + void DecrementBlockCount(uint64 request_id, bool cancel); + + // A map for each profile that maps an event name to a set of extensions that + // are listening to that event. ListenerMap listeners_; + // A map of network requests that are waiting for at least one event handler + // to respond. + BlockedRequestMap blocked_requests_; + DISALLOW_COPY_AND_ASSIGN(ExtensionWebRequestEventRouter); }; @@ -84,4 +107,10 @@ class WebRequestAddEventListener : public SyncExtensionFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.webRequest.addEventListener"); }; +class WebRequestEventHandled : public SyncExtensionFunction { + public: + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("experimental.webRequest.eventHandled"); +}; + #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_WEBREQUEST_API_H_ diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc index 9a0e035..4324b60 100644 --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc @@ -336,7 +336,7 @@ void IOThread::Init() { globals_->extension_event_router_forwarder = extension_event_router_forwarder_; globals_->system_network_delegate.reset(new ChromeNetworkDelegate( - extension_event_router_forwarder_, Profile::kInvalidProfileId)); + extension_event_router_forwarder_, Profile::kInvalidProfileId)); globals_->host_resolver.reset( CreateGlobalHostResolver(net_log_)); globals_->cert_verifier.reset(new net::CertVerifier); diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc index 59fb2e6..8fa34b0 100644 --- a/chrome/browser/net/chrome_network_delegate.cc +++ b/chrome/browser/net/chrome_network_delegate.cc @@ -41,9 +41,10 @@ ChromeNetworkDelegate::ChromeNetworkDelegate( ChromeNetworkDelegate::~ChromeNetworkDelegate() {} -void ChromeNetworkDelegate::OnBeforeURLRequest(net::URLRequest* request) { - ExtensionWebRequestEventRouter::GetInstance()->OnBeforeRequest( - event_router_.get(), profile_id_, request->url(), request->method()); +bool ChromeNetworkDelegate::OnBeforeURLRequest( + net::URLRequest* request, net::CompletionCallback* callback) { + return ExtensionWebRequestEventRouter::GetInstance()->OnBeforeRequest( + profile_id_, event_router_.get(), request, callback); } void ChromeNetworkDelegate::OnSendHttpRequest( diff --git a/chrome/browser/net/chrome_network_delegate.h b/chrome/browser/net/chrome_network_delegate.h index 5b3fb15..2f2f0ea 100644 --- a/chrome/browser/net/chrome_network_delegate.h +++ b/chrome/browser/net/chrome_network_delegate.h @@ -17,8 +17,8 @@ class ExtensionEventRouterForwarder; // add hooks into the network stack. class ChromeNetworkDelegate : public net::NetworkDelegate { public: - // If |profile| is NULL, events will be broadcasted to all profiles, - // otherwise, they will be only send to the specified profile. + // If |profile_id| is the invalid profile, events will be broadcasted to all + // profiles, otherwise, they will only be sent to the specified profile. explicit ChromeNetworkDelegate( ExtensionEventRouterForwarder* event_router, ProfileId profile_id); @@ -26,7 +26,8 @@ class ChromeNetworkDelegate : public net::NetworkDelegate { private: // NetworkDelegate methods: - virtual void OnBeforeURLRequest(net::URLRequest* request); + virtual bool OnBeforeURLRequest(net::URLRequest* request, + net::CompletionCallback* callback); virtual void OnSendHttpRequest(net::HttpRequestHeaders* headers); virtual void OnResponseStarted(net::URLRequest* request); virtual void OnReadCompleted(net::URLRequest* request, int bytes_read); diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 0d4d077..913f6d5 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -836,14 +836,11 @@ URLRequestContextGetter* ProfileImpl::GetRequestContextForExtensions() { void ProfileImpl::RegisterExtensionWithRequestContexts( const Extension* extension) { - // AddRef to ensure the data lives until the other thread gets it. Balanced in - // OnNewExtensions. - extension->AddRef(); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableMethod(extension_info_map_.get(), &ExtensionInfoMap::AddExtension, - extension)); + make_scoped_refptr(extension))); } void ProfileImpl::UnregisterExtensionWithRequestContexts( diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index d23f1ae..a4a53f5 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -3337,7 +3337,7 @@ "tabId": {"type": "integer", "description": "The ID of the tab in which the navigation is about to occur."}, "url": {"type": "string"}, "frameId": {"type": "integer", "description": "0 indicates the navigation happens in the tab content window; positive value indicates navigation in a subframe. Frame IDs are unique within a tab."}, - "requestId": {"type": "integer", "description": "The ID of the request to retrieve the document of this navigation. Note that this event is fired prior to the corresponding chrome.experimental.webRequest.onBeforeRequest."}, + "requestId": {"type": "string", "description": "The ID of the request to retrieve the document of this navigation. Note that this event is fired prior to the corresponding chrome.experimental.webRequest.onBeforeRequest."}, "timeStamp": {"type": "number", "description": "The time when the browser was about to start the navigation, in milliseconds since the epoch."} } } @@ -3479,12 +3479,31 @@ "description": "Array of extra information that should be passed to the listener function.", "items": { "type": "string", - "enum": ["requestLine", "requestHeaders", "statusLine", "responseHeaders", "redirectRequestLine", "redirectRequestHeaders"] + "enum": ["requestLine", "requestHeaders", "statusLine", "responseHeaders", "redirectRequestLine", "redirectRequestHeaders", "blocking"] } }, {"type": "string", "name": "eventName"}, {"type": "string", "name": "subEventName"} ] + }, + { + "name": "eventHandled", + "nodoc": true, + "type": "function", + "description": "Used internally to send a response for a blocked event.", + "parameters": [ + {"type": "string", "name": "eventName"}, + {"type": "string", "name": "subEventName"}, + {"type": "string", "name": "requestId"}, + { + "type": "object", + "name": "response", + "optional": true, + "properties": { + "cancel": {"type": "boolean", "optional": true} + } + } + ] } ], "events": [ @@ -3497,7 +3516,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request. Request IDs are unique within a browser session. As a result, they could be used to relate different events of the same request."}, + "requestId": {"type": "string", "description": "The ID of the request. Request IDs are unique within a browser session. As a result, they could be used to relate different events of the same request."}, "url": {"type": "string"}, "method": {"type": "string", "description": "Standard HTTP method."}, "tabId": {"type": "integer", "description": "The ID of the tab in which the request takes place. Set to null if the request isn't related to a tab."}, @@ -3516,7 +3535,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request."}, + "requestId": {"type": "string", "description": "The ID of the request."}, "url": {"type": "string"}, "ip": {"type": "string", "description": "The server IP address that is actually connected to. Note that it may be a literal IPv6 address."}, "timeStamp": {"type": "number", "description": "The time when the browser finished sending the request, in milliseconds since the epoch."} @@ -3533,7 +3552,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request."}, + "requestId": {"type": "string", "description": "The ID of the request."}, "url": {"type": "string"}, "statusCode": {"type": "integer", "description": "Standard HTTP status code returned by the server."}, "timeStamp": {"type": "number", "description": "The time when the status line and response headers were received, in milliseconds since the epoch."} @@ -3550,7 +3569,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request."}, + "requestId": {"type": "string", "description": "The ID of the request."}, "url": {"type": "string", "description": "The URL of the current request."}, "statusCode": {"type": "integer", "description": "Standard HTTP status code returned by the server."}, "redirectUrl": {"type": "string", "description": "The new URL."}, @@ -3568,7 +3587,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request."}, + "requestId": {"type": "string", "description": "The ID of the request."}, "url": {"type": "string", "description": "The URL of the current request."}, "statusCode": {"type": "integer", "description": "Standard HTTP status code returned by the server."}, "timeStamp": {"type": "number", "description": "The time when the response was received completely, in milliseconds since the epoch."} @@ -3585,7 +3604,7 @@ "type": "object", "name": "details", "properties": { - "requestId": {"type": "integer", "description": "The ID of the request."}, + "requestId": {"type": "string", "description": "The ID of the request."}, "url": {"type": "string", "description": "The URL of the current request."}, "error": {"type": "string", "description": "The error description."}, "timeStamp": {"type": "number", "description": "The time when the error occurred, in milliseconds since the epoch."} diff --git a/chrome/common/extensions/docs/examples/extensions/imageinfo.zip b/chrome/common/extensions/docs/examples/extensions/imageinfo.zip Binary files differindex 7d14906..4a61c1d 100644 --- a/chrome/common/extensions/docs/examples/extensions/imageinfo.zip +++ b/chrome/common/extensions/docs/examples/extensions/imageinfo.zip diff --git a/chrome/common/extensions/docs/experimental.webNavigation.html b/chrome/common/extensions/docs/experimental.webNavigation.html index 7d733d1..9b16346 100644 --- a/chrome/common/extensions/docs/experimental.webNavigation.html +++ b/chrome/common/extensions/docs/experimental.webNavigation.html @@ -717,7 +717,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> diff --git a/chrome/common/extensions/docs/experimental.webRequest.html b/chrome/common/extensions/docs/experimental.webRequest.html index 0e11c39..1f2fbbc 100644 --- a/chrome/common/extensions/docs/experimental.webRequest.html +++ b/chrome/common/extensions/docs/experimental.webRequest.html @@ -281,6 +281,8 @@ <ol> <li style="display: none; "> <a href="#method-anchor">methodName</a> + </li><li style="display: none; "> + <a href="#method-anchor">methodName</a> </li> </ol> </li> @@ -419,6 +421,72 @@ </p> </div> <!-- /description --> + </div><div class="apiItem" style="display: none; "> + <a></a> <!-- method-anchor --> + <h4>method name</h4> + + <div class="summary"><span>void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.module.methodName</span>(<span><span>, </span><span></span> + <var><span></span></var></span>)</div> + + <div class="description"> + <p class="todo">Undocumented.</p> + <p> + A description from the json schema def of the function goes here. + </p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + </div> + </div> + </dl> + + <!-- RETURNS --> + <h4>Returns</h4> + <dl> + <div> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div> + <div> + <h4>Callback function</h4> + <p> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div> + <div> + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p> + This function was added in version <b><span></span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + </div> <!-- /apiItem --> </div> <!-- /apiGroup --> @@ -511,7 +579,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -940,7 +1008,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -1439,7 +1507,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -1800,7 +1868,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -2161,7 +2229,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -2524,7 +2592,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>integer</span> + <span>string</span> <span style="display: none; "></span> </span> </span> diff --git a/chrome/common/extensions/docs/samples.json b/chrome/common/extensions/docs/samples.json index 9a62535..2aad896 100644 --- a/chrome/common/extensions/docs/samples.json +++ b/chrome/common/extensions/docs/samples.json @@ -65,6 +65,7 @@ "chrome.experimental.webNavigation.onDOMContentLoaded": "experimental.webNavigation.html#event-onDOMContentLoaded", "chrome.experimental.webNavigation.onErrorOccurred": "experimental.webNavigation.html#event-onErrorOccurred", "chrome.experimental.webRequest.addEventListener": "experimental.webRequest.html#method-addEventListener", + "chrome.experimental.webRequest.eventHandled": "experimental.webRequest.html#method-eventHandled", "chrome.experimental.webRequest.onBeforeRedirect": "experimental.webRequest.html#event-onBeforeRedirect", "chrome.experimental.webRequest.onBeforeRequest": "experimental.webRequest.html#event-onBeforeRequest", "chrome.experimental.webRequest.onCompleted": "experimental.webRequest.html#event-onCompleted", diff --git a/chrome/renderer/resources/event_bindings.js b/chrome/renderer/resources/event_bindings.js index 2b8cce8..05ed65d 100644 --- a/chrome/renderer/resources/event_bindings.js +++ b/chrome/renderer/resources/event_bindings.js @@ -165,7 +165,8 @@ var chrome = chrome || {}; try { this.listeners_[i].apply(null, args); } catch (e) { - console.error(e); + console.error("Error in event handler for '" + this.eventName_ + + "': " + e); } } }; diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js index 0d97ced8..ce1b21d 100644 --- a/chrome/renderer/resources/extension_process_bindings.js +++ b/chrome/renderer/resources/extension_process_bindings.js @@ -259,6 +259,7 @@ var chrome = chrome || {}; this.eventName_ = eventName; this.argSchemas_ = opt_argSchemas; this.subEvents_ = []; + this.callbackMap_ = {}; }; // Registers a callback to be called when this event is dispatched. If @@ -275,24 +276,43 @@ var chrome = chrome || {}; var subEvent = new chrome.Event(subEventName, this.argSchemas_); this.subEvents_.push(subEvent); - subEvent.addListener(cb); + var subEventCallback = cb; + if (opt_extraInfo && opt_extraInfo.indexOf("blocking") >= 0) { + var eventName = this.eventName_; + subEventCallback = function() { + var requestId = arguments[0].requestId; + try { + var result = cb.apply(null, arguments); + chrome.experimental.webRequest.eventHandled( + eventName, subEventName, requestId, result); + } catch (e) { + chrome.experimental.webRequest.eventHandled( + eventName, subEventName, requestId); + throw e; + } + }; + } + this.callbackMap_[cb] = subEventCallback; + subEvent.addListener(subEventCallback); }; // Unregisters a callback. chrome.WebRequestEvent.prototype.removeListener = function(cb) { var idx = this.findListener_(cb); - if (idx >= -1) { + if (idx < 0) { return; } - this.subEvents_[idx].removeListener(cb); + var subEventCallback = this.callbackMap_[cb]; + this.subEvents_[idx].removeListener(subEventCallback); if (!this.subEvents_[idx].hasListeners()) this.subEvents_.splice(idx, 1); }; chrome.WebRequestEvent.prototype.findListener_ = function(cb) { + var subEventCallback = this.callbackMap_[cb]; for (var i = 0; i < this.subEvents_.length; i++) { - if (this.subEvents_[i].findListener_(cb) > -1) + if (this.subEvents_[i].findListener_(subEventCallback) > -1) return i; } diff --git a/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/tests.js b/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/tests.js index 6cb9510..1b9094e 100644 --- a/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/tests.js +++ b/chrome/test/data/extensions/api_test/webnavigation/clientRedirect/tests.js @@ -10,7 +10,7 @@ function runTests() { expect([ [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('a.html') }], @@ -33,7 +33,7 @@ function runTests() { url: getURL('a.html') }], [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('b.html') }], diff --git a/chrome/test/data/extensions/api_test/webnavigation/failures/tests.js b/chrome/test/data/extensions/api_test/webnavigation/failures/tests.js index a7c2f87..99b97c5 100644 --- a/chrome/test/data/extensions/api_test/webnavigation/failures/tests.js +++ b/chrome/test/data/extensions/api_test/webnavigation/failures/tests.js @@ -9,7 +9,7 @@ function runTests() { expect([ [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('nonexistant.html') }], @@ -27,7 +27,7 @@ function runTests() { expect([ [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('d.html') }], @@ -40,7 +40,7 @@ function runTests() { url: getURL('d.html') }], [ "onBeforeNavigate", { frameId: 1, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('c.html') }], @@ -68,7 +68,7 @@ function runTests() { expect([ [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('a.html') }], @@ -81,7 +81,7 @@ function runTests() { url: getURL('a.html') }], [ "onBeforeNavigate", { frameId: 1, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('b.html') }], @@ -114,7 +114,7 @@ function runTests() { url: getURL('a.html') }], [ "onBeforeNavigate", { frameId: 1, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('c.html') }], diff --git a/chrome/test/data/extensions/api_test/webnavigation/forwardBack/tests.js b/chrome/test/data/extensions/api_test/webnavigation/forwardBack/tests.js index 575a77c..6b8da61 100644 --- a/chrome/test/data/extensions/api_test/webnavigation/forwardBack/tests.js +++ b/chrome/test/data/extensions/api_test/webnavigation/forwardBack/tests.js @@ -10,7 +10,7 @@ function runTests() { expect([ [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('a.html') }], @@ -33,7 +33,7 @@ function runTests() { url: getURL('a.html') }], [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('b.html') }], @@ -56,7 +56,7 @@ function runTests() { url: getURL('b.html') }], [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('a.html') }], diff --git a/chrome/test/data/extensions/api_test/webnavigation/iframe/tests.js b/chrome/test/data/extensions/api_test/webnavigation/iframe/tests.js index 4ba7af6..6d526c8 100644 --- a/chrome/test/data/extensions/api_test/webnavigation/iframe/tests.js +++ b/chrome/test/data/extensions/api_test/webnavigation/iframe/tests.js @@ -10,7 +10,7 @@ function runTests() { expect([ [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('a.html') }], @@ -23,7 +23,7 @@ function runTests() { url: getURL('a.html') }], [ "onBeforeNavigate", { frameId: 1, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('b.html') }], @@ -56,7 +56,7 @@ function runTests() { url: getURL('a.html') }], [ "onBeforeNavigate", { frameId: 1, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('c.html') }], @@ -87,7 +87,7 @@ function runTests() { expect([ [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('d.html') }], @@ -100,7 +100,7 @@ function runTests() { url: getURL('d.html') }], [ "onBeforeNavigate", { frameId: 1, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('e.html') }], @@ -123,7 +123,7 @@ function runTests() { url: getURL('e.html') }], [ "onBeforeNavigate", { frameId: 2, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('f.html') }], @@ -156,7 +156,7 @@ function runTests() { url: getURL('d.html') }], [ "onBeforeNavigate", { frameId: 2, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('g.html') }], diff --git a/chrome/test/data/extensions/api_test/webnavigation/openTab/tests.js b/chrome/test/data/extensions/api_test/webnavigation/openTab/tests.js index a35b60a..d35725c 100644 --- a/chrome/test/data/extensions/api_test/webnavigation/openTab/tests.js +++ b/chrome/test/data/extensions/api_test/webnavigation/openTab/tests.js @@ -9,7 +9,7 @@ function runTests() { expect([ [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('a.html') }], @@ -37,7 +37,7 @@ function runTests() { url: getURL('b.html') }], [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 1, timeStamp: 0, url: getURL('b.html') }], @@ -66,7 +66,7 @@ function runTests() { expect([ [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('c.html') }], @@ -79,7 +79,7 @@ function runTests() { url: getURL('c.html') }], [ "onBeforeNavigate", { frameId: 1, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('a.html') }], @@ -117,7 +117,7 @@ function runTests() { url: getURL('b.html') }], [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 1, timeStamp: 0, url: getURL('b.html') }], diff --git a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/tests.js b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/tests.js index 5b9675e..4fb0341 100644 --- a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/tests.js +++ b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/tests.js @@ -9,7 +9,7 @@ function runTests() { expect([ [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('a.html') }], @@ -32,7 +32,7 @@ function runTests() { url: getURL('a.html') }], [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('a.html#anchor') }], diff --git a/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/tests.js b/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/tests.js index 377d38e..13e7126 100644 --- a/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/tests.js +++ b/chrome/test/data/extensions/api_test/webnavigation/simpleLoad/tests.js @@ -9,7 +9,7 @@ function runTests() { expect([ [ "onBeforeNavigate", { frameId: 0, - requestId: 0, + requestId: "0", tabId: 0, timeStamp: 0, url: getURL('a.html') }], diff --git a/chrome/test/data/extensions/api_test/webrequest/events/test.html b/chrome/test/data/extensions/api_test/webrequest/events/test.html index 1489e2e..83c1dba 100644 --- a/chrome/test/data/extensions/api_test/webrequest/events/test.html +++ b/chrome/test/data/extensions/api_test/webrequest/events/test.html @@ -2,12 +2,12 @@ var getURL = chrome.extension.getURL; var expectedEventData; var capturedEventData; -var captureFilteredEvents; -function expect(data, filtered) { +function expect(data, filter, extraInfoSpec) { expectedEventData = data; capturedEventData = []; - captureFilteredEvents = filtered; + removeListeners(); + initListeners(filter, extraInfoSpec); } function checkExpectations() { @@ -20,49 +20,66 @@ function checkExpectations() { chrome.test.succeed(); } -function captureEvent(name, details, isFiltered) { +function captureEvent(name, details) { // TODO(mpcomplete): implement the rest of the parameters. delete details.requestId; delete details.tabId; delete details.timeStamp; delete details.type; - if (captureFilteredEvents == isFiltered) - capturedEventData.push([name, details]); + capturedEventData.push([name, details]); checkExpectations(); } -chrome.experimental.webRequest.onBeforeRequest.addListener( - function(details) { - captureEvent("onBeforeRequest", details); -}); -chrome.experimental.webRequest.onRequestSent.addListener( - function(details) { - captureEvent("onRequestSent", details); -}); -chrome.experimental.webRequest.onHeadersReceived.addListener( - function(details) { - captureEvent("onHeadersReceived", details); -}); -chrome.experimental.webRequest.onBeforeRedirect.addListener( - function(details) { - captureEvent("onBeforeRedirect", details); -}); -chrome.experimental.webRequest.onCompleted.addListener( - function(details) { - captureEvent("onCompleted", details); -}); -chrome.experimental.webRequest.onErrorOccurred.addListener( - function(details) { - captureEvent("onErrorOccurred", details); -}); +function initListeners(filter, extraInfoSpec) { + chrome.experimental.webRequest.onBeforeRequest.addListener( + function(details) { + captureEvent("onBeforeRequest", details); + return {cancel: true}; // no effect unless event is blocking. + }, filter, extraInfoSpec); + chrome.experimental.webRequest.onRequestSent.addListener( + function(details) { + captureEvent("onRequestSent", details); + return {cancel: true}; + }, filter, extraInfoSpec); + chrome.experimental.webRequest.onHeadersReceived.addListener( + function(details) { + captureEvent("onHeadersReceived", details); + return {cancel: true}; + }, filter, extraInfoSpec); + chrome.experimental.webRequest.onBeforeRedirect.addListener( + function(details) { + captureEvent("onBeforeRedirect", details); + return {cancel: true}; + }, filter, extraInfoSpec); + chrome.experimental.webRequest.onCompleted.addListener( + function(details) { + captureEvent("onCompleted", details); + return {cancel: true}; + }, filter, extraInfoSpec); + chrome.experimental.webRequest.onErrorOccurred.addListener( + function(details) { + captureEvent("onErrorOccurred", details); + return {cancel: true}; + }, filter, extraInfoSpec); +} -// For filter tests. -chrome.experimental.webRequest.onBeforeRequest.addListener( - function(details) { - captureEvent("onBeforeRequest", details, true); -}, - {urls: [getURL("simpleLoad/*")]} -); +function removeListeners() { + function helper(event) { + // Note: We're poking at the internal event data, but it's easier than + // the alternative. If this starts failing, we just need to update this + // helper. + for (var cb in event.callbackMap_) { + event.removeListener(cb); + } + chrome.test.assertEq(0, event.subEvents_.length); + } + helper(chrome.experimental.webRequest.onBeforeRequest); + helper(chrome.experimental.webRequest.onRequestSent); + helper(chrome.experimental.webRequest.onHeadersReceived); + helper(chrome.experimental.webRequest.onBeforeRedirect); + helper(chrome.experimental.webRequest.onCompleted); + helper(chrome.experimental.webRequest.onErrorOccurred); +} chrome.tabs.getSelected(null, function(tab) { var tabId = tab.id; @@ -108,6 +125,21 @@ chrome.tabs.getSelected(null, function(tab) { chrome.tabs.update(tabId, { url: getURL("complexLoad/a.html") }); }, + // Navigates to a page with subresources, with a blocking handler that + // cancels the page request. The page will not load, and we should not + // see the subresources. + function complexLoadBlocking() { + expect([ + [ "onBeforeRequest", + { + method: "GET", + url: getURL("complexLoad/a.html") + } + ] + ], {}, ["blocking"]); + chrome.tabs.update(tabId, { url: getURL("complexLoad/a.html") }); + }, + // Loads several resources, but should only see the simpleLoad // due to the filter. function simpleLoadFiltered() { @@ -118,9 +150,9 @@ chrome.tabs.getSelected(null, function(tab) { url: getURL("simpleLoad/a.html") } ], - ], true); + ], {urls: [getURL("simpleLoad/*")]}, []); chrome.tabs.update(tabId, { url: getURL("complexLoad/a.html") }); - chrome.tabs.update(tabId, { url: getURL("simpleLoad/a.html") }); + chrome.tabs.create({ url: getURL("simpleLoad/a.html") }); }, ]); }); diff --git a/chrome/test/data/extensions/profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/background.html b/chrome/test/data/extensions/profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/background.html new file mode 100644 index 0000000..09904d7 --- /dev/null +++ b/chrome/test/data/extensions/profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/background.html @@ -0,0 +1,10 @@ +<script> +var onBefore = chrome.experimental.webRequest.onBeforeRequest; +onBefore.addListener(function(info) { + return {"cancel": false}; +}, {urls: ["http://*/*"]}, ['blocking']); +var onBefore = chrome.experimental.webRequest.onBeforeRequest; +onBefore.addListener(function(info) { + return {"cancel": false}; +}, {urls: ["file://*/*"]}, ['blocking']); +</script> diff --git a/chrome/test/data/extensions/profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/manifest.json b/chrome/test/data/extensions/profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/manifest.json new file mode 100644 index 0000000..d134e26 --- /dev/null +++ b/chrome/test/data/extensions/profiles/extension_webrequest/Default/Extensions/behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0/manifest.json @@ -0,0 +1,7 @@ +{ + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuUZGKCDbff6IRaxa4Pue7PPkxwPaNhGT3JEqppEsNWFjM80imEdqMbf3lrWqEfaHgaNku7nlpwPO1mu3/4Hr+XdNa5MhfnOnuPee4hyTLwOs3Vzz81wpbdzUxZSi2OmqMyI5oTaBYICfNHLwcuc65N5dbt6WKGeKgTpp4v7j7zwIDAQAB", + "version": "1.0.0.0", + "name": "Webrequest", + "permissions": ["experimental", "webRequest"], + "background_page": "background.html" +} diff --git a/chrome/test/data/extensions/profiles/extension_webrequest/Default/Preferences b/chrome/test/data/extensions/profiles/extension_webrequest/Default/Preferences new file mode 100644 index 0000000..7810326 --- /dev/null +++ b/chrome/test/data/extensions/profiles/extension_webrequest/Default/Preferences @@ -0,0 +1,75 @@ +{ + "browser": { + "window_placement": { + "bottom": 1150, + "left": 10, + "maximized": false, + "right": 955, + "top": 10, + "work_area_bottom": 1160, + "work_area_left": 0, + "work_area_right": 1920, + "work_area_top": 0 + } + }, + "countryid_at_install": 21843, + "dns_prefetching": { + "host_referral_list": [ 2 ], + "startup_list": [ 1 ] + }, + "download": { + "directory_upgrade": true, + "extensions_to_open": "" + }, + "extensions": { + "autoupdate": { + "next_check": "12943417068538765" + }, + "chrome_url_overrides": { + "bookmarks": [ "chrome-extension://eemcgdkfndhakfknompkggombfjjjeno/main.html" ] + }, + "settings": { + "behllobkkfkfnphdnhnkndlbkcpglgmj": { + "granted_permissions": { + "api": [ "experimental" ], + "full": false + }, + "install_time": "12943416715915765", + "location": 1, + "manifest": { + "background_page": "background.html", + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuUZGKCDbff6IRaxa4Pue7PPkxwPaNhGT3JEqppEsNWFjM80imEdqMbf3lrWqEfaHgaNku7nlpwPO1mu3/4Hr+XdNa5MhfnOnuPee4hyTLwOs3Vzz81wpbdzUxZSi2OmqMyI5oTaBYICfNHLwcuc65N5dbt6WKGeKgTpp4v7j7zwIDAQAB", + "name": "Webrequest", + "permissions": [ "experimental", "webRequest" ], + "version": "1.0.0.0" + }, + "path": "behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0", + "state": 1 + } + } + }, + "google": { + "services": { + "username": "" + } + }, + "ntp": { + "pref_version": 3, + "promo_resource_cache_update": "1298943121.229765" + }, + "plugins": { + "enabled_internal_pdf3": true + }, + "profile": { + "content_settings": { + "pref_version": 1 + }, + "exited_cleanly": true, + "id": "not-signed-in", + "name": "", + "nickname": "" + }, + "tabs": { + "use_vertical_tabs": false + } +} diff --git a/chrome/test/page_cycler/page_cycler_test.cc b/chrome/test/page_cycler/page_cycler_test.cc index 69f164b..5cabe96 100644 --- a/chrome/test/page_cycler/page_cycler_test.cc +++ b/chrome/test/page_cycler/page_cycler_test.cc @@ -178,6 +178,8 @@ class PageCyclerTest : public UIPerfTest { // Expose garbage collection for the page cycler tests. launch_arguments_.AppendSwitchASCII(switches::kJavaScriptFlags, "--expose_gc"); + // Enable experimental extension APIs for webrequest tests. + launch_arguments_.AppendSwitch(switches::kEnableExperimentalExtensionApis); #if defined(OS_MACOSX) static rlim_t initial_fd_limit = GetFileDescriptorLimit(); fd_limit_ = initial_fd_limit; @@ -368,6 +370,9 @@ class PageCyclerExtensionTest : public PageCyclerTest { } }; +class PageCyclerExtensionWebRequestTest : public PageCyclerExtensionTest { +}; + static FilePath GetDatabaseDataPath(const char* name) { FilePath test_path; PathService::Get(base::DIR_SOURCE_ROOT, &test_path); @@ -550,11 +555,21 @@ TEST_F(PageCyclerExtensionTest, name##10) { \ RunTest("times", "content_scripts10", "_extcs10", test, false); \ } +// This macro lets us define tests with an extension that listens to the +// webrequest.onBeforeRequest. It measures the effect that a blocking event +// for every request has on page cycle time. +#define PAGE_CYCLER_EXTENSIONS_WEBREQUEST_FILE_TESTS(test, name) \ +TEST_F(PageCyclerExtensionWebRequestTest, name) { \ + RunTest("times", "extension_webrequest", "_extwr", test, false); \ +} + // file-URL tests PAGE_CYCLER_FILE_TESTS("moz", MozFile); PAGE_CYCLER_EXTENSIONS_FILE_TESTS("moz", MozFile); +PAGE_CYCLER_EXTENSIONS_WEBREQUEST_FILE_TESTS("moz", MozFile) PAGE_CYCLER_FILE_TESTS("intl1", Intl1File); PAGE_CYCLER_FILE_TESTS("intl2", Intl2File); +PAGE_CYCLER_EXTENSIONS_WEBREQUEST_FILE_TESTS("intl2", Intl2File); PAGE_CYCLER_FILE_TESTS("dom", DomFile); PAGE_CYCLER_FILE_TESTS("dhtml", DhtmlFile); PAGE_CYCLER_FILE_TESTS("morejs", MorejsFile); diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h index ec72cb4..fa96247 100644 --- a/net/base/net_log_event_type_list.h +++ b/net/base/net_log_event_type_list.h @@ -497,6 +497,10 @@ EVENT_TYPE(URL_REQUEST_START_JOB) // } EVENT_TYPE(URL_REQUEST_REDIRECTED) +// Measures the time a net::URLRequest is blocked waiting for an extension to +// respond to the onBefoteRequest extension event. +EVENT_TYPE(URL_REQUEST_BLOCKED_ON_EXTENSION) + // ------------------------------------------------------------------------ // HttpCache // ------------------------------------------------------------------------ diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc index b00e671..b7ecd44 100644 --- a/net/base/network_delegate.cc +++ b/net/base/network_delegate.cc @@ -8,10 +8,12 @@ namespace net { -void NetworkDelegate::NotifyBeforeURLRequest(URLRequest* request) { +bool NetworkDelegate::NotifyBeforeURLRequest(URLRequest* request, + CompletionCallback* callback) { DCHECK(CalledOnValidThread()); DCHECK(request); - OnBeforeURLRequest(request); + DCHECK(callback); + return OnBeforeURLRequest(request, callback); } void NetworkDelegate::NotifySendHttpRequest(HttpRequestHeaders* headers) { diff --git a/net/base/network_delegate.h b/net/base/network_delegate.h index c4f2b65..7ffa1c3 100644 --- a/net/base/network_delegate.h +++ b/net/base/network_delegate.h @@ -7,6 +7,7 @@ #pragma once #include "base/threading/non_thread_safe.h" +#include "net/base/completion_callback.h" namespace net { @@ -30,7 +31,8 @@ class NetworkDelegate : public base::NonThreadSafe { // Notification interface called by the network stack. Note that these // functions mostly forward to the private virtuals. They also add some sanity // checking on parameters. - void NotifyBeforeURLRequest(URLRequest* request); + bool NotifyBeforeURLRequest(URLRequest* request, + CompletionCallback* callback); void NotifySendHttpRequest(HttpRequestHeaders* headers); void NotifyResponseStarted(URLRequest* request); void NotifyReadCompleted(URLRequest* request, int bytes_read); @@ -41,7 +43,8 @@ class NetworkDelegate : public base::NonThreadSafe { // member function, which will perform basic sanity checking. // Called before a request is sent. - virtual void OnBeforeURLRequest(URLRequest* request) = 0; + virtual bool OnBeforeURLRequest(URLRequest* request, + CompletionCallback* callback) = 0; // Called right before the HTTP headers are sent. Allows the delegate to // read/write |headers| before they get sent out. diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc index 5af99ea..229611ca 100644 --- a/net/url_request/url_request.cc +++ b/net/url_request/url_request.cc @@ -19,6 +19,7 @@ #include "net/http/http_response_headers.h" #include "net/http/http_util.h" #include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_error_job.h" #include "net/url_request/url_request_job.h" #include "net/url_request/url_request_job_manager.h" #include "net/url_request/url_request_netlog_params.h" @@ -131,6 +132,9 @@ URLRequest::URLRequest(const GURL& url, Delegate* delegate) } URLRequest::~URLRequest() { + if (before_request_callback_) + before_request_callback_->Cancel(); + Cancel(); if (job_) @@ -354,6 +358,19 @@ GURL URLRequest::GetSanitizedReferrer() const { } void URLRequest::Start() { + response_info_.request_time = Time::Now(); + + if (context_ && context_->network_delegate()) { + before_request_callback_ = new CancelableCompletionCallback<URLRequest>( + this, &URLRequest::BeforeRequestComplete); + before_request_callback_->AddRef(); // balanced in BeforeRequestComplete + if (context_->network_delegate()->NotifyBeforeURLRequest( + this, before_request_callback_)) { + net_log_.BeginEvent(NetLog::TYPE_URL_REQUEST_BLOCKED_ON_EXTENSION, NULL); + return; // paused + } + } + StartJob(URLRequestJobManager::GetInstance()->CreateJob(this)); } @@ -363,11 +380,6 @@ void URLRequest::StartJob(URLRequestJob* job) { DCHECK(!is_pending_); DCHECK(!job_); - // TODO(mpcomplete): pass in request ID? - // TODO(mpcomplete): allow delegate to potentially delay/cancel request. - if (context_ && context_->network_delegate()) - context_->network_delegate()->NotifyBeforeURLRequest(this); - net_log_.BeginEvent( net::NetLog::TYPE_URL_REQUEST_START_JOB, make_scoped_refptr(new URLRequestStartEventParameters( @@ -381,7 +393,6 @@ void URLRequest::StartJob(URLRequestJob* job) { is_pending_ = true; - response_info_.request_time = Time::Now(); response_info_.was_cached = false; // Don't allow errors to be sent from within Start(). @@ -391,6 +402,18 @@ void URLRequest::StartJob(URLRequestJob* job) { job_->Start(); } +void URLRequest::BeforeRequestComplete(int error) { + DCHECK(!job_); + + net_log_.EndEvent(NetLog::TYPE_URL_REQUEST_BLOCKED_ON_EXTENSION, NULL); + before_request_callback_->Release(); // balanced in Start + if (error != net::OK) { + StartJob(new URLRequestErrorJob(this, error)); + } else { + StartJob(URLRequestJobManager::GetInstance()->CreateJob(this)); + } +} + void URLRequest::Restart() { // Should only be called if the original job didn't make any progress. DCHECK(job_ && !job_->has_response_started()); @@ -539,6 +562,7 @@ void URLRequest::PrepareToRestart() { OrphanJob(); response_info_ = net::HttpResponseInfo(); + response_info_.request_time = Time::Now(); status_ = URLRequestStatus(); is_pending_ = false; } diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h index 849a03b..72ab4d1 100644 --- a/net/url_request/url_request.h +++ b/net/url_request/url_request.h @@ -17,6 +17,7 @@ #include "base/string16.h" #include "base/threading/non_thread_safe.h" #include "googleurl/src/gurl.h" +#include "net/base/completion_callback.h" #include "net/base/load_states.h" #include "net/base/net_log.h" #include "net/base/request_priority.h" @@ -607,6 +608,13 @@ class URLRequest : public base::NonThreadSafe { // passed values. void DoCancel(int os_error, const net::SSLInfo& ssl_info); + // Resumes or blocks a request paused by the NetworkDelegate::OnBeforeRequest + // handler. If |blocked| is true, the request is blocked and an error page is + // returned indicating so. This should only be called after Start is called + // and OnBeforeRequest returns true (signalling that the request should be + // paused). + void BeforeRequestComplete(int error); + // Contextual information used for this request (can be NULL). This contains // most of the dependencies which are shared between requests (disk cache, // cookie store, socket pool, etc.) @@ -664,6 +672,11 @@ class URLRequest : public base::NonThreadSafe { base::debug::LeakTracker<URLRequest> leak_tracker_; + // Callback passed to the network delegate to notify us when a blocked request + // is ready to be resumed or canceled. + scoped_refptr< CancelableCompletionCallback<URLRequest> > + before_request_callback_; + DISALLOW_COPY_AND_ASSIGN(URLRequest); }; diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc index 5e76729..8fd7cab 100644 --- a/net/url_request/url_request_test_util.cc +++ b/net/url_request/url_request_test_util.cc @@ -296,7 +296,9 @@ TestNetworkDelegate::TestNetworkDelegate() TestNetworkDelegate::~TestNetworkDelegate() {} -void TestNetworkDelegate::OnBeforeURLRequest(net::URLRequest* request) { +bool TestNetworkDelegate::OnBeforeURLRequest( + net::URLRequest* request, net::CompletionCallback* callback) { + return false; } void TestNetworkDelegate::OnSendHttpRequest( diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h index 47ecff4..d166f9e 100644 --- a/net/url_request/url_request_test_util.h +++ b/net/url_request/url_request_test_util.h @@ -201,7 +201,8 @@ class TestNetworkDelegate : public net::NetworkDelegate { private: // net::NetworkDelegate: - virtual void OnBeforeURLRequest(net::URLRequest* request); + virtual bool OnBeforeURLRequest(net::URLRequest* request, + net::CompletionCallback* callback); virtual void OnSendHttpRequest(net::HttpRequestHeaders* headers); virtual void OnResponseStarted(net::URLRequest* request); virtual void OnReadCompleted(net::URLRequest* request, int bytes_read); diff --git a/third_party/skia/LICENSE b/third_party/skia/LICENSE deleted file mode 100644 index d645695..0000000 --- a/third_party/skia/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/third_party/skia/README.chromium b/third_party/skia/README.chromium deleted file mode 100644 index ea65a69..0000000 --- a/third_party/skia/README.chromium +++ /dev/null @@ -1,2 +0,0 @@ -Name: skia -URL: http://code.google.com/p/skia |