summaryrefslogtreecommitdiffstats
path: root/extensions/common
diff options
context:
space:
mode:
authorMHX348@motorola.com <MHX348@motorola.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-12 04:45:58 +0000
committerMHX348@motorola.com <MHX348@motorola.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-12 04:45:58 +0000
commit4b3e1928facf6e98d4a386601c6a568bcab69372 (patch)
treefe2f8f5e5a0beec65e60f161905d7d246debfaa8 /extensions/common
parent071d96b541fbc0370354a9fae78e5e87fdbe542d (diff)
downloadchromium_src-4b3e1928facf6e98d4a386601c6a568bcab69372.zip
chromium_src-4b3e1928facf6e98d4a386601c6a568bcab69372.tar.gz
chromium_src-4b3e1928facf6e98d4a386601c6a568bcab69372.tar.bz2
Move EventFilter, EventFilteringInfo and EventMatcher
classes to extensions/common/ TBR=ben@chromium.org BUG=162530 Review URL: https://chromiumcodereview.appspot.com/12208078 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@181849 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions/common')
-rw-r--r--extensions/common/event_filter.cc172
-rw-r--r--extensions/common/event_filter.h126
-rw-r--r--extensions/common/event_filter_unittest.cc244
-rw-r--r--extensions/common/event_filtering_info.cc48
-rw-r--r--extensions/common/event_filtering_info.h47
-rw-r--r--extensions/common/event_matcher.cc47
-rw-r--r--extensions/common/event_matcher.h50
7 files changed, 734 insertions, 0 deletions
diff --git a/extensions/common/event_filter.cc b/extensions/common/event_filter.cc
new file mode 100644
index 0000000..e5d6b1d
--- /dev/null
+++ b/extensions/common/event_filter.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/event_filter.h"
+
+#include "extensions/common/matcher/url_matcher_factory.h"
+
+namespace extensions {
+
+EventFilter::EventMatcherEntry::EventMatcherEntry(
+ scoped_ptr<EventMatcher> event_matcher,
+ URLMatcher* url_matcher,
+ const URLMatcherConditionSet::Vector& condition_sets)
+ : event_matcher_(event_matcher.Pass()),
+ url_matcher_(url_matcher) {
+ for (URLMatcherConditionSet::Vector::const_iterator it =
+ condition_sets.begin(); it != condition_sets.end(); it++)
+ condition_set_ids_.push_back((*it)->id());
+ url_matcher_->AddConditionSets(condition_sets);
+}
+
+EventFilter::EventMatcherEntry::~EventMatcherEntry() {
+ url_matcher_->RemoveConditionSets(condition_set_ids_);
+}
+
+void EventFilter::EventMatcherEntry::DontRemoveConditionSetsInDestructor() {
+ condition_set_ids_.clear();
+}
+
+EventFilter::EventFilter()
+ : next_id_(0),
+ next_condition_set_id_(0) {
+}
+
+EventFilter::~EventFilter() {
+ // Normally when an event matcher entry is removed from event_matchers_ it
+ // will remove its condition sets from url_matcher_, but as url_matcher_ is
+ // being destroyed anyway there is no need to do that step here.
+ for (EventMatcherMultiMap::iterator it = event_matchers_.begin();
+ it != event_matchers_.end(); it++) {
+ for (EventMatcherMap::iterator it2 = it->second.begin();
+ it2 != it->second.end(); it2++) {
+ it2->second->DontRemoveConditionSetsInDestructor();
+ }
+ }
+}
+
+EventFilter::MatcherID
+EventFilter::AddEventMatcher(const std::string& event_name,
+ scoped_ptr<EventMatcher> matcher) {
+ MatcherID id = next_id_++;
+ URLMatcherConditionSet::Vector condition_sets;
+ if (!CreateConditionSets(id, matcher.get(), &condition_sets))
+ return -1;
+
+ for (URLMatcherConditionSet::Vector::iterator it = condition_sets.begin();
+ it != condition_sets.end(); it++) {
+ condition_set_id_to_event_matcher_id_.insert(
+ std::make_pair((*it)->id(), id));
+ }
+ id_to_event_name_[id] = event_name;
+ event_matchers_[event_name][id] = linked_ptr<EventMatcherEntry>(
+ new EventMatcherEntry(matcher.Pass(), &url_matcher_, condition_sets));
+ return id;
+}
+
+EventMatcher* EventFilter::GetEventMatcher(MatcherID id) {
+ DCHECK(id_to_event_name_.find(id) != id_to_event_name_.end());
+ const std::string& event_name = id_to_event_name_[id];
+ return event_matchers_[event_name][id]->event_matcher();
+}
+
+const std::string& EventFilter::GetEventName(MatcherID id) {
+ DCHECK(id_to_event_name_.find(id) != id_to_event_name_.end());
+ return id_to_event_name_[id];
+}
+
+bool EventFilter::CreateConditionSets(
+ MatcherID id,
+ EventMatcher* matcher,
+ URLMatcherConditionSet::Vector* condition_sets) {
+ if (matcher->GetURLFilterCount() == 0) {
+ // If there are no URL filters then we want to match all events, so create a
+ // URLFilter from an empty dictionary.
+ base::DictionaryValue empty_dict;
+ return AddDictionaryAsConditionSet(&empty_dict, condition_sets);
+ }
+ for (int i = 0; i < matcher->GetURLFilterCount(); i++) {
+ base::DictionaryValue* url_filter;
+ if (!matcher->GetURLFilter(i, &url_filter))
+ return false;
+ if (!AddDictionaryAsConditionSet(url_filter, condition_sets))
+ return false;
+ }
+ return true;
+}
+
+bool EventFilter::AddDictionaryAsConditionSet(
+ base::DictionaryValue* url_filter,
+ URLMatcherConditionSet::Vector* condition_sets) {
+ std::string error;
+ URLMatcherConditionSet::ID condition_set_id = next_condition_set_id_++;
+ condition_sets->push_back(URLMatcherFactory::CreateFromURLFilterDictionary(
+ url_matcher_.condition_factory(),
+ url_filter,
+ condition_set_id,
+ &error));
+ if (!error.empty()) {
+ LOG(ERROR) << "CreateFromURLFilterDictionary failed: " << error;
+ url_matcher_.ClearUnusedConditionSets();
+ condition_sets->clear();
+ return false;
+ }
+ return true;
+}
+
+std::string EventFilter::RemoveEventMatcher(MatcherID id) {
+ std::map<MatcherID, std::string>::iterator it = id_to_event_name_.find(id);
+ std::string event_name = it->second;
+ // EventMatcherEntry's destructor causes the condition set ids to be removed
+ // from url_matcher_.
+ event_matchers_[event_name].erase(id);
+ id_to_event_name_.erase(it);
+ return event_name;
+}
+
+std::set<EventFilter::MatcherID> EventFilter::MatchEvent(
+ const std::string& event_name, const EventFilteringInfo& event_info) {
+ std::set<MatcherID> matchers;
+ EventMatcherMultiMap::iterator it = event_matchers_.find(event_name);
+ if (it == event_matchers_.end())
+ return matchers;
+
+ EventMatcherMap& matcher_map = it->second;
+ GURL url_to_match_against = event_info.has_url() ? event_info.url() : GURL();
+ std::set<URLMatcherConditionSet::ID> matching_condition_set_ids =
+ url_matcher_.MatchURL(url_to_match_against);
+ for (std::set<URLMatcherConditionSet::ID>::iterator it =
+ matching_condition_set_ids.begin();
+ it != matching_condition_set_ids.end(); it++) {
+ std::map<URLMatcherConditionSet::ID, MatcherID>::iterator matcher_id =
+ condition_set_id_to_event_matcher_id_.find(*it);
+ if (matcher_id == condition_set_id_to_event_matcher_id_.end()) {
+ NOTREACHED() << "id not found in condition set map (" << (*it) << ")";
+ continue;
+ }
+ MatcherID id = matcher_id->second;
+ EventMatcherMap::iterator matcher_entry = matcher_map.find(id);
+ if (matcher_entry == matcher_map.end()) {
+ // Matcher must be for a different event.
+ continue;
+ }
+ const EventMatcher* event_matcher = matcher_entry->second->event_matcher();
+ if (event_matcher->MatchNonURLCriteria(event_info)) {
+ CHECK(!event_matcher->HasURLFilters() || event_info.has_url());
+ matchers.insert(id);
+ }
+ }
+
+ return matchers;
+}
+
+int EventFilter::GetMatcherCountForEvent(const std::string& name) {
+ EventMatcherMultiMap::const_iterator it = event_matchers_.find(name);
+ if (it == event_matchers_.end())
+ return 0;
+
+ return it->second.size();
+}
+
+} // namespace extensions
diff --git a/extensions/common/event_filter.h b/extensions/common/event_filter.h
new file mode 100644
index 0000000..efde6035
--- /dev/null
+++ b/extensions/common/event_filter.h
@@ -0,0 +1,126 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_EVENT_FILTER_H_
+#define EXTENSIONS_COMMON_EVENT_FILTER_H_
+
+#include <map>
+#include <set>
+
+#include "base/memory/linked_ptr.h"
+#include "extensions/common/event_filtering_info.h"
+#include "extensions/common/event_matcher.h"
+#include "extensions/common/matcher/url_matcher.h"
+
+namespace extensions {
+
+// Matches incoming events against a collection of EventMatchers. Each added
+// EventMatcher is given an id which is returned by MatchEvent() when it is
+// passed a matching event.
+class EventFilter {
+ public:
+ typedef int MatcherID;
+ EventFilter();
+ ~EventFilter();
+
+ // Adds an event matcher that will be used in calls to MatchEvent(). Returns
+ // the id of the matcher, or -1 if there was an error.
+ MatcherID AddEventMatcher(const std::string& event_name,
+ scoped_ptr<EventMatcher> matcher);
+
+ // Retrieve the EventMatcher with the given id.
+ EventMatcher* GetEventMatcher(MatcherID id);
+
+ // Retrieve the name of the event that the EventMatcher specified by |id| is
+ // referring to.
+ const std::string& GetEventName(MatcherID id);
+
+ // Removes an event matcher, returning the name of the event that it was for.
+ std::string RemoveEventMatcher(MatcherID id);
+
+ // Match an event named |event_name| with filtering info |event_info| against
+ // our set of event matchers. Returns a set of ids that correspond to the
+ // event matchers that matched the event.
+ // TODO(koz): Add a std::string* parameter for retrieving error messages.
+ std::set<MatcherID> MatchEvent(const std::string& event_name,
+ const EventFilteringInfo& event_info);
+
+ int GetMatcherCountForEvent(const std::string& event_name);
+
+ // For testing.
+ bool IsURLMatcherEmpty() const {
+ return url_matcher_.IsEmpty();
+ }
+
+ private:
+ class EventMatcherEntry {
+ public:
+ // Adds |condition_sets| to |url_matcher| on construction and removes them
+ // again on destruction. |condition_sets| should be the
+ // URLMatcherConditionSets that match the URL constraints specified by
+ // |event_matcher|.
+ EventMatcherEntry(scoped_ptr<EventMatcher> event_matcher,
+ URLMatcher* url_matcher,
+ const URLMatcherConditionSet::Vector& condition_sets);
+ ~EventMatcherEntry();
+
+ // Prevents the removal of condition sets when this class is destroyed. We
+ // call this in EventFilter's destructor so that we don't do the costly
+ // removal of condition sets when the URLMatcher is going to be destroyed
+ // and clean them up anyway.
+ void DontRemoveConditionSetsInDestructor();
+
+ EventMatcher* event_matcher() {
+ return event_matcher_.get();
+ }
+
+ private:
+ scoped_ptr<EventMatcher> event_matcher_;
+ // The id sets in url_matcher_ that this EventMatcher owns.
+ std::vector<URLMatcherConditionSet::ID> condition_set_ids_;
+ URLMatcher* url_matcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventMatcherEntry);
+ };
+
+ // Maps from a matcher id to an event matcher entry.
+ typedef std::map<MatcherID, linked_ptr<EventMatcherEntry> > EventMatcherMap;
+
+ // Maps from event name to the map of matchers that are registered for it.
+ typedef std::map<std::string, EventMatcherMap> EventMatcherMultiMap;
+
+ // Adds the list of URL filters in |matcher| to the URL matcher, having
+ // matches for those URLs map to |id|.
+ bool CreateConditionSets(MatcherID id,
+ EventMatcher* matcher,
+ URLMatcherConditionSet::Vector* condition_sets);
+
+ bool AddDictionaryAsConditionSet(
+ base::DictionaryValue* url_filter,
+ URLMatcherConditionSet::Vector* condition_sets);
+
+ URLMatcher url_matcher_;
+ EventMatcherMultiMap event_matchers_;
+
+ // The next id to assign to an EventMatcher.
+ MatcherID next_id_;
+
+ // The next id to assign to a condition set passed to URLMatcher.
+ URLMatcherConditionSet::ID next_condition_set_id_;
+
+ // Maps condition set ids, which URLMatcher operates in, to event matcher
+ // ids, which the interface to this class operates in. As each EventFilter
+ // can specify many condition sets this is a many to one relationship.
+ std::map<URLMatcherConditionSet::ID, MatcherID>
+ condition_set_id_to_event_matcher_id_;
+
+ // Maps from event matcher ids to the name of the event they match on.
+ std::map<MatcherID, std::string> id_to_event_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventFilter);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_EVENT_FILTER_H_
diff --git a/extensions/common/event_filter_unittest.cc b/extensions/common/event_filter_unittest.cc
new file mode 100644
index 0000000..9b7f4ba
--- /dev/null
+++ b/extensions/common/event_filter_unittest.cc
@@ -0,0 +1,244 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "extensions/common/event_filter.h"
+#include "extensions/common/event_filtering_info.h"
+#include "extensions/common/event_matcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+class EventFilterUnittest : public testing::Test {
+ public:
+ EventFilterUnittest() {
+ google_event_.SetURL(GURL("http://google.com"));
+ yahoo_event_.SetURL(GURL("http://yahoo.com"));
+ random_url_event_.SetURL(GURL("http://www.something-else.com"));
+ empty_url_event_.SetURL(GURL());
+ }
+
+ protected:
+ scoped_ptr<base::Value> HostSuffixDict(const std::string& host_suffix) {
+ scoped_ptr<base::DictionaryValue> dict(new DictionaryValue());
+ dict->Set("hostSuffix", base::Value::CreateStringValue(host_suffix));
+ return scoped_ptr<base::Value>(dict.release());
+ }
+
+ scoped_ptr<base::ListValue> ValueAsList(scoped_ptr<base::Value> value) {
+ scoped_ptr<base::ListValue> result(new base::ListValue());
+ result->Append(value.release());
+ return result.Pass();
+ }
+
+ scoped_ptr<EventMatcher> AllURLs() {
+ return scoped_ptr<EventMatcher>(new EventMatcher(
+ scoped_ptr<DictionaryValue>(new DictionaryValue)));
+ }
+
+ scoped_ptr<EventMatcher> HostSuffixMatcher(const std::string& host_suffix) {
+ return MatcherFromURLFilterList(ValueAsList(HostSuffixDict(host_suffix)));
+ }
+
+ scoped_ptr<EventMatcher> MatcherFromURLFilterList(
+ scoped_ptr<ListValue> url_filter_list) {
+ scoped_ptr<DictionaryValue> filter_dict(new DictionaryValue);
+ filter_dict->Set("url", url_filter_list.release());
+ return scoped_ptr<EventMatcher>(new EventMatcher(filter_dict.Pass()));
+ }
+
+ EventFilter event_filter_;
+ EventFilteringInfo empty_event_;
+ EventFilteringInfo google_event_;
+ EventFilteringInfo yahoo_event_;
+ EventFilteringInfo random_url_event_;
+ EventFilteringInfo empty_url_event_;
+};
+
+TEST_F(EventFilterUnittest, NoMatchersMatchIfEmpty) {
+ std::set<int> matches = event_filter_.MatchEvent("some-event",
+ empty_event_);
+ ASSERT_EQ(0u, matches.size());
+}
+
+TEST_F(EventFilterUnittest, AddingEventMatcherDoesntCrash) {
+ event_filter_.AddEventMatcher("event1", AllURLs());
+}
+
+TEST_F(EventFilterUnittest,
+ DontMatchAgainstMatchersForDifferentEvents) {
+ event_filter_.AddEventMatcher("event1", AllURLs());
+ std::set<int> matches = event_filter_.MatchEvent("event2",
+ empty_event_);
+ ASSERT_EQ(0u, matches.size());
+}
+
+TEST_F(EventFilterUnittest, DoMatchAgainstMatchersForSameEvent) {
+ int id = event_filter_.AddEventMatcher("event1", AllURLs());
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+}
+
+TEST_F(EventFilterUnittest, DontMatchUnlessMatcherMatches) {
+ EventFilteringInfo info;
+ info.SetURL(GURL("http://www.yahoo.com"));
+ event_filter_.AddEventMatcher("event1", HostSuffixMatcher("google.com"));
+ std::set<int> matches = event_filter_.MatchEvent("event1", info);
+ ASSERT_TRUE(matches.empty());
+}
+
+TEST_F(EventFilterUnittest, RemovingAnEventMatcherStopsItMatching) {
+ int id = event_filter_.AddEventMatcher("event1", AllURLs());
+ event_filter_.RemoveEventMatcher(id);
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ empty_event_);
+ ASSERT_TRUE(matches.empty());
+}
+
+TEST_F(EventFilterUnittest, MultipleEventMatches) {
+ int id1 = event_filter_.AddEventMatcher("event1", AllURLs());
+ int id2 = event_filter_.AddEventMatcher("event1", AllURLs());
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_);
+ ASSERT_EQ(2u, matches.size());
+ ASSERT_EQ(1u, matches.count(id1));
+ ASSERT_EQ(1u, matches.count(id2));
+}
+
+TEST_F(EventFilterUnittest, TestURLMatching) {
+ EventFilteringInfo info;
+ info.SetURL(GURL("http://www.google.com"));
+ int id = event_filter_.AddEventMatcher("event1",
+ HostSuffixMatcher("google.com"));
+ std::set<int> matches = event_filter_.MatchEvent("event1", info);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+}
+
+TEST_F(EventFilterUnittest, TestMultipleURLFiltersMatchOnAny) {
+ scoped_ptr<base::ListValue> filters(new base::ListValue());
+ filters->Append(HostSuffixDict("google.com").release());
+ filters->Append(HostSuffixDict("yahoo.com").release());
+
+ scoped_ptr<EventMatcher> matcher(MatcherFromURLFilterList(filters.Pass()));
+ int id = event_filter_.AddEventMatcher("event1", matcher.Pass());
+
+ {
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+ }
+ {
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ yahoo_event_);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+ }
+ {
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ random_url_event_);
+ ASSERT_EQ(0u, matches.size());
+ }
+}
+
+TEST_F(EventFilterUnittest, TestStillMatchesAfterRemoval) {
+ int id1 = event_filter_.AddEventMatcher("event1", AllURLs());
+ int id2 = event_filter_.AddEventMatcher("event1", AllURLs());
+
+ event_filter_.RemoveEventMatcher(id1);
+ {
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id2));
+ }
+}
+
+TEST_F(EventFilterUnittest, TestMatchesOnlyAgainstPatternsForCorrectEvent) {
+ int id1 = event_filter_.AddEventMatcher("event1", AllURLs());
+ event_filter_.AddEventMatcher("event2", AllURLs());
+
+ {
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id1));
+ }
+}
+
+TEST_F(EventFilterUnittest, TestGetMatcherCountForEvent) {
+ ASSERT_EQ(0, event_filter_.GetMatcherCountForEvent("event1"));
+ int id1 = event_filter_.AddEventMatcher("event1", AllURLs());
+ ASSERT_EQ(1, event_filter_.GetMatcherCountForEvent("event1"));
+ int id2 = event_filter_.AddEventMatcher("event1", AllURLs());
+ ASSERT_EQ(2, event_filter_.GetMatcherCountForEvent("event1"));
+ event_filter_.RemoveEventMatcher(id1);
+ ASSERT_EQ(1, event_filter_.GetMatcherCountForEvent("event1"));
+ event_filter_.RemoveEventMatcher(id2);
+ ASSERT_EQ(0, event_filter_.GetMatcherCountForEvent("event1"));
+}
+
+TEST_F(EventFilterUnittest, RemoveEventMatcherReturnsEventName) {
+ int id1 = event_filter_.AddEventMatcher("event1", AllURLs());
+ int id2 = event_filter_.AddEventMatcher("event1", AllURLs());
+ int id3 = event_filter_.AddEventMatcher("event2", AllURLs());
+
+ ASSERT_EQ("event1", event_filter_.RemoveEventMatcher(id1));
+ ASSERT_EQ("event1", event_filter_.RemoveEventMatcher(id2));
+ ASSERT_EQ("event2", event_filter_.RemoveEventMatcher(id3));
+}
+
+TEST_F(EventFilterUnittest, InvalidURLFilterCantBeAdded) {
+ scoped_ptr<base::ListValue> filter_list(new base::ListValue());
+ filter_list->Append(new base::ListValue()); // Should be a dict.
+ scoped_ptr<EventMatcher> matcher(MatcherFromURLFilterList(
+ filter_list.Pass()));
+ int id1 = event_filter_.AddEventMatcher("event1", matcher.Pass());
+ EXPECT_TRUE(event_filter_.IsURLMatcherEmpty());
+ ASSERT_EQ(-1, id1);
+}
+
+TEST_F(EventFilterUnittest, EmptyListOfURLFiltersMatchesAllURLs) {
+ scoped_ptr<base::ListValue> filter_list(new base::ListValue());
+ scoped_ptr<EventMatcher> matcher(MatcherFromURLFilterList(
+ scoped_ptr<ListValue>(new ListValue)));
+ int id = event_filter_.AddEventMatcher("event1", matcher.Pass());
+ std::set<int> matches = event_filter_.MatchEvent("event1",
+ google_event_);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+}
+
+TEST_F(EventFilterUnittest,
+ InternalURLMatcherShouldBeEmptyWhenThereAreNoEventMatchers) {
+ ASSERT_TRUE(event_filter_.IsURLMatcherEmpty());
+ int id = event_filter_.AddEventMatcher("event1",
+ HostSuffixMatcher("google.com"));
+ ASSERT_FALSE(event_filter_.IsURLMatcherEmpty());
+ event_filter_.RemoveEventMatcher(id);
+ ASSERT_TRUE(event_filter_.IsURLMatcherEmpty());
+}
+
+TEST_F(EventFilterUnittest, EmptyURLsShouldBeMatchedByEmptyURLFilters) {
+ int id = event_filter_.AddEventMatcher("event1", AllURLs());
+ std::set<int> matches = event_filter_.MatchEvent("event1", empty_url_event_);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+}
+
+TEST_F(EventFilterUnittest,
+ EmptyURLsShouldBeMatchedByEmptyURLFiltersWithAnEmptyItem) {
+ scoped_ptr<EventMatcher> matcher(MatcherFromURLFilterList(ValueAsList(
+ scoped_ptr<Value>(new DictionaryValue()))));
+ int id = event_filter_.AddEventMatcher("event1", matcher.Pass());
+ std::set<int> matches = event_filter_.MatchEvent("event1", empty_url_event_);
+ ASSERT_EQ(1u, matches.size());
+ ASSERT_EQ(1u, matches.count(id));
+}
+
+} // namespace extensions
diff --git a/extensions/common/event_filtering_info.cc b/extensions/common/event_filtering_info.cc
new file mode 100644
index 0000000..009dcaa
--- /dev/null
+++ b/extensions/common/event_filtering_info.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/event_filtering_info.h"
+
+#include "base/json/json_writer.h"
+#include "base/values.h"
+
+namespace extensions {
+
+EventFilteringInfo::EventFilteringInfo()
+ : has_url_(false) {
+}
+
+EventFilteringInfo::~EventFilteringInfo() {
+}
+
+void EventFilteringInfo::SetURL(const GURL& url) {
+ url_ = url;
+ has_url_ = true;
+}
+
+std::string EventFilteringInfo::AsJSONString() const {
+ std::string result;
+ base::DictionaryValue value;
+ if (has_url_)
+ value.SetString("url", url_.spec());
+
+ base::JSONWriter::Write(&value, &result);
+ return result;
+}
+
+scoped_ptr<base::Value> EventFilteringInfo::AsValue() const {
+ if (IsEmpty())
+ return scoped_ptr<base::Value>(base::Value::CreateNullValue());
+
+ scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
+ if (has_url_)
+ result->SetString("url", url_.spec());
+ return result.PassAs<base::Value>();
+}
+
+bool EventFilteringInfo::IsEmpty() const {
+ return !has_url_;
+}
+
+} // namespace extensions
diff --git a/extensions/common/event_filtering_info.h b/extensions/common/event_filtering_info.h
new file mode 100644
index 0000000..2fee9c1
--- /dev/null
+++ b/extensions/common/event_filtering_info.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_
+#define EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "googleurl/src/gurl.h"
+
+namespace base {
+class Value;
+}
+
+namespace extensions {
+
+// Extra information about an event that is used in event filtering.
+//
+// This is the information that is matched against criteria specified in JS
+// extension event listeners. Eg:
+//
+// chrome.someApi.onSomeEvent.addListener(cb,
+// {url: [{hostSuffix: 'google.com'}],
+// tabId: 1});
+class EventFilteringInfo {
+ public:
+ EventFilteringInfo();
+ ~EventFilteringInfo();
+ void SetURL(const GURL& url);
+
+ bool has_url() const { return has_url_; }
+ const GURL& url() const { return url_; }
+
+ std::string AsJSONString() const;
+ scoped_ptr<base::Value> AsValue() const;
+ bool IsEmpty() const;
+
+ private:
+ bool has_url_;
+ GURL url_;
+
+ // Allow implicit copy and assignment.
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_EVENT_FILTERING_INFO_H_
diff --git a/extensions/common/event_matcher.cc b/extensions/common/event_matcher.cc
new file mode 100644
index 0000000..c982937
--- /dev/null
+++ b/extensions/common/event_matcher.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/common/event_matcher.h"
+
+#include "extensions/common/event_filtering_info.h"
+
+namespace {
+const char kUrlFiltersKey[] = "url";
+}
+
+namespace extensions {
+
+EventMatcher::EventMatcher(scoped_ptr<base::DictionaryValue> filter)
+ : filter_(filter.Pass()) {
+}
+
+EventMatcher::~EventMatcher() {
+}
+
+bool EventMatcher::MatchNonURLCriteria(
+ const EventFilteringInfo& event_info) const {
+ // There is currently no criteria apart from URL criteria.
+ return true;
+}
+
+int EventMatcher::GetURLFilterCount() const {
+ base::ListValue* url_filters = NULL;
+ if (filter_->GetList(kUrlFiltersKey, &url_filters))
+ return url_filters->GetSize();
+ return 0;
+}
+
+bool EventMatcher::GetURLFilter(int i, base::DictionaryValue** url_filter_out) {
+ base::ListValue* url_filters = NULL;
+ if (filter_->GetList(kUrlFiltersKey, &url_filters)) {
+ return url_filters->GetDictionary(i, url_filter_out);
+ }
+ return false;
+}
+
+int EventMatcher::HasURLFilters() const {
+ return GetURLFilterCount() != 0;
+}
+
+} // namespace extensions
diff --git a/extensions/common/event_matcher.h b/extensions/common/event_matcher.h
new file mode 100644
index 0000000..bb28419
--- /dev/null
+++ b/extensions/common/event_matcher.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_COMMON_EVENT_MATCHER_H_
+#define EXTENSIONS_COMMON_EVENT_MATCHER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+namespace extensions {
+
+class EventFilteringInfo;
+
+// Matches EventFilteringInfos against a set of criteria. This is intended to
+// be used by EventFilter which performs efficient URL matching across
+// potentially many EventMatchers itself. This is why this class only exposes
+// MatchNonURLCriteria() - URL matching is handled by EventFilter.
+class EventMatcher {
+ public:
+ explicit EventMatcher(scoped_ptr<base::DictionaryValue> filter);
+ ~EventMatcher();
+
+ // Returns true if |event_info| satisfies this matcher's criteria, not taking
+ // into consideration any URL criteria.
+ bool MatchNonURLCriteria(const EventFilteringInfo& event_info) const;
+
+ int GetURLFilterCount() const;
+ bool GetURLFilter(int i, base::DictionaryValue** url_filter_out);
+
+ int HasURLFilters() const;
+
+ base::DictionaryValue* value() const {
+ return filter_.get();
+ }
+
+ private:
+ // Contains a dictionary that corresponds to a single event filter, eg:
+ //
+ // {url: [{hostSuffix: 'google.com'}]}
+ //
+ // The valid filter keys are event-specific.
+ scoped_ptr<base::DictionaryValue> filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventMatcher);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_EVENT_MATCHER_H_