| Index: extensions/renderer/event_bindings.cc
|
| diff --git a/extensions/renderer/event_bindings.cc b/extensions/renderer/event_bindings.cc
|
| index 8a005aaec229225b10432440a1a09772ee8af28b..472f369063efe82a074811b1ac52f0881f440c80 100644
|
| --- a/extensions/renderer/event_bindings.cc
|
| +++ b/extensions/renderer/event_bindings.cc
|
| @@ -5,9 +5,6 @@
|
| #include "extensions/renderer/event_bindings.h"
|
|
|
| #include <map>
|
| -#include <set>
|
| -#include <string>
|
| -#include <vector>
|
|
|
| #include "base/basictypes.h"
|
| #include "base/bind.h"
|
| @@ -24,9 +21,7 @@
|
| #include "extensions/common/value_counter.h"
|
| #include "extensions/renderer/dispatcher.h"
|
| #include "extensions/renderer/extension_helper.h"
|
| -#include "extensions/renderer/object_backed_native_handler.h"
|
| #include "url/gurl.h"
|
| -#include "v8/include/v8.h"
|
|
|
| namespace extensions {
|
|
|
| @@ -143,12 +138,10 @@ bool RemoveFilter(const std::string& event_name,
|
|
|
| EventBindings::EventBindings(Dispatcher* dispatcher, ScriptContext* context)
|
| : ObjectBackedNativeHandler(context), dispatcher_(dispatcher) {
|
| - RouteFunction(
|
| - "AttachEvent",
|
| - base::Bind(&EventBindings::AttachEvent, base::Unretained(this)));
|
| - RouteFunction(
|
| - "DetachEvent",
|
| - base::Bind(&EventBindings::DetachEvent, base::Unretained(this)));
|
| + RouteFunction("AttachEvent", base::Bind(&EventBindings::AttachEventHandler,
|
| + base::Unretained(this)));
|
| + RouteFunction("DetachEvent", base::Bind(&EventBindings::DetachEventHandler,
|
| + base::Unretained(this)));
|
| RouteFunction(
|
| "AttachFilteredEvent",
|
| base::Bind(&EventBindings::AttachFilteredEvent, base::Unretained(this)));
|
| @@ -158,21 +151,36 @@ EventBindings::EventBindings(Dispatcher* dispatcher, ScriptContext* context)
|
| RouteFunction("MatchAgainstEventFilter",
|
| base::Bind(&EventBindings::MatchAgainstEventFilter,
|
| base::Unretained(this)));
|
| +
|
| + // It's safe to use base::Unretained here because |context| will always
|
| + // outlive us.
|
| + context->AddInvalidationObserver(
|
| + base::Bind(&EventBindings::OnInvalidated, base::Unretained(this)));
|
| }
|
|
|
| EventBindings::~EventBindings() {}
|
|
|
| -// Attach an event name to an object.
|
| -void EventBindings::AttachEvent(
|
| +void EventBindings::AttachEventHandler(
|
| const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| CHECK_EQ(1, args.Length());
|
| CHECK(args[0]->IsString());
|
| + AttachEvent(*v8::String::Utf8Value(args[0]));
|
| +}
|
|
|
| - std::string event_name = *v8::String::Utf8Value(args[0]);
|
| -
|
| +void EventBindings::AttachEvent(const std::string& event_name) {
|
| + // This method throws an exception if it returns false.
|
| if (!dispatcher_->CheckContextAccessToExtensionAPI(event_name, context()))
|
| return;
|
|
|
| + // Record the attachment for this context so that events can be detached when
|
| + // the context is destroyed.
|
| + //
|
| + // Ideally we'd CHECK that it's not already attached, however that's not
|
| + // possible because extensions can create and attach events themselves. Very
|
| + // silly, but that's the way it is. For an example of this, see
|
| + // chrome/test/data/extensions/api_test/events/background.js.
|
| + attached_event_names_.insert(event_name);
|
| +
|
| const std::string& extension_id = context()->GetExtensionID();
|
| if (IncrementEventListenerCount(context(), event_name) == 1) {
|
| content::RenderThread::Get()->Send(new ExtensionHostMsg_AddListener(
|
| @@ -189,16 +197,20 @@ void EventBindings::AttachEvent(
|
| }
|
| }
|
|
|
| -void EventBindings::DetachEvent(
|
| +void EventBindings::DetachEventHandler(
|
| const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| CHECK_EQ(2, args.Length());
|
| CHECK(args[0]->IsString());
|
| CHECK(args[1]->IsBoolean());
|
| + DetachEvent(*v8::String::Utf8Value(args[0]), args[1]->BooleanValue());
|
| +}
|
|
|
| - std::string event_name = *v8::String::Utf8Value(args[0]);
|
| - bool is_manual = args[1]->BooleanValue();
|
| +void EventBindings::DetachEvent(const std::string& event_name, bool is_manual) {
|
| + // See comment in AttachEvent().
|
| + attached_event_names_.erase(event_name);
|
|
|
| const std::string& extension_id = context()->GetExtensionID();
|
| +
|
| if (DecrementEventListenerCount(context(), event_name) == 0) {
|
| content::RenderThread::Get()->Send(new ExtensionHostMsg_RemoveListener(
|
| extension_id, context()->GetURL(), event_name));
|
| @@ -324,4 +336,15 @@ scoped_ptr<EventMatcher> EventBindings::ParseEventMatcher(
|
| context()->GetRenderView()->GetRoutingID()));
|
| }
|
|
|
| +void EventBindings::OnInvalidated() {
|
| + // Detach all attached events that weren't attached. Iterate over a copy
|
| + // because it will be mutated.
|
| + std::set<std::string> attached_event_names_safe = attached_event_names_;
|
| + for (const std::string& event_name : attached_event_names_safe) {
|
| + DetachEvent(event_name, false /* is_manual */);
|
| + }
|
| + DCHECK(attached_event_names_.empty())
|
| + << "Events cannot be attached during invalidation";
|
| +}
|
| +
|
| } // namespace extensions
|
|
|