diff options
author | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-06 04:24:05 +0000 |
---|---|---|
committer | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-06 04:24:05 +0000 |
commit | 1eb5a2298dbc3dc3d30272847cdaa3e6b4d2791d (patch) | |
tree | d1e3f3daac83f66b73dc06daf5c1c8e58a88d3b0 /chrome/renderer/extensions | |
parent | 46018c9d614cd47961cad5a39416687ebdd8d1cd (diff) | |
download | chromium_src-1eb5a2298dbc3dc3d30272847cdaa3e6b4d2791d.zip chromium_src-1eb5a2298dbc3dc3d30272847cdaa3e6b4d2791d.tar.gz chromium_src-1eb5a2298dbc3dc3d30272847cdaa3e6b4d2791d.tar.bz2 |
Revert 99689 - Refactor the ContextInfo struct from bindings_utils into a
real class. Pull functionality from bindings_utils and
event_bindings in as methods. Simplify lifetime management.
Review URL: http://codereview.chromium.org/7792090
TBR=aa@chromium.org
Review URL: http://codereview.chromium.org/7800034
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99694 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer/extensions')
-rw-r--r-- | chrome/renderer/extensions/bindings_utils.cc | 131 | ||||
-rw-r--r-- | chrome/renderer/extensions/bindings_utils.h | 89 | ||||
-rw-r--r-- | chrome/renderer/extensions/chrome_app_bindings.cc | 1 | ||||
-rw-r--r-- | chrome/renderer/extensions/chrome_webstore_bindings.cc | 1 | ||||
-rw-r--r-- | chrome/renderer/extensions/event_bindings.cc | 270 | ||||
-rw-r--r-- | chrome/renderer/extensions/event_bindings.h | 38 | ||||
-rw-r--r-- | chrome/renderer/extensions/extension_api_json_validity_unittest.cc | 1 | ||||
-rw-r--r-- | chrome/renderer/extensions/extension_base.cc | 31 | ||||
-rw-r--r-- | chrome/renderer/extensions/extension_base.h | 8 | ||||
-rw-r--r-- | chrome/renderer/extensions/extension_bindings_context.cc | 235 | ||||
-rw-r--r-- | chrome/renderer/extensions/extension_bindings_context.h | 95 | ||||
-rw-r--r-- | chrome/renderer/extensions/extension_dispatcher.cc | 3 | ||||
-rw-r--r-- | chrome/renderer/extensions/extension_helper.cc | 3 | ||||
-rw-r--r-- | chrome/renderer/extensions/extension_process_bindings.cc | 39 | ||||
-rw-r--r-- | chrome/renderer/extensions/renderer_extension_bindings.cc | 1 |
15 files changed, 537 insertions, 409 deletions
diff --git a/chrome/renderer/extensions/bindings_utils.cc b/chrome/renderer/extensions/bindings_utils.cc new file mode 100644 index 0000000..3cad5ea --- /dev/null +++ b/chrome/renderer/extensions/bindings_utils.cc @@ -0,0 +1,131 @@ +// Copyright (c) 2011 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 "chrome/renderer/extensions/bindings_utils.h" + +#include "base/lazy_instance.h" +#include "base/string_split.h" +#include "base/string_util.h" +#include "content/renderer/render_view.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" +#include "v8/include/v8.h" + +using WebKit::WebFrame; +using WebKit::WebView; + +namespace bindings_utils { + +const char* kChromeHidden = "chromeHidden"; +const char* kValidateCallbacks = "validateCallbacks"; + +struct SingletonData { + ContextList contexts; + PendingRequestMap pending_requests; +}; +static base::LazyInstance<SingletonData> g_singleton_data( + base::LINKER_INITIALIZED); + +ContextInfo::ContextInfo(v8::Persistent<v8::Context> context, + const std::string& extension_id, + WebFrame* frame) + : context(context), + extension_id(extension_id), + unsafe_frame(frame), + num_connected_events(0) { +} + +ContextInfo::~ContextInfo() {} + +ContextList& GetContexts() { + return g_singleton_data.Get().contexts; +} + +ContextInfo* GetInfoForCurrentContext() { + // This can happen in testing scenarios and v8::Context::GetCurrent() crashes + // if there is no JavaScript currently running. + if (!v8::Context::InContext()) + return NULL; + + v8::Local<v8::Context> context = v8::Context::GetCurrent(); + ContextList::iterator context_iter = FindContext(context); + if (context_iter == GetContexts().end()) + return NULL; + else + return context_iter->get(); +} + +v8::Handle<v8::Object> GetChromeHiddenForContext( + v8::Handle<v8::Context> context) { + v8::Local<v8::Object> global = context->Global(); + v8::Local<v8::Value> hidden = global->GetHiddenValue( + v8::String::New(kChromeHidden)); + + if (hidden.IsEmpty() || hidden->IsUndefined()) { + hidden = v8::Object::New(); + global->SetHiddenValue(v8::String::New(kChromeHidden), hidden); + +#ifndef NDEBUG + // Tell extension_process_bindings.js to validate callbacks and events + // against their schema definitions in api/extension_api.json. + v8::Local<v8::Object>::Cast(hidden) + ->Set(v8::String::New(kValidateCallbacks), v8::True()); +#endif + } + + DCHECK(hidden->IsObject()); + return v8::Local<v8::Object>::Cast(hidden); +} + +PendingRequest::PendingRequest(v8::Persistent<v8::Context> context, + const std::string& name) + : context(context), name(name) { +} + +PendingRequest::~PendingRequest() {} + +ContextList::iterator FindContext(v8::Handle<v8::Context> context) { + ContextList& all_contexts = GetContexts(); + + ContextList::iterator it = all_contexts.begin(); + for (; it != all_contexts.end(); ++it) { + if ((*it)->context == context) + break; + } + + return it; +} + +PendingRequestMap& GetPendingRequestMap() { + return g_singleton_data.Get().pending_requests; +} + +v8::Handle<v8::Value> CallFunctionInContext(v8::Handle<v8::Context> context, + const std::string& function_name, int argc, + v8::Handle<v8::Value>* argv) { + v8::Context::Scope context_scope(context); + + // Look up the function name, which may be a sub-property like + // "Port.dispatchOnMessage" in the hidden global variable. + v8::Local<v8::Value> value = + context->Global()->GetHiddenValue(v8::String::New(kChromeHidden)); + std::vector<std::string> components; + base::SplitStringDontTrim(function_name, '.', &components); + for (size_t i = 0; i < components.size(); ++i) { + if (!value.IsEmpty() && value->IsObject()) + value = value->ToObject()->Get(v8::String::New(components[i].c_str())); + } + if (value.IsEmpty() || !value->IsFunction()) { + NOTREACHED(); + return v8::Undefined(); + } + + v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(value); + if (!function.IsEmpty()) + return function->Call(v8::Object::New(), argc, argv); + + return v8::Undefined(); +} + +} // namespace bindings_utils diff --git a/chrome/renderer/extensions/bindings_utils.h b/chrome/renderer/extensions/bindings_utils.h new file mode 100644 index 0000000..5318da9 --- /dev/null +++ b/chrome/renderer/extensions/bindings_utils.h @@ -0,0 +1,89 @@ +// Copyright (c) 2011 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 CHROME_RENDERER_EXTENSIONS_BINDINGS_UTILS_H_ +#define CHROME_RENDERER_EXTENSIONS_BINDINGS_UTILS_H_ +#pragma once + +#include "base/memory/linked_ptr.h" +#include "base/memory/singleton.h" +#include "v8/include/v8.h" + +#include <list> +#include <map> +#include <string> + +class RenderView; + +namespace WebKit { +class WebFrame; +} + +namespace bindings_utils { + +// Contains information about a JavaScript context that is hosting extension +// bindings. +struct ContextInfo { + ContextInfo(v8::Persistent<v8::Context> context, + const std::string& extension_id, + WebKit::WebFrame* frame); + ~ContextInfo(); + + v8::Persistent<v8::Context> context; + + // The extension ID this context is associated with. + std::string extension_id; + + // The frame the context is associated with. ContextInfo can outlive its + // frame, so this should not be dereferenced. It is stored only for use for + // comparison. + void* unsafe_frame; + + // A count of the number of events that are listening in this context. When + // this is zero, |context| will be a weak handle. + int num_connected_events; +}; +typedef std::list< linked_ptr<ContextInfo> > ContextList; + +// Returns a mutable reference to the ContextList. Note: be careful using this. +// Calling into javascript may result in the list being modified, so don't rely +// on iterators remaining valid between calls to javascript. +ContextList& GetContexts(); + +// Returns the ContextInfo item that has the given context. +ContextList::iterator FindContext(v8::Handle<v8::Context> context); + +// Returns the ContextInfo for the current v8 context. +ContextInfo* GetInfoForCurrentContext(); + +// Returns the 'chromeHidden' object for the specified context. +v8::Handle<v8::Object> GetChromeHiddenForContext( + v8::Handle<v8::Context> context); + +// Contains info relevant to a pending API request. +struct PendingRequest { + public : + PendingRequest(v8::Persistent<v8::Context> context, const std::string& name); + ~PendingRequest(); + + v8::Persistent<v8::Context> context; + std::string name; +}; +typedef std::map<int, linked_ptr<PendingRequest> > PendingRequestMap; + +// Returns a mutable reference to the PendingRequestMap. +PendingRequestMap& GetPendingRequestMap(); + +// Call the named javascript function with the given arguments in a context. +// The function name should be reachable from the chromeHidden object, and can +// be a sub-property like "Port.dispatchOnMessage". Returns the result of +// the function call. If an exception is thrown an empty Handle will be +// returned. +v8::Handle<v8::Value> CallFunctionInContext(v8::Handle<v8::Context> context, + const std::string& function_name, int argc, + v8::Handle<v8::Value>* argv); + +} // namespace bindings_utils + +#endif // CHROME_RENDERER_EXTENSIONS_BINDINGS_UTILS_H_ diff --git a/chrome/renderer/extensions/chrome_app_bindings.cc b/chrome/renderer/extensions/chrome_app_bindings.cc index b52b7ca..3f1ceba 100644 --- a/chrome/renderer/extensions/chrome_app_bindings.cc +++ b/chrome/renderer/extensions/chrome_app_bindings.cc @@ -12,6 +12,7 @@ #include "base/values.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_set.h" +#include "chrome/renderer/extensions/bindings_utils.h" #include "chrome/renderer/extensions/extension_dispatcher.h" #include "chrome/renderer/extensions/extension_helper.h" #include "content/renderer/render_view.h" diff --git a/chrome/renderer/extensions/chrome_webstore_bindings.cc b/chrome/renderer/extensions/chrome_webstore_bindings.cc index 1f32fa2..aa87e44 100644 --- a/chrome/renderer/extensions/chrome_webstore_bindings.cc +++ b/chrome/renderer/extensions/chrome_webstore_bindings.cc @@ -6,6 +6,7 @@ #include "base/string_util.h" #include "chrome/common/extensions/extension.h" +#include "chrome/renderer/extensions/bindings_utils.h" #include "chrome/renderer/extensions/extension_helper.h" #include "content/renderer/render_view.h" #include "googleurl/src/gurl.h" diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc index ed65e3f..9be737b 100644 --- a/chrome/renderer/extensions/event_bindings.cc +++ b/chrome/renderer/extensions/event_bindings.cc @@ -12,9 +12,10 @@ #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/extensions/extension_set.h" #include "chrome/common/url_constants.h" +#include "chrome/renderer/chrome_render_process_observer.h" +#include "chrome/renderer/extensions/bindings_utils.h" #include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/extension_base.h" -#include "chrome/renderer/extensions/extension_bindings_context.h" #include "chrome/renderer/extensions/extension_dispatcher.h" #include "chrome/renderer/extensions/extension_process_bindings.h" #include "chrome/renderer/extensions/js_only_v8_extensions.h" @@ -24,6 +25,7 @@ #include "content/renderer/v8_value_converter.h" #include "googleurl/src/gurl.h" #include "grit/renderer_resources.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" @@ -32,14 +34,32 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "v8/include/v8.h" +using bindings_utils::CallFunctionInContext; +using bindings_utils::ContextInfo; +using bindings_utils::ContextList; +using bindings_utils::GetContexts; +using bindings_utils::GetInfoForCurrentContext; +using bindings_utils::GetPendingRequestMap; +using bindings_utils::PendingRequestMap; +using WebKit::WebDataSource; +using WebKit::WebDocument; using WebKit::WebFrame; using WebKit::WebSecurityOrigin; using WebKit::WebURL; +static void ContextWeakReferenceCallback(v8::Persistent<v8::Value> context, + void*); + namespace { // Keep a local cache of RenderThread so that we can mock it out for unit tests. static RenderThreadBase* render_thread = NULL; +static bool in_unit_tests = false; + +// Set to true if these bindings are registered. Will be false when extensions +// are disabled. +static bool bindings_registered = false; + // A map of event names to the number of listeners for that event. We notify // the browser about event listeners when we transition between 0 and 1. @@ -85,11 +105,9 @@ class ExtensionImpl : public ExtensionBase { DCHECK(args[0]->IsString() || args[0]->IsUndefined()); if (args[0]->IsString()) { - ExtensionBindingsContext* context = - ExtensionBindingsContext::GetCurrent(); - CHECK(context); + ContextInfo* context_info = GetInfoForCurrentContext(); EventListenerCounts& listener_counts = - GetListenerCounts(context->extension_id()); + GetListenerCounts(context_info->extension_id); std::string event_name(*v8::String::AsciiValue(args[0])); ExtensionImpl* v8_extension = GetFromArguments<ExtensionImpl>(args); @@ -98,9 +116,13 @@ class ExtensionImpl : public ExtensionBase { if (++listener_counts[event_name] == 1) { EventBindings::GetRenderThread()->Send( - new ExtensionHostMsg_AddListener(context->extension_id(), + new ExtensionHostMsg_AddListener(context_info->extension_id, event_name)); } + + if (++context_info->num_connected_events == 1) + context_info->context.ClearWeak(); + } return v8::Undefined(); @@ -112,19 +134,22 @@ class ExtensionImpl : public ExtensionBase { DCHECK(args[0]->IsString() || args[0]->IsUndefined()); if (args[0]->IsString()) { - ExtensionBindingsContext* context = - ExtensionBindingsContext::GetCurrent(); - if (!context) + ContextInfo* context_info = GetInfoForCurrentContext(); + if (!context_info) return v8::Undefined(); EventListenerCounts& listener_counts = - GetListenerCounts(context->extension_id()); + GetListenerCounts(context_info->extension_id); std::string event_name(*v8::String::AsciiValue(args[0])); if (--listener_counts[event_name] == 0) { EventBindings::GetRenderThread()->Send( - new ExtensionHostMsg_RemoveListener(context->extension_id(), + new ExtensionHostMsg_RemoveListener(context_info->extension_id, event_name)); } + + if (--context_info->num_connected_events == 0) { + context_info->context.MakeWeak(NULL, &ContextWeakReferenceCallback); + } } return v8::Undefined(); @@ -163,21 +188,244 @@ class ExtensionImpl : public ExtensionBase { } }; +// Returns true if the extension running in the given |context| has sufficient +// permissions to access the data. +static bool HasSufficientPermissions(RenderView* render_view, + const GURL& event_url) { + // During unit tests, we might be invoked without a v8 context. In these + // cases, we only allow empty event_urls and short-circuit before retrieving + // the render view from the current context. + if (!event_url.is_valid()) + return true; + + WebDocument document = render_view->webview()->mainFrame()->document(); + return GURL(document.url()).SchemeIs(chrome::kExtensionScheme) && + document.securityOrigin().canRequest(event_url); +} + } // namespace const char* EventBindings::kName = "chrome/EventBindings"; +const char* EventBindings::kTestingExtensionId = + "oooooooooooooooooooooooooooooooo"; v8::Extension* EventBindings::Get(ExtensionDispatcher* dispatcher) { static v8::Extension* extension = new ExtensionImpl(dispatcher); + bindings_registered = true; return extension; } // static void EventBindings::SetRenderThread(RenderThreadBase* thread) { render_thread = thread; + in_unit_tests = true; } // static RenderThreadBase* EventBindings::GetRenderThread() { return render_thread ? render_thread : RenderThread::current(); } + +static void DeferredUnload(v8::Persistent<v8::Context> context) { + v8::HandleScope handle_scope; + CallFunctionInContext(context, "dispatchOnUnload", 0, NULL); + context.Dispose(); + context.Clear(); +} + +static void UnregisterContext(ContextList::iterator context_iter, bool in_gc) { + // Notify the bindings that they're going away. + if (in_gc) { + // We shouldn't call back into javascript during a garbage collect. Do it + // later. We'll hang onto the context until this DeferredUnload is called. + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableFunction( + DeferredUnload, (*context_iter)->context)); + } else { + CallFunctionInContext((*context_iter)->context, "dispatchOnUnload", + 0, NULL); + } + + // Remove all pending requests for this context. + PendingRequestMap& pending_requests = GetPendingRequestMap(); + for (PendingRequestMap::iterator it = pending_requests.begin(); + it != pending_requests.end(); ) { + PendingRequestMap::iterator current = it++; + if (current->second->context == (*context_iter)->context) { + current->second->context.Dispose(); + current->second->context.Clear(); + pending_requests.erase(current); + } + } + + // Remove it from our registered contexts. + (*context_iter)->context.ClearWeak(); + if (!in_gc) { + (*context_iter)->context.Dispose(); + (*context_iter)->context.Clear(); + } + + GetContexts().erase(context_iter); +} + +static void ContextWeakReferenceCallback(v8::Persistent<v8::Value> context, + void*) { + // This should only get called for content script contexts. + for (ContextList::iterator it = GetContexts().begin(); + it != GetContexts().end(); ++it) { + if ((*it)->context == context) { + UnregisterContext(it, true); + return; + } + } + + NOTREACHED(); +} + +void EventBindings::HandleContextCreated( + WebFrame* frame, + v8::Handle<v8::Context> context, + ExtensionDispatcher* extension_dispatcher, + int isolated_world_id) { + if (!bindings_registered) + return; + + bool content_script = isolated_world_id != 0; + ContextList& contexts = GetContexts(); + + v8::Persistent<v8::Context> persistent_context; + std::string extension_id; + + if (content_script) { + // Content script contexts can get GCed before their frame goes away, so + // set up a GC callback. + persistent_context = v8::Persistent<v8::Context>::New(context); + persistent_context.MakeWeak(NULL, &ContextWeakReferenceCallback); + extension_id = + extension_dispatcher->user_script_slave()-> + GetExtensionIdForIsolatedWorld(isolated_world_id); + } else { + // Figure out the frame's URL. If the frame is loading, use its provisional + // URL, since we get this notification before commit. + WebDataSource* ds = frame->provisionalDataSource(); + if (!ds) + ds = frame->dataSource(); + GURL url = ds->request().url(); + const ExtensionSet* extensions = extension_dispatcher->extensions(); + extension_id = extensions->GetIdByURL(url); + + if (!extensions->ExtensionBindingsAllowed(url)) { + // This context is a regular non-extension web page or an unprivileged + // chrome app. Ignore it. We only care about content scripts and extension + // frames. + // (Unless we're in unit tests, in which case we don't care what the URL + // is). + if (!in_unit_tests) + return; + + // For tests, we want the dispatchOnLoad to actually setup our bindings, + // so we give a fake extension id; + extension_id = kTestingExtensionId; + } + + persistent_context = v8::Persistent<v8::Context>::New(context); + } + + contexts.push_back(linked_ptr<ContextInfo>( + new ContextInfo(persistent_context, extension_id, frame))); + + v8::HandleScope handle_scope; + v8::Handle<v8::Value> argv[3]; + argv[0] = v8::String::New(extension_id.c_str()); + argv[1] = v8::Boolean::New(extension_dispatcher->is_extension_process()); + argv[2] = v8::Boolean::New( + ChromeRenderProcessObserver::is_incognito_process()); + CallFunctionInContext(context, "dispatchOnLoad", arraysize(argv), argv); +} + +// static +void EventBindings::HandleContextDestroyed(WebFrame* frame) { + if (!bindings_registered) + return; + + v8::HandleScope handle_scope; + v8::Local<v8::Context> context = frame->mainWorldScriptContext(); + if (!context.IsEmpty()) { + ContextList::iterator context_iter = bindings_utils::FindContext(context); + if (context_iter != GetContexts().end()) + UnregisterContext(context_iter, false); + } + + // Unload any content script contexts for this frame. Note that the frame + // itself might not be registered, but can still be a parent frame. + for (ContextList::iterator it = GetContexts().begin(); + it != GetContexts().end(); ) { + if ((*it)->unsafe_frame == frame) { + UnregisterContext(it, false); + // UnregisterContext will remove |it| from the list, but may also + // modify the rest of the list as a result of calling into javascript. + it = GetContexts().begin(); + } else { + ++it; + } + } +} + +// static +void EventBindings::CallFunction(const std::string& extension_id, + const std::string& function_name, + const ListValue& arguments, + RenderView* render_view, + const GURL& event_url) { + v8::HandleScope handle_scope; + + // We copy the context list, because calling into javascript may modify it + // out from under us. We also guard against deleted contexts by checking if + // they have been cleared first. + ContextList contexts = GetContexts(); + + V8ValueConverter converter; + for (ContextList::iterator it = contexts.begin(); + it != contexts.end(); ++it) { + if ((*it)->context.IsEmpty()) + continue; + + if (!extension_id.empty() && extension_id != (*it)->extension_id) + continue; + + WebFrame* context_frame = WebFrame::frameForContext((*it)->context); + if (!context_frame || !context_frame->view()) + continue; + + RenderView* context_render_view = + RenderView::FromWebView(context_frame->view()); + if (!context_render_view) + continue; + + if (render_view && render_view != context_render_view) + continue; + + if (!HasSufficientPermissions(context_render_view, event_url)) + continue; + + v8::Local<v8::Context> context(*((*it)->context)); + std::vector<v8::Handle<v8::Value> > v8_arguments; + for (size_t i = 0; i < arguments.GetSize(); ++i) { + Value* item = NULL; + CHECK(arguments.Get(i, &item)); + v8_arguments.push_back(converter.ToV8Value(item, context)); + } + + v8::Handle<v8::Value> retval = CallFunctionInContext( + context, function_name, v8_arguments.size(), &v8_arguments[0]); + // In debug, the js will validate the event parameters and return a + // string if a validation error has occured. + // TODO(rafaelw): Consider only doing this check if function_name == + // "Event.dispatchJSON". +#ifndef NDEBUG + if (!retval.IsEmpty() && !retval->IsUndefined()) { + std::string error = *v8::String::AsciiValue(retval); + DCHECK(false) << error; + } +#endif + } +} diff --git a/chrome/renderer/extensions/event_bindings.h b/chrome/renderer/extensions/event_bindings.h index 4599217..75deefb 100644 --- a/chrome/renderer/extensions/event_bindings.h +++ b/chrome/renderer/extensions/event_bindings.h @@ -6,21 +6,57 @@ #define CHROME_RENDERER_EXTENSIONS_EVENT_BINDINGS_H_ #pragma once -#include "v8/include/v8.h" +#include <string> class ExtensionDispatcher; +class GURL; class RenderThreadBase; +class RenderView; + +namespace base { +class ListValue; +} + +namespace v8 { +class Context; +class Extension; +template <class T> class Handle; +} + +namespace WebKit { +class WebFrame; +} // This class deals with the javascript bindings related to Event objects. class EventBindings { public: static const char* kName; // The v8::Extension name, for dependencies. + static const char* kTestingExtensionId; static v8::Extension* Get(ExtensionDispatcher* dispatcher); // Allow RenderThread to be mocked out. static void SetRenderThread(RenderThreadBase* thread); static RenderThreadBase* GetRenderThread(); + + // Handle a script context coming / going away. + static void HandleContextCreated(WebKit::WebFrame* frame, + v8::Handle<v8::Context> context, + ExtensionDispatcher* extension_dispatcher, + int isolated_world_id); + static void HandleContextDestroyed(WebKit::WebFrame* frame); + + // Calls the given function in each registered context which is listening for + // events. If render_view is non-NULL, only call the function in contexts + // belonging to that view. See comments on + // bindings_utils::CallFunctionInContext for more details. + // The called javascript function should not return a value other than + // v8::Undefined(). A DCHECK is setup to break if it is otherwise. + static void CallFunction(const std::string& extension_id, + const std::string& function_name, + const base::ListValue& arguments, + RenderView* render_view, + const GURL& event_url); }; #endif // CHROME_RENDERER_EXTENSIONS_EVENT_BINDINGS_H_ diff --git a/chrome/renderer/extensions/extension_api_json_validity_unittest.cc b/chrome/renderer/extensions/extension_api_json_validity_unittest.cc index f382923..3270ec9 100644 --- a/chrome/renderer/extensions/extension_api_json_validity_unittest.cc +++ b/chrome/renderer/extensions/extension_api_json_validity_unittest.cc @@ -7,6 +7,7 @@ #include "base/memory/scoped_ptr.h" #include "base/path_service.h" #include "chrome/common/chrome_paths.h" +#include "chrome/renderer/extensions/bindings_utils.h" #include "chrome/renderer/extensions/extension_base.h" #include "chrome/test/base/v8_unit_test.h" #include "content/common/json_value_serializer.h" diff --git a/chrome/renderer/extensions/extension_base.cc b/chrome/renderer/extensions/extension_base.cc index 002c3b1..ce66101 100644 --- a/chrome/renderer/extensions/extension_base.cc +++ b/chrome/renderer/extensions/extension_base.cc @@ -10,6 +10,7 @@ #include "base/string_util.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_set.h" +#include "chrome/renderer/extensions/bindings_utils.h" #include "chrome/renderer/extensions/extension_dispatcher.h" #include "content/renderer/render_view.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" @@ -23,12 +24,6 @@ using WebKit::WebView; namespace { -const char* kChromeHidden = "chromeHidden"; - -#ifndef NDEBUG -const char* kValidateCallbacks = "validateCallbacks"; -#endif - typedef std::map<int, std::string> StringMap; static base::LazyInstance<StringMap> g_string_map(base::LINKER_INITIALIZED); @@ -108,29 +103,7 @@ v8::Handle<v8::FunctionTemplate> v8::Handle<v8::Value> ExtensionBase::GetChromeHidden( const v8::Arguments& args) { - return GetChromeHidden(v8::Context::GetCurrent()); -} - -v8::Handle<v8::Value> ExtensionBase::GetChromeHidden( - const v8::Handle<v8::Context>& context) { - v8::Local<v8::Object> global = context->Global(); - v8::Local<v8::Value> hidden = global->GetHiddenValue( - v8::String::New(kChromeHidden)); - - if (hidden.IsEmpty() || hidden->IsUndefined()) { - hidden = v8::Object::New(); - global->SetHiddenValue(v8::String::New(kChromeHidden), hidden); - -#ifndef NDEBUG - // Tell extension_process_bindings.js to validate callbacks and events - // against their schema definitions in api/extension_api.json. - v8::Local<v8::Object>::Cast(hidden) - ->Set(v8::String::New(kValidateCallbacks), v8::True()); -#endif - } - - DCHECK(hidden->IsObject()); - return v8::Local<v8::Object>::Cast(hidden); + return bindings_utils::GetChromeHiddenForContext(v8::Context::GetCurrent()); } v8::Handle<v8::Value> ExtensionBase::Print(const v8::Arguments& args) { diff --git a/chrome/renderer/extensions/extension_base.h b/chrome/renderer/extensions/extension_base.h index 9e9efb1..3594f39 100644 --- a/chrome/renderer/extensions/extension_base.h +++ b/chrome/renderer/extensions/extension_base.h @@ -43,11 +43,6 @@ class ExtensionBase : public v8::Extension { // TODO(jstritar): Used for testing http://crbug.com/91582. Remove when done. ExtensionDispatcher* extension_dispatcher() { return extension_dispatcher_; } - // Returns a hidden variable for use by the bindings in the specified context - // that is unreachable by the page for the current context. - static v8::Handle<v8::Value> GetChromeHidden( - const v8::Handle<v8::Context>& context); - protected: template<class T> static T* GetFromArguments(const v8::Arguments& args) { @@ -70,7 +65,8 @@ class ExtensionBase : public v8::Extension { bool CheckPermissionForCurrentRenderView( const std::string& function_name) const; - // Returns the chromeHidden object for the current context. + // Returns a hidden variable for use by the bindings that is unreachable + // by the page. static v8::Handle<v8::Value> GetChromeHidden(const v8::Arguments& args); ExtensionDispatcher* extension_dispatcher_; diff --git a/chrome/renderer/extensions/extension_bindings_context.cc b/chrome/renderer/extensions/extension_bindings_context.cc deleted file mode 100644 index 1d3e1eb..0000000 --- a/chrome/renderer/extensions/extension_bindings_context.cc +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) 2011 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 "chrome/renderer/extensions/extension_bindings_context.h" - -#include <string> -#include <vector> - -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/memory/linked_ptr.h" -#include "base/string_split.h" -#include "base/values.h" -#include "chrome/common/extensions/extension.h" -#include "chrome/common/extensions/extension_set.h" -#include "chrome/renderer/chrome_render_process_observer.h" -#include "chrome/renderer/extensions/extension_base.h" -#include "chrome/renderer/extensions/extension_dispatcher.h" -#include "chrome/renderer/extensions/user_script_slave.h" -#include "content/common/url_constants.h" -#include "content/renderer/render_view.h" -#include "content/renderer/v8_value_converter.h" -#include "googleurl/src/gurl.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" -#include "v8/include/v8.h" - -namespace { - -typedef std::vector<linked_ptr<ExtensionBindingsContext> > ContextList; -base::LazyInstance<ContextList> g_contexts(base::LINKER_INITIALIZED); - -base::LazyInstance<std::string> g_testing_id(base::LINKER_INITIALIZED); - -// Returns true if the extension running in the given |render_view| has -// sufficient permissions to access the data. -static bool HasSufficientPermissions(RenderView* render_view, - const GURL& event_url) { - // During unit tests, we might be invoked without a v8 context. In these - // cases, we only allow empty event_urls and short-circuit before retrieving - // the render view from the current context. - if (!event_url.is_valid()) - return true; - - // TODO(aa): This looks super suspicious. - WebKit::WebDocument document = - render_view->webview()->mainFrame()->document(); - return GURL(document.url()).SchemeIs(chrome::kExtensionScheme) && - document.securityOrigin().canRequest(event_url); -} - -} - -void ExtensionBindingsContext::HandleV8ContextCreated( - WebKit::WebFrame* web_frame, - const v8::Handle<v8::Context>& v8_context, - ExtensionDispatcher* extension_dispatcher, - int isolated_world_id) { - - std::string extension_id; - if (!g_testing_id.Get().empty()) { - extension_id = g_testing_id.Get(); - } else if (isolated_world_id == 0) { - // Figure out the frame's URL. If the frame is loading, use its provisional - // URL, since we get this notification before commit. - WebKit::WebDataSource* ds = web_frame->provisionalDataSource(); - if (!ds) - ds = web_frame->dataSource(); - GURL url = ds->request().url(); - const ExtensionSet* extensions = extension_dispatcher->extensions(); - extension_id = extensions->GetIdByURL(url); - - if (!extensions->ExtensionBindingsAllowed(url)) - return; - } else { - extension_id = - extension_dispatcher->user_script_slave()-> - GetExtensionIdForIsolatedWorld(isolated_world_id); - } - - ExtensionBindingsContext* instance = - new ExtensionBindingsContext(v8_context, web_frame, extension_id); - g_contexts.Pointer()->push_back( - linked_ptr<ExtensionBindingsContext>(instance)); - VLOG(1) << "Num tracked contexts: " << g_contexts.Pointer()->size(); - - v8::HandleScope handle_scope; - v8::Handle<v8::Value> argv[3]; - argv[0] = v8::String::New(extension_id.c_str()); - argv[1] = v8::Boolean::New(extension_dispatcher->is_extension_process()); - argv[2] = v8::Boolean::New( - ChromeRenderProcessObserver::is_incognito_process()); - instance->CallChromeHiddenMethod("dispatchOnLoad", arraysize(argv), argv); -} - -void ExtensionBindingsContext::HandleV8ContextDestroyed( - WebKit::WebFrame* web_frame) { - for (ContextList::iterator iter = g_contexts.Pointer()->begin(); - iter != g_contexts.Pointer()->end();) { - if ((*iter)->web_frame() == web_frame) - iter = g_contexts.Pointer()->erase(iter); - else - ++iter; - } - VLOG(1) << "Num tracked contexts: " << g_contexts.Pointer()->size(); -} - -ExtensionBindingsContext* ExtensionBindingsContext::GetCurrent() { - return GetByV8Context(v8::Context::GetCurrent()); -} - -ExtensionBindingsContext* ExtensionBindingsContext::GetByV8Context( - const v8::Handle<v8::Context>& v8_context) { - CHECK(!v8_context.IsEmpty()); - - for (ContextList::iterator iter = g_contexts.Pointer()->begin(); - iter != g_contexts.Pointer()->end(); ++iter) { - if ((*iter)->v8_context() == v8_context) - return iter->get(); - } - - return NULL; -} - -void ExtensionBindingsContext::DispatchChromeHiddenMethod( - const std::string& extension_id, - const std::string& method_name, - const base::ListValue& arguments, - RenderView* render_view, - const GURL& event_url) { - v8::HandleScope handle_scope; - - // We copy the context list, because calling into javascript may modify it - // out from under us. We also guard against deleted contexts by checking if - // they have been cleared first. - ContextList contexts = g_contexts.Get(); - - V8ValueConverter converter; - for (ContextList::iterator it = contexts.begin(); it != contexts.end(); - ++it) { - if ((*it)->v8_context().IsEmpty()) - continue; - - if (!extension_id.empty() && extension_id != (*it)->extension_id()) - continue; - - if (!(*it)->web_frame()->view()) - continue; - - RenderView* context_render_view = - RenderView::FromWebView((*it)->web_frame()->view()); - if (!context_render_view) - continue; - - if (render_view && render_view != context_render_view) - continue; - - if (!HasSufficientPermissions(context_render_view, event_url)) - continue; - - v8::Local<v8::Context> context(*((*it)->v8_context())); - std::vector<v8::Handle<v8::Value> > v8_arguments; - for (size_t i = 0; i < arguments.GetSize(); ++i) { - base::Value* item = NULL; - CHECK(arguments.Get(i, &item)); - v8_arguments.push_back(converter.ToV8Value(item, context)); - } - - v8::Handle<v8::Value> retval = (*it)->CallChromeHiddenMethod( - method_name, v8_arguments.size(), &v8_arguments[0]); - // In debug, the js will validate the event parameters and return a - // string if a validation error has occured. - // TODO(rafaelw): Consider only doing this check if function_name == - // "Event.dispatchJSON". -#ifndef NDEBUG - if (!retval.IsEmpty() && !retval->IsUndefined()) { - std::string error = *v8::String::AsciiValue(retval); - DCHECK(false) << error; - } -#endif - } -} - -void ExtensionBindingsContext::SetTestExtensionId(const std::string& id) { - g_testing_id.Get() = id; -} - -ExtensionBindingsContext::ExtensionBindingsContext( - v8::Handle<v8::Context> v8_context, - WebKit::WebFrame* web_frame, - const std::string& extension_id) - : v8_context_(v8::Persistent<v8::Context>::New(v8_context)), - web_frame_(web_frame), - extension_id_(extension_id) { - VLOG(1) << "Created context for extension\n" - << " id: " << extension_id << "\n" - << " frame: " << web_frame_; -} - -ExtensionBindingsContext::~ExtensionBindingsContext() { - VLOG(1) << "Destroyed context for extension\n" - << " id: " << extension_id_ << "\n" - << " frame: " << web_frame_; - v8::HandleScope handle_scope; - CallChromeHiddenMethod("dispatchOnUnload", 0, NULL); - v8_context_.Dispose(); -} - -v8::Handle<v8::Value> ExtensionBindingsContext::CallChromeHiddenMethod( - const std::string& function_name, - int argc, - v8::Handle<v8::Value>* argv) { - v8::Context::Scope context_scope(v8_context_); - - // Look up the function name, which may be a sub-property like - // "Port.dispatchOnMessage" in the hidden global variable. - v8::Local<v8::Value> value = v8::Local<v8::Value>::New( - ExtensionBase::GetChromeHidden(v8_context_)); - std::vector<std::string> components; - base::SplitStringDontTrim(function_name, '.', &components); - for (size_t i = 0; i < components.size(); ++i) { - if (!value.IsEmpty()) { - value = v8::Local<v8::Object>::Cast(value)->Get( - v8::String::New(components[i].c_str())); - } - } - CHECK(!value.IsEmpty() && value->IsFunction()); - return v8::Local<v8::Function>::Cast(value)->Call( - v8::Object::New(), argc, argv); -} diff --git a/chrome/renderer/extensions/extension_bindings_context.h b/chrome/renderer/extensions/extension_bindings_context.h deleted file mode 100644 index 44f8b0a..0000000 --- a/chrome/renderer/extensions/extension_bindings_context.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2011 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 CHROME_RENDERER_EXTENSIONS_EXTENSION_BINDINGS_CONTEXT_H_ -#define CHROME_RENDERER_EXTENSIONS_EXTENSION_BINDINGS_CONTEXT_H_ -#pragma once - -#include <string> - -#include "v8/include/v8.h" - -namespace base { -class ListValue; -} - -namespace WebKit { -class WebFrame; -} - -class ExtensionDispatcher; -class GURL; -class RenderView; - -// A v8 context that contains extension bindings. -class ExtensionBindingsContext { - public: - static void HandleV8ContextCreated(WebKit::WebFrame* frame, - const v8::Handle<v8::Context>& v8_context, - ExtensionDispatcher* extension_dispatcher, - int isolated_world_id); - - static void HandleV8ContextDestroyed(WebKit::WebFrame* frame); - - static ExtensionBindingsContext* GetCurrent(); - - static ExtensionBindingsContext* GetByV8Context( - const v8::Handle<v8::Context>& v8_context); - - // Calls chromeHidden.<methodName> in each context for <extension_id>. If - // render_view is non-NULL, only call the function in contexts belonging to - // that view. The called javascript function should not return a value other - // than v8::Undefined(). A DCHECK is setup to break if it is otherwise. - static void DispatchChromeHiddenMethod(const std::string& extension_id, - const std::string& method_name, - const base::ListValue& arguments, - RenderView* render_view, - const GURL& event_url); - - static void SetTestExtensionId(const std::string&); - - ~ExtensionBindingsContext(); - - v8::Handle<v8::Context> v8_context() const { - return v8_context_; - } - - const std::string& extension_id() const { - return extension_id_; - } - - WebKit::WebFrame* web_frame() const { - return web_frame_; - } - - // Call the named method of the chromeHidden object in this context. - // The function can be a sub-property like "Port.dispatchOnMessage". Returns - // the result of the function call. If an exception is thrown an empty Handle - // will be returned. - v8::Handle<v8::Value> CallChromeHiddenMethod(const std::string& function_name, - int argc, - v8::Handle<v8::Value>* argv); - - private: - ExtensionBindingsContext(v8::Handle<v8::Context> context, - WebKit::WebFrame* frame, - const std::string& extension_id); - - // The v8 context the bindings are accessible to. We keep a strong reference - // to it for simplicity. In the case of content scripts, this is necessary - // because we want all scripts from the same extension for the same frame to - // run in the same context, so we can't have the contexts being GC'd if - // nothing is happening. In the case of page contexts, this isn't necessary - // since the DOM keeps the context alive, but it makes things simpler to not - // distinguish the two cases. - v8::Persistent<v8::Context> v8_context_; - - // The WebFrame associated with this context. - WebKit::WebFrame* web_frame_; - - // The extension ID this context is associated with. - std::string extension_id_; -}; - -#endif // CHROME_RENDERER_EXTENSIONS_EXTENSION_BINDINGS_CONTEXT_H_ diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc index 5baa8d4..d2f06ef 100644 --- a/chrome/renderer/extensions/extension_dispatcher.cc +++ b/chrome/renderer/extensions/extension_dispatcher.cc @@ -14,7 +14,6 @@ #include "chrome/renderer/extensions/chrome_app_bindings.h" #include "chrome/renderer/extensions/chrome_webstore_bindings.h" #include "chrome/renderer/extensions/event_bindings.h" -#include "chrome/renderer/extensions/extension_bindings_context.h" #include "chrome/renderer/extensions/extension_groups.h" #include "chrome/renderer/extensions/extension_process_bindings.h" #include "chrome/renderer/extensions/js_only_v8_extensions.h" @@ -136,7 +135,7 @@ void ExtensionDispatcher::OnMessageInvoke(const std::string& extension_id, const std::string& function_name, const ListValue& args, const GURL& event_url) { - ExtensionBindingsContext::DispatchChromeHiddenMethod( + EventBindings::CallFunction( extension_id, function_name, args, NULL, event_url); // Reset the idle handler each time there's any activity like event or message diff --git a/chrome/renderer/extensions/extension_helper.cc b/chrome/renderer/extensions/extension_helper.cc index e1a1e27..a3df162c 100644 --- a/chrome/renderer/extensions/extension_helper.cc +++ b/chrome/renderer/extensions/extension_helper.cc @@ -13,7 +13,6 @@ #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" #include "chrome/renderer/extensions/event_bindings.h" -#include "chrome/renderer/extensions/extension_bindings_context.h" #include "chrome/renderer/extensions/extension_dispatcher.h" #include "chrome/renderer/extensions/extension_process_bindings.h" #include "chrome/renderer/extensions/user_script_idle_scheduler.h" @@ -204,7 +203,7 @@ void ExtensionHelper::OnExtensionMessageInvoke(const std::string& extension_id, const std::string& function_name, const ListValue& args, const GURL& event_url) { - ExtensionBindingsContext::DispatchChromeHiddenMethod( + EventBindings::CallFunction( extension_id, function_name, args, render_view(), event_url); } diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc index 9cdc396..128375b 100644 --- a/chrome/renderer/extensions/extension_process_bindings.cc +++ b/chrome/renderer/extensions/extension_process_bindings.cc @@ -4,7 +4,6 @@ #include "chrome/renderer/extensions/extension_process_bindings.h" -#include <map> #include <set> #include <string> #include <vector> @@ -12,7 +11,6 @@ #include "base/callback.h" #include "base/command_line.h" #include "base/json/json_reader.h" -#include "base/lazy_instance.h" #include "base/memory/scoped_ptr.h" #include "base/string_number_conversions.h" #include "base/string_util.h" @@ -25,8 +23,8 @@ #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" #include "chrome/renderer/chrome_render_process_observer.h" +#include "chrome/renderer/extensions/bindings_utils.h" #include "chrome/renderer/extensions/event_bindings.h" -#include "chrome/renderer/extensions/extension_bindings_context.h" #include "chrome/renderer/extensions/extension_base.h" #include "chrome/renderer/extensions/extension_dispatcher.h" #include "chrome/renderer/extensions/extension_helper.h" @@ -44,6 +42,9 @@ #include "third_party/skia/include/core/SkColor.h" #include "webkit/glue/webkit_glue.h" +using bindings_utils::GetPendingRequestMap; +using bindings_utils::PendingRequest; +using bindings_utils::PendingRequestMap; using WebKit::WebFrame; using WebKit::WebView; @@ -57,20 +58,6 @@ const char* kExtensionDeps[] = { ExtensionApiTestV8Extension::kName, }; -// Contains info relevant to a pending API request. -struct PendingRequest { - public : - PendingRequest(v8::Persistent<v8::Context> context, const std::string& name) - : context(context), name(name) { - } - v8::Persistent<v8::Context> context; - std::string name; -}; -typedef std::map<int, linked_ptr<PendingRequest> > PendingRequestMap; - -base::LazyInstance<PendingRequestMap> g_pending_requests( - base::LINKER_INITIALIZED); - // A RenderViewVisitor class that iterates through the set of available // views, looking for a view of the given type, in the given browser window // and within the given extension. @@ -442,7 +429,7 @@ class ExtensionImpl : public ExtensionBase { v8::Persistent<v8::Context> current_context = v8::Persistent<v8::Context>::New(v8::Context::GetCurrent()); DCHECK(!current_context.IsEmpty()); - g_pending_requests.Get()[request_id].reset(new PendingRequest( + GetPendingRequestMap()[request_id].reset(new PendingRequest( current_context, name)); ExtensionHostMsg_Request_Params params; @@ -573,9 +560,9 @@ v8::Extension* ExtensionProcessBindings::Get( void ExtensionProcessBindings::HandleResponse(int request_id, bool success, const std::string& response, const std::string& error) { - PendingRequestMap::iterator request = - g_pending_requests.Get().find(request_id); - if (request == g_pending_requests.Get().end()) + PendingRequestMap& pending_requests = GetPendingRequestMap(); + PendingRequestMap::iterator request = pending_requests.find(request_id); + if (request == pending_requests.end()) return; // The frame went away. v8::HandleScope handle_scope; @@ -585,12 +572,8 @@ void ExtensionProcessBindings::HandleResponse(int request_id, bool success, argv[2] = v8::Boolean::New(success); argv[3] = v8::String::New(response.c_str()); argv[4] = v8::String::New(error.c_str()); - ExtensionBindingsContext* bindings_context = - ExtensionBindingsContext::GetByV8Context(request->second->context); - v8::Handle<v8::Value> retval = - bindings_context->CallChromeHiddenMethod("handleResponse", - arraysize(argv), - argv); + v8::Handle<v8::Value> retval = bindings_utils::CallFunctionInContext( + request->second->context, "handleResponse", arraysize(argv), argv); // In debug, the js will validate the callback parameters and return a // string if a validation error has occured. #ifndef NDEBUG @@ -602,5 +585,5 @@ void ExtensionProcessBindings::HandleResponse(int request_id, bool success, request->second->context.Dispose(); request->second->context.Clear(); - g_pending_requests.Get().erase(request); + pending_requests.erase(request); } diff --git a/chrome/renderer/extensions/renderer_extension_bindings.cc b/chrome/renderer/extensions/renderer_extension_bindings.cc index cb0e8be..3e4b25d 100644 --- a/chrome/renderer/extensions/renderer_extension_bindings.cc +++ b/chrome/renderer/extensions/renderer_extension_bindings.cc @@ -12,6 +12,7 @@ #include "chrome/common/extensions/extension_message_bundle.h" #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/url_constants.h" +#include "chrome/renderer/extensions/bindings_utils.h" #include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/extension_base.h" #include "chrome/renderer/extensions/extension_dispatcher.h" |