// Copyright 2014 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/renderer/script_context_set.h" #include "base/message_loop/message_loop.h" #include "content/public/renderer/render_view.h" #include "extensions/common/extension.h" #include "extensions/renderer/script_context.h" #include "v8/include/v8.h" namespace extensions { ScriptContextSet::ScriptContextSet() { } ScriptContextSet::~ScriptContextSet() { } int ScriptContextSet::size() const { return static_cast(contexts_.size()); } void ScriptContextSet::Add(ScriptContext* context) { #if DCHECK_IS_ON() // It's OK to insert the same context twice, but we should only ever have // one ScriptContext per v8::Context. for (ContextSet::iterator iter = contexts_.begin(); iter != contexts_.end(); ++iter) { ScriptContext* candidate = *iter; if (candidate != context) DCHECK(candidate->v8_context() != context->v8_context()); } #endif contexts_.insert(context); } void ScriptContextSet::Remove(ScriptContext* context) { if (contexts_.erase(context)) { context->Invalidate(); base::MessageLoop::current()->DeleteSoon(FROM_HERE, context); } } ScriptContextSet::ContextSet ScriptContextSet::GetAll() const { return contexts_; } ScriptContext* ScriptContextSet::GetCurrent() const { v8::Isolate* isolate = v8::Isolate::GetCurrent(); return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext()) : NULL; } ScriptContext* ScriptContextSet::GetCalling() const { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Local calling = isolate->GetCallingContext(); return calling.IsEmpty() ? NULL : GetByV8Context(calling); } ScriptContext* ScriptContextSet::GetByV8Context( v8::Handle v8_context) const { for (ContextSet::const_iterator iter = contexts_.begin(); iter != contexts_.end(); ++iter) { if ((*iter)->v8_context() == v8_context) return *iter; } return NULL; } void ScriptContextSet::ForEach( const std::string& extension_id, content::RenderView* render_view, const base::Callback& callback) const { // We copy the context list, because calling into javascript may modify it // out from under us. ContextSet contexts = GetAll(); for (ContextSet::iterator it = contexts.begin(); it != contexts.end(); ++it) { ScriptContext* context = *it; // For the same reason as above, contexts may become invalid while we run. if (!context->is_valid()) continue; if (!extension_id.empty()) { const Extension* extension = context->extension(); if (!extension || (extension_id != extension->id())) continue; } content::RenderView* context_render_view = context->GetRenderView(); if (!context_render_view) continue; if (render_view && render_view != context_render_view) continue; callback.Run(context); } } ScriptContextSet::ContextSet ScriptContextSet::OnExtensionUnloaded( const std::string& extension_id) { ContextSet contexts = GetAll(); ContextSet removed; // Clean up contexts belonging to the unloaded extension. This is done so // that content scripts (which remain injected into the page) don't continue // receiving events and sending messages. for (ContextSet::iterator it = contexts.begin(); it != contexts.end(); ++it) { if ((*it)->extension() && (*it)->extension()->id() == extension_id) { (*it)->DispatchOnUnloadEvent(); removed.insert(*it); Remove(*it); } } return removed; } } // namespace extensions