diff options
author | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-15 01:19:26 +0000 |
---|---|---|
committer | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-15 01:19:26 +0000 |
commit | c41fe6697e38057138cbe33332d9882e0d7d9b4b (patch) | |
tree | 1b7791940ff36d4a2fd3f43065b857fdaa14b4b4 /chrome/browser/extensions | |
parent | a9846c8d20c3658aadacee6127fc734dc3488b25 (diff) | |
download | chromium_src-c41fe6697e38057138cbe33332d9882e0d7d9b4b.zip chromium_src-c41fe6697e38057138cbe33332d9882e0d7d9b4b.tar.gz chromium_src-c41fe6697e38057138cbe33332d9882e0d7d9b4b.tar.bz2 |
Retrying r74887: Add plumbing to webRequest API to support event filtering.
This introduces a new kind of event (WebRequestEvent) to handle the extra parameters to addListener. Behind the scenes, adding a listener creates a unique sub-event associated with that set of filter+extraInfo parameters. When we dispatch the event, we dispatch only those sub-events that match the required filters.
Original CL: http://codereview.chromium.org/6250152/
This time I fixed the linux compile.
BUG=60101
TEST=no
Review URL: http://codereview.chromium.org/6528005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@74902 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions')
6 files changed, 364 insertions, 17 deletions
diff --git a/chrome/browser/extensions/extension_event_router.cc b/chrome/browser/extensions/extension_event_router.cc index 0b4f799..13532d7 100644 --- a/chrome/browser/extensions/extension_event_router.cc +++ b/chrome/browser/extensions/extension_event_router.cc @@ -9,8 +9,9 @@ #include "chrome/browser/extensions/extension_devtools_manager.h" #include "chrome/browser/extensions/extension_processes_api.h" #include "chrome/browser/extensions/extension_processes_api_constants.h" -#include "chrome/browser/extensions/extension_tabs_module.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_tabs_module.h" +#include "chrome/browser/extensions/extension_webrequest_api.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/common/extensions/extension.h" @@ -108,6 +109,8 @@ void ExtensionEventRouter::RemoveEventListener( " PID=" << process->id() << " extension=" << extension_id << " event=" << event_name; listeners_[event_name].erase(listener); + // Note: extension_id may point to data in the now-deleted listeners_ object. + // Do not use. if (extension_devtools_manager_.get()) extension_devtools_manager_->RemoveEventListener(event_name, process->id()); @@ -116,6 +119,9 @@ void ExtensionEventRouter::RemoveEventListener( // exits), then we let the TaskManager know that it has one fewer listener. if (event_name.compare(extension_processes_api_constants::kOnUpdated) == 0) ExtensionProcessesEventRouter::GetInstance()->ListenerRemoved(); + + ExtensionWebRequestEventRouter::RemoveEventListenerOnUIThread( + 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 7507656..3d6a965 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -44,6 +44,7 @@ #include "chrome/browser/extensions/extension_test_api.h" #include "chrome/browser/extensions/extension_tts_api.h" #include "chrome/browser/extensions/extension_web_ui.h" +#include "chrome/browser/extensions/extension_webrequest_api.h" #include "chrome/browser/extensions/extension_webstore_private_api.h" #include "chrome/browser/extensions/extensions_quota_service.h" #include "chrome/browser/extensions/extension_service.h" @@ -294,6 +295,9 @@ void FactoryRegistry::ResetFunctions() { RegisterFunction<PromptBrowserLoginFunction>(); RegisterFunction<BeginInstallFunction>(); RegisterFunction<CompleteInstallFunction>(); + + // WebRequest. + RegisterFunction<WebRequestAddEventListener>(); } void FactoryRegistry::GetAllNames(std::vector<std::string>* names) { diff --git a/chrome/browser/extensions/extension_io_event_router.cc b/chrome/browser/extensions/extension_io_event_router.cc index 64ddfc0..5beb4b8 100644 --- a/chrome/browser/extensions/extension_io_event_router.cc +++ b/chrome/browser/extensions/extension_io_event_router.cc @@ -16,17 +16,21 @@ ExtensionIOEventRouter::ExtensionIOEventRouter(Profile* profile) ExtensionIOEventRouter::~ExtensionIOEventRouter() { } -void ExtensionIOEventRouter::DispatchEvent( - const std::string& event_name, const std::string& event_args) const { +void ExtensionIOEventRouter::DispatchEventToExtension( + const std::string& extension_id, + const std::string& event_name, + const std::string& event_args) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(this, &ExtensionIOEventRouter::DispatchEventOnUIThread, - event_name, event_args)); + extension_id, event_name, event_args)); } void ExtensionIOEventRouter::DispatchEventOnUIThread( - const std::string& event_name, const std::string& event_args) const { + const std::string& extension_id, + const std::string& event_name, + const std::string& event_args) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // If the profile has gone away, we're shutting down. If there's no event @@ -34,6 +38,6 @@ void ExtensionIOEventRouter::DispatchEventOnUIThread( if (!profile_ || !profile_->GetExtensionEventRouter()) return; - profile_->GetExtensionEventRouter()->DispatchEventToRenderers( - event_name, event_args, profile_, GURL()); + profile_->GetExtensionEventRouter()->DispatchEventToExtension( + extension_id, event_name, event_args, profile_, GURL()); } diff --git a/chrome/browser/extensions/extension_io_event_router.h b/chrome/browser/extensions/extension_io_event_router.h index 1a27890..eae207b 100644 --- a/chrome/browser/extensions/extension_io_event_router.h +++ b/chrome/browser/extensions/extension_io_event_router.h @@ -25,15 +25,17 @@ class ExtensionIOEventRouter void DestroyingProfile() { profile_ = NULL; } // Dispatch the named event to every extension listening to that event. - void DispatchEvent(const std::string& event_name, - const std::string& event_args) const; + void DispatchEventToExtension(const std::string& extension_id, + const std::string& event_name, + const std::string& event_args) const; private: - Profile* profile_; - - void DispatchEventOnUIThread(const std::string& event_name, + void DispatchEventOnUIThread(const std::string& extension_id, + const std::string& event_name, const std::string& event_args) const; + Profile* profile_; + DISALLOW_COPY_AND_ASSIGN(ExtensionIOEventRouter); }; diff --git a/chrome/browser/extensions/extension_webrequest_api.cc b/chrome/browser/extensions/extension_webrequest_api.cc index 3ca3fca..8653e1d 100644 --- a/chrome/browser/extensions/extension_webrequest_api.cc +++ b/chrome/browser/extensions/extension_webrequest_api.cc @@ -4,14 +4,189 @@ #include "chrome/browser/extensions/extension_webrequest_api.h" +#include <algorithm> + #include "base/json/json_writer.h" #include "base/values.h" +#include "chrome/browser/browser_thread.h" #include "chrome/browser/extensions/extension_io_event_router.h" #include "chrome/browser/extensions/extension_webrequest_api_constants.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_extent.h" +#include "chrome/common/extensions/url_pattern.h" #include "googleurl/src/gurl.h" namespace keys = extension_webrequest_api_constants; +namespace { + +// List of all the webRequest events. +static const char *kWebRequestEvents[] = { + keys::kOnBeforeRedirect, + keys::kOnBeforeRequest, + keys::kOnCompleted, + keys::kOnErrorOccurred, + keys::kOnHeadersReceived, + keys::kOnRequestSent +}; + +// TODO(mpcomplete): should this be a set of flags? +static const char* kRequestFilterTypes[] = { + "main_frame", + "sub_frame", + "stylesheet", + "script", + "image", + "object", + "other", +}; + +static bool IsWebRequestEvent(const std::string& event_name) { + return std::find(kWebRequestEvents, + kWebRequestEvents + arraysize(kWebRequestEvents), + event_name) != + kWebRequestEvents + arraysize(kWebRequestEvents); +} + +static bool IsValidRequestFilterType(const std::string& type) { + return std::find(kRequestFilterTypes, + kRequestFilterTypes + arraysize(kRequestFilterTypes), + type) != + kRequestFilterTypes + arraysize(kRequestFilterTypes); +} + +static void AddEventListenerOnIOThread( + 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); +} + +static void RemoveEventListenerOnIOThread( + const std::string& extension_id, const std::string& sub_event_name) { + ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener( + extension_id, sub_event_name); +} + +} // 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<std::string> types; + int tab_id; + int window_id; + + bool InitFromValue(const DictionaryValue& value); +}; + +// 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, + REDIRECT_REQUEST_LINE = 1<<4, + REDIRECT_REQUEST_HEADERS = 1<<5, + }; + + 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. +struct ExtensionWebRequestEventRouter::EventListener { + std::string extension_id; + std::string sub_event_name; + RequestFilter filter; + int extra_info_spec; + + // Comparator to work with std::set. + bool operator<(const EventListener& that) const { + if (extension_id < that.extension_id) + return true; + if (extension_id == that.extension_id && + sub_event_name < that.sub_event_name) + return true; + return false; + } +}; + +bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue( + const DictionaryValue& value) { + for (DictionaryValue::key_iterator key = value.begin_keys(); + key != value.end_keys(); ++key) { + if (*key == "urls") { + ListValue* urls_value = NULL; + if (!value.GetList("urls", &urls_value)) + return false; + for (size_t i = 0; i < urls_value->GetSize(); ++i) { + std::string url; + URLPattern pattern(URLPattern::SCHEME_ALL); + if (!urls_value->GetString(i, &url) || + pattern.Parse(url) != URLPattern::PARSE_SUCCESS) + return false; + urls.AddPattern(pattern); + } + } else if (*key == "types") { + ListValue* types_value = NULL; + if (!value.GetList("urls", &types_value)) + return false; + for (size_t i = 0; i < types_value->GetSize(); ++i) { + std::string type; + if (!types_value->GetString(i, &type) || + !IsValidRequestFilterType(type)) + return false; + types.push_back(type); + } + } else if (*key == "tabId") { + if (!value.GetInteger("tabId", &tab_id)) + return false; + } else if (*key == "windowId") { + if (!value.GetInteger("windowId", &window_id)) + return false; + } else { + return false; + } + } + return true; +} + +// static +bool ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( + const ListValue& value, int* extra_info_spec) { + *extra_info_spec = 0; + for (size_t i = 0; i < value.GetSize(); ++i) { + std::string str; + if (!value.GetString(i, &str)) + return false; + + // TODO(mpcomplete): not all of these are valid for every event. + if (str == "requestLine") + *extra_info_spec |= REQUEST_LINE; + else if (str == "requestHeaders") + *extra_info_spec |= REQUEST_HEADERS; + else if (str == "statusLine") + *extra_info_spec |= STATUS_LINE; + else if (str == "responseHeaders") + *extra_info_spec |= RESPONSE_HEADERS; + else if (str == "redirectRequestLine") + *extra_info_spec |= REDIRECT_REQUEST_LINE; + else if (str == "redirectRequestHeaders") + *extra_info_spec |= REDIRECT_REQUEST_HEADERS; + else + return false; + } + return true; +} + // static ExtensionWebRequestEventRouter* ExtensionWebRequestEventRouter::GetInstance() { return Singleton<ExtensionWebRequestEventRouter>::get(); @@ -23,9 +198,25 @@ 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( - const ExtensionIOEventRouter* event_router, const GURL& url, + const ExtensionIOEventRouter* event_router, + const GURL& url, const std::string& method) { + std::vector<const EventListener*> listeners = + GetMatchingListeners(keys::kOnBeforeRequest, url); + if (listeners.empty()) + return; + ListValue args; DictionaryValue* dict = new DictionaryValue(); dict->SetString(keys::kUrlKey, url.spec()); @@ -39,5 +230,95 @@ void ExtensionWebRequestEventRouter::OnBeforeRequest( std::string json_args; base::JSONWriter::Write(&args, false, &json_args); - event_router->DispatchEvent(keys::kOnBeforeRequest, json_args); + + 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); + } +} + +void ExtensionWebRequestEventRouter::AddEventListener( + const std::string& extension_id, + const std::string& event_name, + const std::string& sub_event_name, + const RequestFilter& filter, + int extra_info_spec) { + if (!IsWebRequestEvent(event_name)) + return; + + EventListener listener; + listener.extension_id = extension_id; + listener.sub_event_name = sub_event_name; + listener.filter = filter; + listener.extra_info_spec = extra_info_spec; + + CHECK_EQ(listeners_[event_name].count(listener), 0u) << + "extension=" << extension_id << " event=" << event_name; + listeners_[event_name].insert(listener); +} + +void ExtensionWebRequestEventRouter::RemoveEventListener( + const std::string& extension_id, + const std::string& sub_event_name) { + size_t slash_sep = sub_event_name.find('/'); + std::string event_name = sub_event_name.substr(0, slash_sep); + + if (!IsWebRequestEvent(event_name)) + return; + + EventListener listener; + listener.extension_id = extension_id; + listener.sub_event_name = sub_event_name; + + CHECK_EQ(listeners_[event_name].count(listener), 1u) << + "extension=" << extension_id << " event=" << event_name; + listeners_[event_name].erase(listener); +} + +std::vector<const ExtensionWebRequestEventRouter::EventListener*> +ExtensionWebRequestEventRouter::GetMatchingListeners( + const std::string& event_name, const GURL& url) { + std::vector<const EventListener*> matching_listeners; + std::set<EventListener>& listeners = listeners_[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)) + matching_listeners.push_back(&(*it)); + } + return matching_listeners; +} + +bool WebRequestAddEventListener::RunImpl() { + // Argument 0 is the callback, which we don't use here. + + ExtensionWebRequestEventRouter::RequestFilter filter; + if (HasOptionalArgument(1)) { + DictionaryValue* value = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &value)); + EXTENSION_FUNCTION_VALIDATE(filter.InitFromValue(*value)); + } + + int extra_info_spec = 0; + if (HasOptionalArgument(2)) { + ListValue* value = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetList(2, &value)); + EXTENSION_FUNCTION_VALIDATE( + ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( + *value, &extra_info_spec)); + } + + std::string event_name; + EXTENSION_FUNCTION_VALIDATE(args_->GetString(3, &event_name)); + + std::string sub_event_name; + EXTENSION_FUNCTION_VALIDATE(args_->GetString(4, &sub_event_name)); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableFunction( + &AddEventListenerOnIOThread, + extension_id(), event_name, sub_event_name, filter, extra_info_spec)); + + return true; } diff --git a/chrome/browser/extensions/extension_webrequest_api.h b/chrome/browser/extensions/extension_webrequest_api.h index 1945627..27fb5cb 100644 --- a/chrome/browser/extensions/extension_webrequest_api.h +++ b/chrome/browser/extensions/extension_webrequest_api.h @@ -6,30 +6,80 @@ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_WEBREQUEST_API_H_ #pragma once +#include <map> +#include <set> #include <string> +#include <vector> #include "base/singleton.h" +#include "ipc/ipc_message.h" +#include "chrome/browser/extensions/extension_function.h" class ExtensionIOEventRouter; class GURL; -// IO thread +// 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. class ExtensionWebRequestEventRouter { public: + struct RequestFilter; + struct ExtraInfoSpec; + 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); + // TODO(mpcomplete): additional params - void OnBeforeRequest(const ExtensionIOEventRouter* event_router, - const GURL& url, const std::string& method); + void OnBeforeRequest( + const ExtensionIOEventRouter* event_router, + const GURL& url, + const std::string& method); + + // 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( + const std::string& extension_id, + const std::string& event_name, + const std::string& sub_event_name, + const RequestFilter& filter, + int extra_info_spec); + + // Removes the listener for the given sub-event. + void RemoveEventListener( + 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; ExtensionWebRequestEventRouter(); ~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); + + // A map between an event name and a set of extensions that are listening + // to that event. + ListenerMap listeners_; + DISALLOW_COPY_AND_ASSIGN(ExtensionWebRequestEventRouter); }; +class WebRequestAddEventListener : public SyncExtensionFunction { + public: + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("experimental.webRequest.addEventListener"); +}; #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_WEBREQUEST_API_H_ |