summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions
diff options
context:
space:
mode:
authormpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-15 01:19:26 +0000
committermpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-15 01:19:26 +0000
commitc41fe6697e38057138cbe33332d9882e0d7d9b4b (patch)
tree1b7791940ff36d4a2fd3f43065b857fdaa14b4b4 /chrome/browser/extensions
parenta9846c8d20c3658aadacee6127fc734dc3488b25 (diff)
downloadchromium_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')
-rw-r--r--chrome/browser/extensions/extension_event_router.cc8
-rw-r--r--chrome/browser/extensions/extension_function_dispatcher.cc4
-rw-r--r--chrome/browser/extensions/extension_io_event_router.cc16
-rw-r--r--chrome/browser/extensions/extension_io_event_router.h12
-rw-r--r--chrome/browser/extensions/extension_webrequest_api.cc285
-rw-r--r--chrome/browser/extensions/extension_webrequest_api.h56
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_