Index: extensions/renderer/api_event_handler.cc |
diff --git a/extensions/renderer/api_event_handler.cc b/extensions/renderer/api_event_handler.cc |
index d05e9e48c40ad0275f216647b920dc166eb5f1f4..794b19436638ab4200778061ad77bc629fbbeed5 100644 |
--- a/extensions/renderer/api_event_handler.cc |
+++ b/extensions/renderer/api_event_handler.cc |
@@ -28,18 +28,10 @@ const char kExtensionAPIEventPerContextKey[] = "extension_api_events"; |
struct APIEventPerContextData : public base::SupportsUserData::Data { |
APIEventPerContextData(v8::Isolate* isolate) : isolate(isolate) {} |
~APIEventPerContextData() override { |
- v8::HandleScope scope(isolate); |
- // We explicitly clear the event data map here to remove all references to |
- // v8 objects. This helps us avoid cycles in v8 where an event listener |
- // could hold a reference to the event, which in turn holds the reference |
- // to the listener. |
- for (const auto& pair : emitters) { |
- EventEmitter* emitter = nullptr; |
- gin::Converter<EventEmitter*>::FromV8( |
- isolate, pair.second.Get(isolate), &emitter); |
- CHECK(emitter); |
- emitter->listeners()->clear(); |
- } |
+ DCHECK(emitters.empty()) |
+ << "|emitters| should have been cleared by InvalidateContext()"; |
+ DCHECK(massagers.empty()) |
+ << "|massagers| should have been cleared by InvalidateContext()"; |
} |
// The associated v8::Isolate. Since this object is cleaned up at context |
@@ -198,6 +190,39 @@ void APIEventHandler::RegisterArgumentMassager( |
data->massagers[event_name].Reset(context->GetIsolate(), massager); |
} |
+void APIEventHandler::InvalidateContext(v8::Local<v8::Context> context) { |
+ gin::PerContextData* per_context_data = gin::PerContextData::From(context); |
+ DCHECK(per_context_data); |
+ APIEventPerContextData* data = static_cast<APIEventPerContextData*>( |
+ per_context_data->GetUserData(kExtensionAPIEventPerContextKey)); |
+ if (!data) |
+ return; |
+ |
+ v8::Isolate* isolate = context->GetIsolate(); |
+ v8::HandleScope scope(isolate); |
+ // This loop *shouldn't* allow any self-modification (i.e., no listeners |
+ // should be added or removed as a result of the iteration). If that changes, |
+ // we'll need to cache the listeners elsewhere before iterating. |
+ for (const auto& pair : data->emitters) { |
+ EventEmitter* emitter = nullptr; |
+ gin::Converter<EventEmitter*>::FromV8(isolate, pair.second.Get(isolate), |
+ &emitter); |
+ CHECK(emitter); |
+ emitter->Invalidate(); |
+ // When the context is shut down, all listeners are removed. |
+ listeners_changed_.Run( |
+ pair.first, binding::EventListenersChanged::NO_LISTENERS, context); |
+ } |
+ |
+ data->emitters.clear(); |
+ data->massagers.clear(); |
+ |
+ // InvalidateContext() is called shortly (and, theoretically, synchronously) |
+ // before the PerContextData is deleted. We have a check that guarantees that |
+ // no new EventEmitters are created after the PerContextData is deleted, so |
+ // no new emitters should be created after this point. |
+} |
+ |
size_t APIEventHandler::GetNumEventListenersForTesting( |
const std::string& event_name, |
v8::Local<v8::Context> context) { |