Commit bd9f618e authored by kalman's avatar kalman Committed by Commit bot

Clear an extension's filtered events when a context is destroyed.

In crrev.com/325156 I moved event invalidation logic from JavaScript into C++,
but I missed out filtered events. This CL also does some cleanup.

BUG=503913
R=rdevlin.cronin@chromium.org

Review URL: https://codereview.chromium.org/1227093008

Cr-Commit-Position: refs/heads/master@{#338403}
parent c58992cc
...@@ -45,7 +45,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, EventsAreUnregistered) { ...@@ -45,7 +45,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, EventsAreUnregistered) {
const std::string& id = extension->id(); const std::string& id = extension->id();
// The page has closed, so no matter what all events are no longer listened // The page has closed, so no matter what all events are no longer listened
// to. // to. Assertions for normal events:
EXPECT_FALSE( EXPECT_FALSE(
event_router->ExtensionHasEventListener(id, "browserAction.onClicked")); event_router->ExtensionHasEventListener(id, "browserAction.onClicked"));
EXPECT_FALSE( EXPECT_FALSE(
...@@ -54,6 +54,15 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, EventsAreUnregistered) { ...@@ -54,6 +54,15 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, EventsAreUnregistered) {
event_router->ExtensionHasEventListener(id, "runtime.onSuspend")); event_router->ExtensionHasEventListener(id, "runtime.onSuspend"));
EXPECT_FALSE( EXPECT_FALSE(
event_router->ExtensionHasEventListener(id, "runtime.onInstalled")); event_router->ExtensionHasEventListener(id, "runtime.onInstalled"));
// Assertions for filtered events:
EXPECT_FALSE(event_router->ExtensionHasEventListener(
id, "webNavigation.onBeforeNavigate"));
EXPECT_FALSE(
event_router->ExtensionHasEventListener(id, "webNavigation.onCommitted"));
EXPECT_FALSE(event_router->ExtensionHasEventListener(
id, "webNavigation.onDOMContentLoaded"));
EXPECT_FALSE(
event_router->ExtensionHasEventListener(id, "webNavigation.onCompleted"));
} }
} // namespace extensions } // namespace extensions
...@@ -7,36 +7,62 @@ ...@@ -7,36 +7,62 @@
#include "extensions/common/value_counter.h" #include "extensions/common/value_counter.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
class ValueCounterUnittest : public testing::Test { using ValueCounterTest = testing::Test;
};
TEST_F(ValueCounterUnittest, TestAddingSameValue) { namespace extensions {
extensions::ValueCounter vc;
TEST_F(ValueCounterTest, TestAddingSameValue) {
ValueCounter vc;
base::ListValue value; base::ListValue value;
ASSERT_EQ(1, vc.Add(value)); ASSERT_TRUE(vc.Add(value));
ASSERT_EQ(2, vc.Add(value)); ASSERT_FALSE(vc.Add(value));
} }
TEST_F(ValueCounterUnittest, TestAddingDifferentValue) { TEST_F(ValueCounterTest, TestAddingDifferentValue) {
extensions::ValueCounter vc; ValueCounter vc;
base::ListValue value1; base::ListValue value1;
base::DictionaryValue value2; base::DictionaryValue value2;
ASSERT_EQ(1, vc.Add(value1)); ASSERT_TRUE(vc.Add(value1));
ASSERT_EQ(1, vc.Add(value2)); ASSERT_TRUE(vc.Add(value2));
} }
TEST_F(ValueCounterUnittest, TestRemovingValue) { TEST_F(ValueCounterTest, TestRemovingSameValue) {
extensions::ValueCounter vc; ValueCounter vc;
base::ListValue value; base::ListValue value;
ASSERT_EQ(1, vc.Add(value)); vc.Add(value);
ASSERT_EQ(2, vc.Add(value)); vc.Add(value);
ASSERT_EQ(1, vc.Remove(value)); ASSERT_FALSE(vc.Remove(value));
ASSERT_EQ(0, vc.Remove(value)); ASSERT_TRUE(vc.Remove(value));
ASSERT_FALSE(vc.Remove(value));
} }
TEST_F(ValueCounterUnittest, TestAddIfMissing) { TEST_F(ValueCounterTest, TestReAddingSameValue) {
extensions::ValueCounter vc; ValueCounter vc;
base::ListValue value; base::ListValue value;
ASSERT_EQ(1, vc.AddIfMissing(value)); ASSERT_FALSE(vc.Remove(value));
ASSERT_EQ(1, vc.AddIfMissing(value)); ASSERT_TRUE(vc.Add(value));
ASSERT_TRUE(vc.Remove(value));
ASSERT_TRUE(vc.Add(value));
ASSERT_TRUE(vc.Remove(value));
ASSERT_FALSE(vc.Remove(value));
}
TEST_F(ValueCounterTest, TestIsEmpty) {
ValueCounter vc;
base::ListValue value1;
base::DictionaryValue value2;
ASSERT_TRUE(vc.is_empty());
vc.Add(value1);
ASSERT_FALSE(vc.is_empty());
vc.Remove(value1);
ASSERT_TRUE(vc.is_empty());
vc.Add(value1);
vc.Add(value2);
ASSERT_FALSE(vc.is_empty());
vc.Remove(value1);
ASSERT_FALSE(vc.is_empty());
vc.Remove(value2);
ASSERT_TRUE(vc.is_empty());
} }
} // namespace extensions
...@@ -3,5 +3,6 @@ ...@@ -3,5 +3,6 @@
"description": "events_are_unregistered", "description": "events_are_unregistered",
"manifest_version": 2, "manifest_version": 2,
"name": "events_are_unregistered", "name": "events_are_unregistered",
"permissions": ["webNavigation"],
"version": "1" "version": "1"
} }
...@@ -2,23 +2,68 @@ ...@@ -2,23 +2,68 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
'use strict';
// Register for events in 4 configurations, then navigate to page2.html, which // Register for events in 4 configurations, then navigate to page2.html, which
// will notify success and succeed the test on the C++ side. The C++ code // will notify success and succeed the test on the C++ side. The C++ code
// asserts that the events have been unregistered. // asserts that the events have been unregistered.
//
// Unfiltered events.
//
// A single listener. // A single listener.
chrome.browserAction.onClicked.addListener(function() {}); chrome.browserAction.onClicked.addListener(function() {});
// Multiple listeners for the same event. // Multiple listeners for the same event.
chrome.runtime.onStartup.addListener(function() {}); chrome.runtime.onStartup.addListener(function() {});
chrome.runtime.onStartup.addListener(function() {}); chrome.runtime.onStartup.addListener(function() {});
// A single listener, which previously had multiple listeners. // A single listener, which previously had multiple listeners.
chrome.runtime.onSuspend.addListener(function() {}); {
chrome.runtime.onSuspend.addListener(function() {}); let singleListener = function() {};
chrome.runtime.onSuspend.removeListener(function() {}); chrome.runtime.onSuspend.addListener(singleListener);
chrome.runtime.onSuspend.addListener(function() {});
chrome.runtime.onSuspend.removeListener(singleListener);
}
// No listeners, which previously had listeners (all were removed). // No listeners, which previously had listeners (all were removed).
chrome.runtime.onInstalled.addListener(function() {}); {
chrome.runtime.onInstalled.addListener(function() {}); let listener1 = function() {};
chrome.runtime.onInstalled.removeListener(function() {}); let listener2 = function() {};
chrome.runtime.onInstalled.removeListener(function() {}); chrome.runtime.onInstalled.addListener(listener1);
chrome.runtime.onInstalled.addListener(listener2);
chrome.runtime.onInstalled.removeListener(listener1);
chrome.runtime.onInstalled.removeListener(listener2);
}
//
// Filtered events.
//
function filterPort(portNumber) {
return {url: [{ports: [portNumber]}]};
}
// A single listener.
chrome.webNavigation.onBeforeNavigate.addListener(function() {});
// Multiple, different listeners.
chrome.webNavigation.onCommitted.addListener(function() {});
chrome.webNavigation.onCommitted.addListener(function() {});
// Different listeners with the same filter.
chrome.webNavigation.onDOMContentLoaded.addListener(
function() {}, filterPort(80));
chrome.webNavigation.onDOMContentLoaded.addListener(
function() {}, filterPort(80));
// Different listeners with different filters, same event added twice.
{
let singleListener = function() {};
chrome.webNavigation.onCompleted.addListener(
function() {}, filterPort(80));
chrome.webNavigation.onCompleted.addListener(
function() {}, filterPort(81));
chrome.webNavigation.onCompleted.addListener(
function() {}, filterPort(81));
chrome.webNavigation.onCompleted.addListener(
singleListener, filterPort(82));
chrome.webNavigation.onCompleted.removeListener(singleListener);
}
location.assign('page2.html'); location.assign('page2.html');
...@@ -10,47 +10,44 @@ ...@@ -10,47 +10,44 @@
namespace extensions { namespace extensions {
ValueCounter::ValueCounter() {} struct ValueCounter::Entry {
explicit Entry(scoped_ptr<base::Value> value)
: value(value.Pass()), count(1) {}
ValueCounter::~ValueCounter() {} scoped_ptr<base::Value> value;
int count;
};
ValueCounter::Entry::Entry(const base::Value& value) ValueCounter::ValueCounter() {
: value_(value.DeepCopy()), count_(1) {} }
ValueCounter::Entry::~Entry() {}
int ValueCounter::Entry::Increment() { return ++count_; }
int ValueCounter::Entry::Decrement() { return --count_; } ValueCounter::~ValueCounter() {
}
int ValueCounter::Add(const base::Value& value) { return AddImpl(value, true); } bool ValueCounter::Add(const base::Value& value) {
for (Entry* entry : entries_) {
if (entry->value->Equals(&value)) {
++entry->count;
return false;
}
}
entries_.push_back(new Entry(value.CreateDeepCopy()));
return true;
}
int ValueCounter::Remove(const base::Value& value) { bool ValueCounter::Remove(const base::Value& value) {
for (EntryList::iterator it = entries_.begin(); it != entries_.end(); it++) { for (ScopedVector<Entry>::iterator it = entries_.begin();
(*it)->value()->GetType(); it != entries_.end(); ++it) {
if ((*it)->value()->Equals(&value)) { if ((*it)->value->Equals(&value)) {
int remaining = (*it)->Decrement(); if (--(*it)->count == 0) {
if (remaining == 0) {
std::swap(*it, entries_.back()); std::swap(*it, entries_.back());
entries_.pop_back(); entries_.pop_back();
return true; // Removed the last entry.
} }
return remaining; return false; // Removed, but no the last entry.
} }
} }
return 0; return false; // Nothing to remove.
}
int ValueCounter::AddIfMissing(const base::Value& value) {
return AddImpl(value, false);
}
int ValueCounter::AddImpl(const base::Value& value, bool increment) {
for (EntryList::iterator it = entries_.begin(); it != entries_.end(); it++) {
if ((*it)->value()->Equals(&value))
return increment ? (*it)->Increment() : (*it)->count();
}
entries_.push_back(linked_ptr<Entry>(new Entry(value)));
return 1;
} }
} // namespace extensions } // namespace extensions
...@@ -5,9 +5,7 @@ ...@@ -5,9 +5,7 @@
#ifndef EXTENSIONS_COMMON_VALUE_COUNTER_H_ #ifndef EXTENSIONS_COMMON_VALUE_COUNTER_H_
#define EXTENSIONS_COMMON_VALUE_COUNTER_H_ #define EXTENSIONS_COMMON_VALUE_COUNTER_H_
#include <vector> #include "base/memory/scoped_vector.h"
#include "base/memory/linked_ptr.h"
namespace base { namespace base {
class Value; class Value;
...@@ -15,8 +13,8 @@ class Value; ...@@ -15,8 +13,8 @@ class Value;
namespace extensions { namespace extensions {
// Keeps a running count of Values, like map<Value, int>. Adding / removing // Keeps a running count of Values, like map<Value, int>. Adding/removing
// values increments / decrements the count associated with a given Value. // values increments/decrements the count associated with a given Value.
// //
// Add() and Remove() are linear in the number of Values in the ValueCounter, // Add() and Remove() are linear in the number of Values in the ValueCounter,
// because there is no operator<() defined on Value, so we must iterate to find // because there is no operator<() defined on Value, so we must iterate to find
...@@ -26,42 +24,22 @@ class ValueCounter { ...@@ -26,42 +24,22 @@ class ValueCounter {
ValueCounter(); ValueCounter();
~ValueCounter(); ~ValueCounter();
// Adds |value| to the set and returns how many equal values are in the set // Adds |value| to the set. In the case where a Value equal to |value|
// after. Does not take ownership of |value|. In the case where a Value equal // doesn't already exist in this map, this function makes a copy of |value|
// to |value| doesn't already exist in this map, this function makes a // and returns true. Otherwise, it returns false.
// DeepCopy() of |value|. bool Add(const base::Value& value);
int Add(const base::Value& value);
// Removes |value| from the set and returns how many equal values are in // Removes |value| from the set, and returns true if it removed the last
// the set after. // value equal to |value|. If there are more equal values, or if there
int Remove(const base::Value& value); // weren't any in the first place, returns false.
bool Remove(const base::Value& value);
// Same as Add() but only performs the add if the value isn't present. // Returns true if there are no values of any type being counted.
int AddIfMissing(const base::Value& value); bool is_empty() const { return entries_.empty(); }
private: private:
class Entry { struct Entry;
public: ScopedVector<Entry> entries_;
explicit Entry(const base::Value& value);
~Entry();
int Increment();
int Decrement();
const base::Value* value() const { return value_.get(); }
int count() const { return count_; }
private:
linked_ptr<base::Value> value_;
int count_;
DISALLOW_COPY_AND_ASSIGN(Entry);
};
typedef std::vector<linked_ptr<Entry> > EntryList;
int AddImpl(const base::Value& value, bool increment);
EntryList entries_;
DISALLOW_COPY_AND_ASSIGN(ValueCounter); DISALLOW_COPY_AND_ASSIGN(ValueCounter);
}; };
......
...@@ -5,9 +5,11 @@ ...@@ -5,9 +5,11 @@
#include "extensions/renderer/event_bindings.h" #include "extensions/renderer/event_bindings.h"
#include <map> #include <map>
#include <utility>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/containers/scoped_ptr_map.h"
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "components/crx_file/id_util.h" #include "components/crx_file/id_util.h"
...@@ -33,22 +35,23 @@ namespace { ...@@ -33,22 +35,23 @@ namespace {
typedef std::map<std::string, int> EventListenerCounts; typedef std::map<std::string, int> EventListenerCounts;
// A map of extension IDs to listener counts for that extension. // A map of extension IDs to listener counts for that extension.
base::LazyInstance<std::map<std::string, EventListenerCounts> > base::LazyInstance<std::map<std::string, EventListenerCounts>>
g_listener_counts = LAZY_INSTANCE_INITIALIZER; g_listener_counts = LAZY_INSTANCE_INITIALIZER;
// A map of event names to a (filter -> count) map. The map is used to keep // A map of (extension ID, event name) pairs to the filtered listener counts
// track of which filters are in effect for which events. // for that pair. The map is used to keep track of which filters are in effect
// We notify the browser about filtered event listeners when we transition // for which events. We notify the browser about filtered event listeners when
// between 0 and 1. // we transition between 0 and 1.
typedef std::map<std::string, linked_ptr<ValueCounter> > using FilteredEventListenerKey = std::pair<std::string, std::string>;
FilteredEventListenerCounts; using FilteredEventListenerCounts =
base::ScopedPtrMap<FilteredEventListenerKey, scoped_ptr<ValueCounter>>;
// A map of extension IDs to filtered listener counts for that extension. base::LazyInstance<FilteredEventListenerCounts> g_filtered_listener_counts =
base::LazyInstance<std::map<std::string, FilteredEventListenerCounts> > LAZY_INSTANCE_INITIALIZER;
g_filtered_listener_counts = LAZY_INSTANCE_INITIALIZER;
base::LazyInstance<EventFilter> g_event_filter = LAZY_INSTANCE_INITIALIZER; base::LazyInstance<EventFilter> g_event_filter = LAZY_INSTANCE_INITIALIZER;
// Gets a unique string key identifier for a ScriptContext.
// TODO(kalman): Just use pointer equality...?
std::string GetKeyForScriptContext(ScriptContext* script_context) { std::string GetKeyForScriptContext(ScriptContext* script_context) {
const std::string& extension_id = script_context->GetExtensionID(); const std::string& extension_id = script_context->GetExtensionID();
CHECK(crx_file::id_util::IdIsValid(extension_id) || CHECK(crx_file::id_util::IdIsValid(extension_id) ||
...@@ -101,15 +104,14 @@ EventFilteringInfo ParseFromObject(v8::Local<v8::Object> object, ...@@ -101,15 +104,14 @@ EventFilteringInfo ParseFromObject(v8::Local<v8::Object> object,
// was the first filter for that event in that extension. // was the first filter for that event in that extension.
bool AddFilter(const std::string& event_name, bool AddFilter(const std::string& event_name,
const std::string& extension_id, const std::string& extension_id,
base::DictionaryValue* filter) { const base::DictionaryValue& filter) {
FilteredEventListenerCounts& counts = FilteredEventListenerKey key(extension_id, event_name);
g_filtered_listener_counts.Get()[extension_id]; FilteredEventListenerCounts& all_counts = g_filtered_listener_counts.Get();
FilteredEventListenerCounts::iterator it = counts.find(event_name); FilteredEventListenerCounts::const_iterator counts = all_counts.find(key);
if (it == counts.end()) if (counts == all_counts.end()) {
counts[event_name].reset(new ValueCounter); counts = all_counts.insert(key, make_scoped_ptr(new ValueCounter())).first;
}
int result = counts[event_name]->Add(*filter); return counts->second->Add(filter);
return 1 == result;
} }
// Remove a filter from |event_name| in |extension_id|, returning true if it // Remove a filter from |event_name| in |extension_id|, returning true if it
...@@ -117,12 +119,20 @@ bool AddFilter(const std::string& event_name, ...@@ -117,12 +119,20 @@ bool AddFilter(const std::string& event_name,
bool RemoveFilter(const std::string& event_name, bool RemoveFilter(const std::string& event_name,
const std::string& extension_id, const std::string& extension_id,
base::DictionaryValue* filter) { base::DictionaryValue* filter) {
FilteredEventListenerCounts& counts = FilteredEventListenerKey key(extension_id, event_name);
g_filtered_listener_counts.Get()[extension_id]; FilteredEventListenerCounts& all_counts = g_filtered_listener_counts.Get();
FilteredEventListenerCounts::iterator it = counts.find(event_name); FilteredEventListenerCounts::const_iterator counts = all_counts.find(key);
if (it == counts.end()) if (counts == all_counts.end())
return false; return false;
return 0 == it->second->Remove(*filter); // Note: Remove() returns true if it removed the last filter equivalent to
// |filter|. If there are more equivalent filters, or if there weren't any in
// the first place, it returns false.
if (counts->second->Remove(*filter)) {
if (counts->second->is_empty())
all_counts.erase(counts); // Clean up if there are no more filters.
return true;
}
return false;
} }
} // namespace } // namespace
...@@ -136,9 +146,9 @@ EventBindings::EventBindings(ScriptContext* context) ...@@ -136,9 +146,9 @@ EventBindings::EventBindings(ScriptContext* context)
RouteFunction( RouteFunction(
"AttachFilteredEvent", "AttachFilteredEvent",
base::Bind(&EventBindings::AttachFilteredEvent, base::Unretained(this))); base::Bind(&EventBindings::AttachFilteredEvent, base::Unretained(this)));
RouteFunction( RouteFunction("DetachFilteredEvent",
"DetachFilteredEvent", base::Bind(&EventBindings::DetachFilteredEventHandler,
base::Bind(&EventBindings::DetachFilteredEvent, base::Unretained(this))); base::Unretained(this)));
RouteFunction("MatchAgainstEventFilter", RouteFunction("MatchAgainstEventFilter",
base::Bind(&EventBindings::MatchAgainstEventFilter, base::Bind(&EventBindings::MatchAgainstEventFilter,
base::Unretained(this))); base::Unretained(this)));
...@@ -230,56 +240,53 @@ void EventBindings::AttachFilteredEvent( ...@@ -230,56 +240,53 @@ void EventBindings::AttachFilteredEvent(
if (!context()->HasAccessOrThrowError(event_name)) if (!context()->HasAccessOrThrowError(event_name))
return; return;
std::string extension_id = context()->GetExtensionID();
scoped_ptr<base::DictionaryValue> filter; scoped_ptr<base::DictionaryValue> filter;
scoped_ptr<content::V8ValueConverter> converter( {
content::V8ValueConverter::create()); scoped_ptr<content::V8ValueConverter> converter(
content::V8ValueConverter::create());
base::DictionaryValue* filter_dict = NULL; scoped_ptr<base::Value> filter_value(converter->FromV8Value(
base::Value* filter_value = converter->FromV8Value( v8::Local<v8::Object>::Cast(args[1]), context()->v8_context()));
v8::Local<v8::Object>::Cast(args[1]), context()->v8_context()); if (!filter_value || !filter_value->IsType(base::Value::TYPE_DICTIONARY)) {
if (!filter_value) { args.GetReturnValue().Set(static_cast<int32_t>(-1));
args.GetReturnValue().Set(static_cast<int32_t>(-1)); return;
return; }
} filter.reset(static_cast<base::DictionaryValue*>(filter_value.release()));
if (!filter_value->GetAsDictionary(&filter_dict)) {
delete filter_value;
args.GetReturnValue().Set(static_cast<int32_t>(-1));
return;
} }
filter.reset(filter_dict); // Hold onto a weak reference to |filter| so that it can be used after passing
EventFilter& event_filter = g_event_filter.Get(); // ownership to |event_filter|.
int id = base::DictionaryValue* filter_weak = filter.get();
event_filter.AddEventMatcher(event_name, ParseEventMatcher(filter.get())); int id = g_event_filter.Get().AddEventMatcher(
event_name, ParseEventMatcher(filter.Pass()));
attached_matcher_ids_.insert(id);
// Only send IPCs the first time a filter gets added. // Only send IPCs the first time a filter gets added.
if (AddFilter(event_name, extension_id, filter.get())) { std::string extension_id = context()->GetExtensionID();
if (AddFilter(event_name, extension_id, *filter_weak)) {
bool lazy = ExtensionFrameHelper::IsContextForEventPage(context()); bool lazy = ExtensionFrameHelper::IsContextForEventPage(context());
content::RenderThread::Get()->Send(new ExtensionHostMsg_AddFilteredListener( content::RenderThread::Get()->Send(new ExtensionHostMsg_AddFilteredListener(
extension_id, event_name, *filter, lazy)); extension_id, event_name, *filter_weak, lazy));
} }
args.GetReturnValue().Set(static_cast<int32_t>(id)); args.GetReturnValue().Set(static_cast<int32_t>(id));
} }
void EventBindings::DetachFilteredEvent( void EventBindings::DetachFilteredEventHandler(
const v8::FunctionCallbackInfo<v8::Value>& args) { const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK_EQ(2, args.Length()); CHECK_EQ(2, args.Length());
CHECK(args[0]->IsInt32()); CHECK(args[0]->IsInt32());
CHECK(args[1]->IsBoolean()); CHECK(args[1]->IsBoolean());
bool is_manual = args[1]->BooleanValue(); DetachFilteredEvent(args[0]->Int32Value(), args[1]->BooleanValue());
}
std::string extension_id = context()->GetExtensionID();
int matcher_id = args[0]->Int32Value(); void EventBindings::DetachFilteredEvent(int matcher_id, bool is_manual) {
EventFilter& event_filter = g_event_filter.Get(); EventFilter& event_filter = g_event_filter.Get();
EventMatcher* event_matcher = event_filter.GetEventMatcher(matcher_id); EventMatcher* event_matcher = event_filter.GetEventMatcher(matcher_id);
const std::string& event_name = event_filter.GetEventName(matcher_id); const std::string& event_name = event_filter.GetEventName(matcher_id);
// Only send IPCs the last time a filter gets removed. // Only send IPCs the last time a filter gets removed.
std::string extension_id = context()->GetExtensionID();
if (RemoveFilter(event_name, extension_id, event_matcher->value())) { if (RemoveFilter(event_name, extension_id, event_matcher->value())) {
bool remove_lazy = bool remove_lazy =
is_manual && ExtensionFrameHelper::IsContextForEventPage(context()); is_manual && ExtensionFrameHelper::IsContextForEventPage(context());
...@@ -289,6 +296,7 @@ void EventBindings::DetachFilteredEvent( ...@@ -289,6 +296,7 @@ void EventBindings::DetachFilteredEvent(
} }
event_filter.RemoveEventMatcher(matcher_id); event_filter.RemoveEventMatcher(matcher_id);
attached_matcher_ids_.erase(matcher_id);
} }
void EventBindings::MatchAgainstEventFilter( void EventBindings::MatchAgainstEventFilter(
...@@ -315,10 +323,9 @@ void EventBindings::MatchAgainstEventFilter( ...@@ -315,10 +323,9 @@ void EventBindings::MatchAgainstEventFilter(
} }
scoped_ptr<EventMatcher> EventBindings::ParseEventMatcher( scoped_ptr<EventMatcher> EventBindings::ParseEventMatcher(
base::DictionaryValue* filter_dict) { scoped_ptr<base::DictionaryValue> filter) {
return scoped_ptr<EventMatcher>(new EventMatcher( return make_scoped_ptr(new EventMatcher(
scoped_ptr<base::DictionaryValue>(filter_dict->DeepCopy()), filter.Pass(), context()->GetRenderFrame()->GetRoutingID()));
context()->GetRenderFrame()->GetRoutingID()));
} }
void EventBindings::OnInvalidated() { void EventBindings::OnInvalidated() {
...@@ -330,6 +337,14 @@ void EventBindings::OnInvalidated() { ...@@ -330,6 +337,14 @@ void EventBindings::OnInvalidated() {
} }
DCHECK(attached_event_names_.empty()) DCHECK(attached_event_names_.empty())
<< "Events cannot be attached during invalidation"; << "Events cannot be attached during invalidation";
// Same for filtered events.
std::set<int> attached_matcher_ids_safe = attached_matcher_ids_;
for (int matcher_id : attached_matcher_ids_safe) {
DetachFilteredEvent(matcher_id, false /* is_manual */);
}
DCHECK(attached_matcher_ids_.empty())
<< "Filtered events cannot be attached during invalidation";
} }
} // namespace extensions } // namespace extensions
...@@ -53,23 +53,32 @@ class EventBindings : public ObjectBackedNativeHandler { ...@@ -53,23 +53,32 @@ class EventBindings : public ObjectBackedNativeHandler {
// to MatchAgainstEventFilter where this listener matches. // to MatchAgainstEventFilter where this listener matches.
void AttachFilteredEvent(const v8::FunctionCallbackInfo<v8::Value>& args); void AttachFilteredEvent(const v8::FunctionCallbackInfo<v8::Value>& args);
// JavaScript handler which forwards to DetachFilteredEvent.
// void DetachFilteredEvent(int id, bool manual) // void DetachFilteredEvent(int id, bool manual)
// id - Id of the event to detach. // args[0] forwards to |matcher_id|
// manual - false if this is part of the extension unload process where all // args[1] forwards to |is_manual|
// listeners are automatically detached. void DetachFilteredEventHandler(
void DetachFilteredEvent(const v8::FunctionCallbackInfo<v8::Value>& args); const v8::FunctionCallbackInfo<v8::Value>& args);
// Detaches a filtered event. Unlike a normal event, a filtered event is
// identified by a unique ID per filter, not its name.
// |matcher_id| The ID of the filtered event.
// |is_manual| false if this is part of the extension unload process where all
// listeners are automatically detached.
void DetachFilteredEvent(int matcher_id, bool is_manual);
void MatchAgainstEventFilter(const v8::FunctionCallbackInfo<v8::Value>& args); void MatchAgainstEventFilter(const v8::FunctionCallbackInfo<v8::Value>& args);
scoped_ptr<EventMatcher> ParseEventMatcher( scoped_ptr<EventMatcher> ParseEventMatcher(
base::DictionaryValue* filter_dict); scoped_ptr<base::DictionaryValue> filter);
// Called when our context, and therefore us, is invalidated. Run any cleanup. // Called when our context, and therefore us, is invalidated. Run any cleanup.
void OnInvalidated(); void OnInvalidated();
// The set of attached events. Maintain this so that we can detch them on // The set of attached events and filtered events. Maintain these so that we
// unload. // can detch them on unload.
std::set<std::string> attached_event_names_; std::set<std::string> attached_event_names_;
std::set<int> attached_matcher_ids_;
DISALLOW_COPY_AND_ASSIGN(EventBindings); DISALLOW_COPY_AND_ASSIGN(EventBindings);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment