diff options
author | mark@chromium.org <mark@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-09 18:07:23 +0000 |
---|---|---|
committer | mark@chromium.org <mark@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-09 18:07:23 +0000 |
commit | f0d6178322aeca21201c807c1368e7e753dcfe6f (patch) | |
tree | 237e432dbdec82f3eb43cd6cfeab7f882677bfc7 | |
parent | 376ec3bf33e294d7b771e04859dec122e8d2268d (diff) | |
download | chromium_src-f0d6178322aeca21201c807c1368e7e753dcfe6f.zip chromium_src-f0d6178322aeca21201c807c1368e7e753dcfe6f.tar.gz chromium_src-f0d6178322aeca21201c807c1368e7e753dcfe6f.tar.bz2 |
Merge trunk r100434 to the 15.0.874 branch.
Revert trunk r99696 for real.
r100067 purported to revert this along with r99705, but it only addressed
r99705. r99696 is causing Mac crashes in
ExtensionBindingsContext::DispatchChromeHiddenMethod.
Reland r99689: 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/7834035
BUG=95667
TEST=No more Mac crashes in ExtensionBindingsContext::DispatchChromeHiddenMethod
Review URL: http://codereview.chromium.org/7859035
Review URL: http://codereview.chromium.org/7865003
git-svn-id: svn://svn.chromium.org/chrome/branches/874/src@100436 0039d316-1c4b-4281-b951-d872f2087c98
18 files changed, 560 insertions, 443 deletions
diff --git a/chrome/browser/extensions/extension_messages_browsertest.cc b/chrome/browser/extensions/extension_messages_browsertest.cc index 242824c..ace9c0c 100644 --- a/chrome/browser/extensions/extension_messages_browsertest.cc +++ b/chrome/browser/extensions/extension_messages_browsertest.cc @@ -7,48 +7,44 @@ #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/render_messages.h" -#include "chrome/renderer/extensions/extension_bindings_context.h" +#include "chrome/renderer/extensions/event_bindings.h" #include "chrome/test/base/render_view_test.h" #include "content/common/view_messages.h" #include "testing/gtest/include/gtest/gtest.h" -namespace { - -static const char kTestingExtensionId[] = "oooooooooooooooooooooooooooooooo"; - -void DispatchOnConnect(int source_port_id, const std::string& name, +static void DispatchOnConnect(int source_port_id, const std::string& name, const std::string& tab_json) { ListValue args; args.Set(0, Value::CreateIntegerValue(source_port_id)); args.Set(1, Value::CreateStringValue(name)); args.Set(2, Value::CreateStringValue(tab_json)); - args.Set(3, Value::CreateStringValue(kTestingExtensionId)); - args.Set(4, Value::CreateStringValue(kTestingExtensionId)); - ExtensionBindingsContext::DispatchChromeHiddenMethod( + // Testing extensionId. Set in EventBindings::HandleContextCreated. + // We use the same id for source & target to similute an extension "talking + // to itself". + args.Set(3, Value::CreateStringValue(EventBindings::kTestingExtensionId)); + args.Set(4, Value::CreateStringValue(EventBindings::kTestingExtensionId)); + EventBindings::CallFunction( "", ExtensionMessageService::kDispatchOnConnect, args, NULL, GURL()); } -void DispatchOnDisconnect(int source_port_id) { +static void DispatchOnDisconnect(int source_port_id) { ListValue args; args.Set(0, Value::CreateIntegerValue(source_port_id)); - ExtensionBindingsContext::DispatchChromeHiddenMethod( + EventBindings::CallFunction( "", ExtensionMessageService::kDispatchOnDisconnect, args, NULL, GURL()); } -void DispatchOnMessage(const std::string& message, int source_port_id) { +static void DispatchOnMessage(const std::string& message, int source_port_id) { ListValue args; args.Set(0, Value::CreateStringValue(message)); args.Set(1, Value::CreateIntegerValue(source_port_id)); - ExtensionBindingsContext::DispatchChromeHiddenMethod( + EventBindings::CallFunction( "", ExtensionMessageService::kDispatchOnMessage, args, NULL, GURL()); } -} - // Tests that the bindings for opening a channel to an extension and sending // and receiving messages through that channel all works. TEST_F(RenderViewTest, ExtensionMessagesOpenChannel) { - ExtensionBindingsContext::SetTestExtensionId(kTestingExtensionId); render_thread_.sink().ClearMessages(); LoadHTML("<body></body>"); ExecuteJavaScript( @@ -96,7 +92,6 @@ TEST_F(RenderViewTest, ExtensionMessagesOpenChannel) { // Tests that the bindings for handling a new channel connection and channel // closing all works. TEST_F(RenderViewTest, ExtensionMessagesOnConnect) { - ExtensionBindingsContext::SetTestExtensionId(kTestingExtensionId); LoadHTML("<body></body>"); ExecuteJavaScript( "chrome.extension.onConnect.addListener(function (port) {" diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index 4b81d12..3e80022 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -55,6 +55,8 @@ 'renderer/automation/dom_automation_controller.h', 'renderer/automation/dom_automation_v8_extension.cc', 'renderer/automation/dom_automation_v8_extension.h', + 'renderer/extensions/bindings_utils.cc', + 'renderer/extensions/bindings_utils.h', 'renderer/extensions/chrome_app_bindings.cc', 'renderer/extensions/chrome_app_bindings.h', 'renderer/extensions/chrome_webstore_bindings.cc', @@ -63,8 +65,6 @@ 'renderer/extensions/event_bindings.h', 'renderer/extensions/extension_base.cc', 'renderer/extensions/extension_base.h', - 'renderer/extensions/extension_bindings_context.cc', - 'renderer/extensions/extension_bindings_context.h', 'renderer/extensions/extension_dispatcher.cc', 'renderer/extensions/extension_dispatcher.h', 'renderer/extensions/extension_groups.h', diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index 5fb7168..7becf4a 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc @@ -33,7 +33,8 @@ #include "chrome/renderer/chrome_render_process_observer.h" #include "chrome/renderer/chrome_render_view_observer.h" #include "chrome/renderer/content_settings_observer.h" -#include "chrome/renderer/extensions/extension_bindings_context.h" +#include "chrome/renderer/extensions/bindings_utils.h" +#include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/extension_dispatcher.h" #include "chrome/renderer/extensions/extension_helper.h" #include "chrome/renderer/extensions/extension_process_bindings.h" @@ -574,7 +575,7 @@ bool ChromeContentRendererClient::AllowPopup(const GURL& creator) { // Extensions and apps always allowed to create unrequested popups. The second // check is necessary to include content scripts. return extension_dispatcher_->extensions()->GetByURL(creator) || - ExtensionBindingsContext::GetCurrent(); + bindings_utils::GetInfoForCurrentContext(); } bool ChromeContentRendererClient::ShouldFork(WebFrame* frame, @@ -642,24 +643,22 @@ void ChromeContentRendererClient::DidCreateScriptContext(WebFrame* frame) { if (frame->mainWorldScriptContext().IsEmpty()) return; - ExtensionBindingsContext::HandleV8ContextCreated( - frame, - frame->mainWorldScriptContext(), - extension_dispatcher_.get(), - 0); // isolated world id + EventBindings::HandleContextCreated(frame, + frame->mainWorldScriptContext(), + extension_dispatcher_.get(), + 0); // isolated world ID } void ChromeContentRendererClient::DidDestroyScriptContext(WebFrame* frame) { - ExtensionBindingsContext::HandleV8ContextDestroyed(frame); + EventBindings::HandleContextDestroyed(frame); } void ChromeContentRendererClient::DidCreateIsolatedScriptContext( WebFrame* frame, int world_id, v8::Handle<v8::Context> context) { - ExtensionBindingsContext::HandleV8ContextCreated( - frame, - context, - extension_dispatcher_.get(), - world_id); + EventBindings::HandleContextCreated(frame, + context, + extension_dispatcher_.get(), + world_id); } unsigned long long ChromeContentRendererClient::VisitedLinkHash( 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 74e3f95..3e5cb67 100644 --- a/chrome/renderer/extensions/chrome_webstore_bindings.cc +++ b/chrome/renderer/extensions/chrome_webstore_bindings.cc @@ -7,6 +7,7 @@ #include "base/lazy_instance.h" #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 40e6d2b..75deefb 100644 --- a/chrome/renderer/extensions/event_bindings.h +++ b/chrome/renderer/extensions/event_bindings.h @@ -6,23 +6,57 @@ #define CHROME_RENDERER_EXTENSIONS_EVENT_BINDINGS_H_ #pragma once +#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 e6dad05..0000000 --- a/chrome/renderer/extensions/extension_bindings_context.cc +++ /dev/null @@ -1,238 +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() { - if (!v8::Context::InContext()) - return NULL; - else - 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 267e5a7..85c4e70 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 0f0bae5..3b79e84 100644 --- a/chrome/renderer/extensions/extension_helper.cc +++ b/chrome/renderer/extensions/extension_helper.cc @@ -14,7 +14,6 @@ #include "chrome/common/url_constants.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_dispatcher.h" #include "chrome/renderer/extensions/extension_process_bindings.h" #include "chrome/renderer/extensions/user_script_idle_scheduler.h" @@ -201,7 +200,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 a673183..b746186 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" @@ -45,6 +43,9 @@ #include "v8/include/v8.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; @@ -58,20 +59,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. @@ -443,7 +430,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; @@ -574,9 +561,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; @@ -586,12 +573,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 @@ -603,5 +586,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 367fca3..ec8619c 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" |