summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-08 18:35:34 +0000
committermpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-08 18:35:34 +0000
commita40caa97fbdae3760f52f95f6b265bd1f39b19ae (patch)
treeb98dceab49c4efb854c9923660735cbf96addbcd
parent1b812ea42f713908a9034fcf2a26e8d4a8a86a04 (diff)
downloadchromium_src-a40caa97fbdae3760f52f95f6b265bd1f39b19ae.zip
chromium_src-a40caa97fbdae3760f52f95f6b265bd1f39b19ae.tar.gz
chromium_src-a40caa97fbdae3760f52f95f6b265bd1f39b19ae.tar.bz2
Add aa's Event class to our javascript bindings and use it in our extension
message passing API. Review URL: http://codereview.chromium.org/62069 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13371 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.cc1
-rw-r--r--chrome/chrome.gyp3
-rwxr-xr-xchrome/renderer/extensions/bindings_utils.h28
-rwxr-xr-xchrome/renderer/extensions/event_bindings.cc119
-rwxr-xr-xchrome/renderer/extensions/event_bindings.h25
-rwxr-xr-xchrome/renderer/extensions/renderer_extension_bindings.cc118
-rwxr-xr-xchrome/renderer/extensions/renderer_extension_bindings.h1
-rw-r--r--chrome/renderer/render_thread.cc14
-rw-r--r--chrome/renderer/render_view_unittest.cc12
-rw-r--r--chrome/renderer/renderer.vcproj12
-rwxr-xr-xchrome/renderer/renderer_resources.grd1
-rw-r--r--chrome/renderer/resources/event_bindings.js120
-rw-r--r--chrome/renderer/resources/renderer_extension_bindings.js113
-rw-r--r--chrome/test/data/extensions/good/extension1/1/toolstrip1.html4
14 files changed, 383 insertions, 188 deletions
diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc
index 62498a0..807e457 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.cc
+++ b/chrome/browser/renderer_host/browser_render_process_host.cc
@@ -267,6 +267,7 @@ bool BrowserRenderProcessHost::Init() {
switches::kEnableVideo,
switches::kEnableWebWorkers,
switches::kEnableStatsTable,
+ switches::kEnableExtensions,
};
for (size_t i = 0; i < arraysize(switch_names); ++i) {
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index cf3a9c3..99dee76 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1410,6 +1410,9 @@
'app/breakpad_mac.h',
'renderer/automation/dom_automation_controller.cc',
'renderer/automation/dom_automation_controller.h',
+ 'renderer/extensions/bindings_utils.h',
+ 'renderer/extensions/event_bindings.cc',
+ 'renderer/extensions/event_bindings.h',
'renderer/extensions/extension_process_bindings.cc',
'renderer/extensions/extension_process_bindings.h',
'renderer/extensions/renderer_extension_bindings.cc',
diff --git a/chrome/renderer/extensions/bindings_utils.h b/chrome/renderer/extensions/bindings_utils.h
new file mode 100755
index 0000000..9e7d213
--- /dev/null
+++ b/chrome/renderer/extensions/bindings_utils.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2009 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_
+
+#include "base/singleton.h"
+#include "chrome/common/resource_bundle.h"
+
+#include <string>
+
+template<int kResourceId>
+struct StringResourceTemplate {
+ StringResourceTemplate()
+ : resource(ResourceBundle::GetSharedInstance().GetRawDataResource(
+ kResourceId).as_string()) {
+ }
+ std::string resource;
+};
+
+template<int kResourceId>
+const char* GetStringResource() {
+ return
+ Singleton< StringResourceTemplate<kResourceId> >::get()->resource.c_str();
+}
+
+#endif // CHROME_RENDERER_EXTENSIONS_BINDINGS_UTILS_H_
diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc
new file mode 100755
index 0000000..0b45abba
--- /dev/null
+++ b/chrome/renderer/extensions/event_bindings.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2009 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/event_bindings.h"
+
+#include "base/basictypes.h"
+#include "base/singleton.h"
+#include "chrome/renderer/extensions/bindings_utils.h"
+#include "chrome/renderer/extensions/event_bindings.h"
+#include "chrome/renderer/render_thread.h"
+#include "grit/renderer_resources.h"
+
+namespace {
+
+// Keep a list of contexts that have registered themselves with us. This lets
+// us know where to dispatch events when we receive them.
+typedef std::list< v8::Persistent<v8::Context> > ContextList;
+struct ExtensionData {
+ ContextList contexts;
+};
+ContextList& GetRegisteredContexts() {
+ return Singleton<ExtensionData>::get()->contexts;
+}
+
+class ExtensionImpl : public v8::Extension {
+ public:
+ ExtensionImpl()
+ : v8::Extension(EventBindings::kName,
+ GetStringResource<IDR_EVENT_BINDINGS_JS>()) {
+ }
+ ~ExtensionImpl() {}
+
+ virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
+ v8::Handle<v8::String> name) {
+ if (name->Equals(v8::String::New("AttachEvent"))) {
+ return v8::FunctionTemplate::New(AttachEvent);
+ } else if (name->Equals(v8::String::New("DetachEvent"))) {
+ return v8::FunctionTemplate::New(DetachEvent);
+ }
+ return v8::Handle<v8::FunctionTemplate>();
+ }
+
+ // Attach an event name to an object.
+ // TODO(mpcomplete): I'm just using this to register the v8 Context right now.
+ // The idea is to eventually notify the browser about what events are being
+ // listened to, so it can dispatch appropriately.
+ static v8::Handle<v8::Value> AttachEvent(const v8::Arguments& args) {
+ const char* kContextRegistered = "chromium.extension.contextRegistered";
+
+ v8::Persistent<v8::Context> context =
+ v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
+ v8::Local<v8::Object> global = context->Global();
+ v8::Local<v8::Value> is_registered = global->GetHiddenValue(
+ v8::String::New(kContextRegistered));
+ if (is_registered.IsEmpty() || is_registered->IsUndefined()) {
+ GetRegisteredContexts().push_back(context);
+ context.MakeWeak(NULL, WeakContextCallback);
+ global->SetHiddenValue(
+ v8::String::New(kContextRegistered), v8::Boolean::New(true));
+ }
+ return v8::Undefined();
+ }
+
+ // TODO(mpcomplete): Implement this. This should do the reverse of
+ // AttachEvent, when that actually does what we plan on.
+ static v8::Handle<v8::Value> DetachEvent(const v8::Arguments& args) {
+ return v8::Undefined();
+ }
+
+ // Called when a registered context is garbage collected.
+ static void WeakContextCallback(v8::Persistent<v8::Value> obj, void*) {
+ ContextList::iterator it = std::find(GetRegisteredContexts().begin(),
+ GetRegisteredContexts().end(), obj);
+ if (it == GetRegisteredContexts().end()) {
+ NOTREACHED();
+ return;
+ }
+
+ it->Dispose();
+ it->Clear();
+ GetRegisteredContexts().erase(it);
+ }
+};
+
+} // namespace
+
+const char* EventBindings::kName = "chrome/EventBindings";
+
+v8::Extension* EventBindings::Get() {
+ return new ExtensionImpl();
+}
+
+void EventBindings::CallFunction(const std::string& function_name,
+ int argc, v8::Handle<v8::Value>* argv) {
+ for (ContextList::iterator it = GetRegisteredContexts().begin();
+ it != GetRegisteredContexts().end(); ++it) {
+ DCHECK(!it->IsEmpty());
+ v8::Context::Scope context_scope(*it);
+ v8::Local<v8::Object> global = (*it)->Global();
+
+ // Check if the window object is gone, which means this context's frame
+ // has been unloaded.
+ v8::Local<v8::Value> window = global->Get(v8::String::New("window"));
+ if (!window->IsObject())
+ continue;
+
+ v8::Local<v8::Script> script = v8::Script::Compile(
+ v8::String::New(function_name.c_str()));
+ v8::Local<v8::Value> function_obj = script->Run();
+ if (!function_obj->IsFunction())
+ continue;
+
+ v8::Local<v8::Function> function =
+ v8::Local<v8::Function>::Cast(function_obj);
+ if (!function.IsEmpty())
+ function->Call(v8::Object::New(), argc, argv);
+ }
+}
diff --git a/chrome/renderer/extensions/event_bindings.h b/chrome/renderer/extensions/event_bindings.h
new file mode 100755
index 0000000..dd91811
--- /dev/null
+++ b/chrome/renderer/extensions/event_bindings.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 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_EVENT_BINDINGS_H_
+#define CHROME_RENDERER_EXTENSIONS_EVENT_BINDINGS_H_
+
+#include "v8/include/v8.h"
+
+#include <string>
+
+// 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 v8::Extension* Get();
+
+ // Calls the given function in each registered context which is listening
+ // for events. The function can be an object property, ie:
+ // "chromium.Event.dispatch_".
+ static void CallFunction(const std::string& function_name, int argc,
+ v8::Handle<v8::Value>* argv);
+};
+
+#endif // CHROME_RENDERER_EXTENSIONS_EVENT_BINDINGS_H_
diff --git a/chrome/renderer/extensions/renderer_extension_bindings.cc b/chrome/renderer/extensions/renderer_extension_bindings.cc
index 8254bc6..572b00c 100755
--- a/chrome/renderer/extensions/renderer_extension_bindings.cc
+++ b/chrome/renderer/extensions/renderer_extension_bindings.cc
@@ -5,53 +5,38 @@
#include "chrome/renderer/extensions/renderer_extension_bindings.h"
#include "base/basictypes.h"
-#include "base/singleton.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/resource_bundle.h"
+#include "chrome/renderer/extensions/bindings_utils.h"
+#include "chrome/renderer/extensions/event_bindings.h"
#include "chrome/renderer/render_thread.h"
#include "grit/renderer_resources.h"
-#include "webkit/glue/webframe.h"
// Message passing API example (in a content script):
// var extension =
// new chromium.Extension('00123456789abcdef0123456789abcdef0123456');
-// var channel = extension.openChannel();
-// channel.postMessage('Can you hear me now?');
-// channel.onMessage = function(msg, port) {
+// var port = extension.connect();
+// port.postMessage('Can you hear me now?');
+// port.onmessage.addListener(function(msg, port) {
// alert('response=' + msg);
// port.postMessage('I got your reponse');
-// }
+// });
namespace {
-// Keep a list of contexts that have registered themselves with us. This lets
-// us know where to dispatch events when we receive them.
-typedef std::list< v8::Persistent<v8::Context> > ContextList;
-struct SingletonData {
- ContextList contexts;
- std::string js_source;
-
- SingletonData() :
- js_source(ResourceBundle::GetSharedInstance().GetRawDataResource(
- IDR_RENDERER_EXTENSION_BINDINGS_JS).as_string()) {
- }
-};
-ContextList& GetRegisteredContexts() {
- return Singleton<SingletonData>::get()->contexts;
-}
-const char* GetSource() {
- return Singleton<SingletonData>::get()->js_source.c_str();
-}
-
// We use the generic interface so that unit tests can inject a mock.
RenderThreadBase* render_thread_ = NULL;
const char* kExtensionName = "v8/RendererExtensionBindings";
-const char* kScriptAPI = "chromium.extensions.scriptAPI";
+const char* kExtensionDeps[] = { EventBindings::kName };
class ExtensionImpl : public v8::Extension {
public:
- ExtensionImpl() : v8::Extension(kExtensionName, GetSource()) {}
+ ExtensionImpl()
+ : v8::Extension(kExtensionName,
+ GetStringResource<IDR_RENDERER_EXTENSION_BINDINGS_JS>(),
+ arraysize(kExtensionDeps), kExtensionDeps) {
+ }
~ExtensionImpl() {}
virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
@@ -60,8 +45,6 @@ class ExtensionImpl : public v8::Extension {
return v8::FunctionTemplate::New(OpenChannelToExtension);
} else if (name->Equals(v8::String::New("PostMessage"))) {
return v8::FunctionTemplate::New(PostMessage);
- } else if (name->Equals(v8::String::New("RegisterScriptAPI"))) {
- return v8::FunctionTemplate::New(RegisterScriptAPI);
}
return v8::Handle<v8::FunctionTemplate>();
}
@@ -89,77 +72,6 @@ class ExtensionImpl : public v8::Extension {
}
return v8::Undefined();
}
-
- // This method is internal to the extension, and fulfills a dual purpose:
- // 1. Keep track of which v8::Contexts have registered event listeners.
- // 2. Registers a private variable on the context that we use for dispatching
- // events as they come in, by calling designated methods.
- static v8::Handle<v8::Value> RegisterScriptAPI(const v8::Arguments& args) {
- if (args.Length() >= 1 && args[0]->IsObject()) {
- v8::Persistent<v8::Context> context =
- v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
- GetRegisteredContexts().push_back(context);
- context.MakeWeak(NULL, WeakContextCallback);
- context->Global()->SetHiddenValue(v8::String::New(kScriptAPI), args[0]);
- DCHECK(args[0]->ToObject()->Get(v8::String::New("dispatchOnConnect"))->
- IsFunction());
- DCHECK(args[0]->ToObject()->Get(v8::String::New("dispatchOnMessage"))->
- IsFunction());
- return v8::Undefined();
- }
- return v8::Undefined();
- }
-
- // Calls the given chromiumPrivate method in each registered context.
- static void CallMethod(const std::string& method_name, int argc,
- v8::Handle<v8::Value>* argv) {
- for (ContextList::iterator it = GetRegisteredContexts().begin();
- it != GetRegisteredContexts().end(); ++it) {
- DCHECK(!it->IsEmpty());
- v8::Context::Scope context_scope(*it);
- v8::Local<v8::Object> global = (*it)->Global();
-
- // Check if the window object is gone, which means this context's frame
- // has been unloaded.
- v8::Local<v8::Value> window = global->Get(v8::String::New("window"));
- if (!window->IsObject())
- continue;
-
- // Retrieve our hidden variable and call the method on it.
- v8::Local<v8::Value> script_api = global->GetHiddenValue(
- v8::String::New(kScriptAPI));
- if (!script_api->IsObject()) {
- NOTREACHED();
- continue;
- }
-
- v8::Handle<v8::Value> function_obj = script_api->ToObject()->Get(
- v8::String::New(method_name.c_str()));
- if (!function_obj->IsFunction()) {
- NOTREACHED();
- continue;
- }
-
- v8::Handle<v8::Function> function =
- v8::Handle<v8::Function>::Cast(function_obj);
- if (!function.IsEmpty())
- function->Call(v8::Object::New(), argc, argv);
- }
- }
-
- // Called when a registered context is garbage collected.
- static void WeakContextCallback(v8::Persistent<v8::Value> obj, void*) {
- ContextList::iterator it = std::find(GetRegisteredContexts().begin(),
- GetRegisteredContexts().end(), obj);
- if (it == GetRegisteredContexts().end()) {
- NOTREACHED();
- return;
- }
-
- it->Dispose();
- it->Clear();
- GetRegisteredContexts().erase(it);
- }
};
} // namespace
@@ -175,7 +87,8 @@ void RendererExtensionBindings::HandleConnect(int port_id) {
v8::HandleScope handle_scope;
v8::Handle<v8::Value> argv[1];
argv[0] = v8::Integer::New(port_id);
- ExtensionImpl::CallMethod("dispatchOnConnect", arraysize(argv), argv);
+ EventBindings::CallFunction("chromium.Port.dispatchOnConnect_",
+ arraysize(argv), argv);
}
void RendererExtensionBindings::HandleMessage(const std::string& message,
@@ -184,7 +97,8 @@ void RendererExtensionBindings::HandleMessage(const std::string& message,
v8::Handle<v8::Value> argv[2];
argv[0] = v8::String::New(message.c_str());
argv[1] = v8::Integer::New(port_id);
- ExtensionImpl::CallMethod("dispatchOnMessage", arraysize(argv), argv);
+ EventBindings::CallFunction("chromium.Port.dispatchOnMessage_",
+ arraysize(argv), argv);
}
} // namespace extensions_v8
diff --git a/chrome/renderer/extensions/renderer_extension_bindings.h b/chrome/renderer/extensions/renderer_extension_bindings.h
index 7c951ec..b0d04de 100755
--- a/chrome/renderer/extensions/renderer_extension_bindings.h
+++ b/chrome/renderer/extensions/renderer_extension_bindings.h
@@ -10,7 +10,6 @@
#include <string>
class RenderThreadBase;
-class WebFrame;
namespace extensions_v8 {
diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc
index 0b3c171..ea0cf94 100644
--- a/chrome/renderer/render_thread.cc
+++ b/chrome/renderer/render_thread.cc
@@ -25,6 +25,7 @@
#include "chrome/plugin/plugin_channel_base.h"
#include "webkit/glue/weburlrequest.h"
#endif
+#include "chrome/renderer/extensions/event_bindings.h"
#include "chrome/renderer/extensions/extension_process_bindings.h"
#include "chrome/renderer/extensions/renderer_extension_bindings.h"
#include "chrome/renderer/loadtimes_extension_bindings.h"
@@ -286,13 +287,16 @@ void RenderThread::EnsureWebKitInitialized() {
WebKit::registerExtension(extensions_v8::GearsExtension::Get());
WebKit::registerExtension(extensions_v8::IntervalExtension::Get());
WebKit::registerExtension(extensions_v8::LoadTimesExtension::Get());
- WebKit::registerExtension(
- extensions_v8::RendererExtensionBindings::Get(this));
-
- WebKit::registerExtension(extensions_v8::ExtensionProcessBindings::Get(),
- WebKit::WebString::fromUTF8(chrome::kExtensionScheme));
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ if (command_line.HasSwitch(switches::kEnableExtensions)) {
+ WebKit::registerExtension(EventBindings::Get());
+ WebKit::registerExtension(
+ extensions_v8::RendererExtensionBindings::Get(this));
+ WebKit::registerExtension(extensions_v8::ExtensionProcessBindings::Get(),
+ WebKit::WebString::fromUTF8(chrome::kExtensionScheme));
+ }
+
if (command_line.HasSwitch(switches::kPlaybackMode) ||
command_line.HasSwitch(switches::kRecordMode)) {
WebKit::registerExtension(extensions_v8::PlaybackExtension::Get());
diff --git a/chrome/renderer/render_view_unittest.cc b/chrome/renderer/render_view_unittest.cc
index c27bcbf..95a584bb 100644
--- a/chrome/renderer/render_view_unittest.cc
+++ b/chrome/renderer/render_view_unittest.cc
@@ -4,6 +4,7 @@
#include "base/scoped_ptr.h"
#include "chrome/common/render_messages.h"
+#include "chrome/renderer/extensions/event_bindings.h"
#include "chrome/renderer/extensions/renderer_extension_bindings.h"
#include "chrome/renderer/mock_render_process.h"
#include "chrome/renderer/mock_render_thread.h"
@@ -66,6 +67,7 @@ class RenderViewTest : public testing::Test {
// testing::Test
virtual void SetUp() {
WebKit::initialize(&webkitclient_);
+ WebKit::registerExtension(EventBindings::Get());
WebKit::registerExtension(
extensions_v8::RendererExtensionBindings::Get(&render_thread_));
@@ -378,8 +380,8 @@ TEST_F(RenderViewTest, ExtensionMessagesOpenChannel) {
LoadHTML("<body></body>");
ExecuteJavaScript(
"var e = new chromium.Extension('foobar');"
- "var port = e.openChannel();"
- "port.onMessage = doOnMessage;"
+ "var port = e.connect();"
+ "port.onmessage.addListener(doOnMessage);"
"port.postMessage('content ready');"
"function doOnMessage(msg, port) {"
" alert('content got: ' + msg);"
@@ -420,10 +422,10 @@ TEST_F(RenderViewTest, ExtensionMessagesOpenChannel) {
TEST_F(RenderViewTest, ExtensionMessagesOnConnect) {
LoadHTML("<body></body>");
ExecuteJavaScript(
- "chromium.addConnectListener(function (port) {"
- " port.onMessage = doOnMessage;"
+ "chromium.onconnect.addListener(function (port) {"
+ " port.onmessage.addListener(doOnMessage);"
" port.postMessage('onconnect');"
- " });"
+ "});"
"function doOnMessage(msg, port) {"
" alert('got: ' + msg);"
"}");
diff --git a/chrome/renderer/renderer.vcproj b/chrome/renderer/renderer.vcproj
index d9ee179..fca563e 100644
--- a/chrome/renderer/renderer.vcproj
+++ b/chrome/renderer/renderer.vcproj
@@ -185,11 +185,19 @@
Name="extensions"
>
<File
- RelativePath=".\extensions\extension_process_bindings.cc"
+ RelativePath=".\extensions\bindings_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\extensions\event_bindings.cc"
>
</File>
<File
- RelativePath=".\extensions\extension_process_bindings.h"
+ RelativePath=".\extensions\event_bindings.h"
+ >
+ </File>
+ <File
+ RelativePath=".\extensions\extension_process_bindings.cc"
>
</File>
<File
diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd
index e805630..5589cc0 100755
--- a/chrome/renderer/renderer_resources.grd
+++ b/chrome/renderer/renderer_resources.grd
@@ -15,6 +15,7 @@
<include name="IDR_GREASEMONKEY_API_JS" file="resources\greasemonkey_api.js" type="BINDATA" />
<include name="IDR_EXTENSION_PROCESS_BINDINGS_JS" file="resources\extension_process_bindings.js" type="BINDATA" />
<include name="IDR_RENDERER_EXTENSION_BINDINGS_JS" file="resources\renderer_extension_bindings.js" type="BINDATA" />
+ <include name="IDR_EVENT_BINDINGS_JS" file="resources\event_bindings.js" type="BINDATA" />
</includes>
</release>
</grit> \ No newline at end of file
diff --git a/chrome/renderer/resources/event_bindings.js b/chrome/renderer/resources/event_bindings.js
new file mode 100644
index 0000000..de5b3a9
--- /dev/null
+++ b/chrome/renderer/resources/event_bindings.js
@@ -0,0 +1,120 @@
+var chromium = chromium || {};
+(function () {
+ native function AttachEvent(eventName);
+ native function DetachEvent(eventName);
+
+ // Event object. If opt_eventName is provided, this object represents
+ // the unique instance of that named event, and dispatching an event
+ // with that name will route through this object's listeners.
+ //
+ // Example:
+ // chromium.ontabchanged = new Event('tabchanged');
+ // chromium.ontabchanged.addListener(function(data) { alert(data); });
+ // chromium.Event.dispatch_('tabchanged', 'hi');
+ // will result in an alert dialog that says 'hi'.
+ chromium.Event = function(opt_eventName) {
+ this.eventName_ = opt_eventName;
+ this.listeners_ = [];
+ };
+
+ // A map of event names to the event object that is registered to that name.
+ chromium.Event.attached_ = {};
+
+ // Dispatches a named event with the given JSON data, which is deserialized
+ // before dispatch.
+ chromium.Event.dispatchJSON_ = function(name, data) {
+ if (chromium.Event.attached_[name]) {
+ if (data) {
+ data = chromium.json.deserialize_(data);
+ }
+ chromium.Event.attached_[name].dispatch_(data);
+ }
+ };
+
+ // Dispatches a named event with the given object data.
+ chromium.Event.dispatch_ = function(name, data) {
+ if (chromium.Event.attached_[name]) {
+ chromium.Event.attached_[name].dispatch(data);
+ }
+ };
+
+ // Registers a callback to be called when this event is dispatched.
+ chromium.Event.prototype.addListener = function(cb) {
+ this.listeners_.push(cb);
+ if (this.listeners_.length == 1) {
+ this.attach_();
+ }
+ };
+
+ // Unregisters a callback.
+ chromium.Event.prototype.removeListener = function(cb) {
+ var idx = this.findListener_(cb);
+ if (idx == -1) {
+ return;
+ }
+
+ this.listeners_.splice(idx, 1);
+ if (this.listeners_.length == 0) {
+ this.detach_();
+ }
+ };
+
+ // Test if the given callback is registered for this event.
+ chromium.Event.prototype.hasListener = function(cb) {
+ return this.findListeners_(cb) > -1;
+ };
+
+ // Returns the index of the given callback if registered, or -1 if not
+ // found.
+ chromium.Event.prototype.findListener_ = function(cb) {
+ for (var i = 0; i < this.listeners_.length; i++) {
+ if (this.listeners_[i] == cb) {
+ return i;
+ }
+ }
+
+ return -1;
+ };
+
+ // Dispatches this event object to all listeners, passing all supplied
+ // arguments to this function each listener.
+ chromium.Event.prototype.dispatch = function(varargs) {
+ var args = Array.prototype.slice.call(arguments);
+ for (var i = 0; i < this.listeners_.length; i++) {
+ try {
+ this.listeners_[i].apply(null, args);
+ } catch (e) {
+ console.error(e);
+ }
+ }
+ };
+
+ // Attaches this event object to its name. Only one object can have a given
+ // name.
+ chromium.Event.prototype.attach_ = function() {
+ AttachEvent(this.eventName_);
+ if (!this.eventName_)
+ return;
+
+ if (chromium.Event.attached_[this.eventName_]) {
+ throw new Error("chromium.Event '" + this.eventName_ +
+ "' is already attached.");
+ }
+
+ chromium.Event.attached_[this.eventName_] = this;
+ };
+
+ // Detaches this event object from its name.
+ chromium.Event.prototype.detach_ = function() {
+ DetachEvent(this.eventName_);
+ if (!this.eventName_)
+ return;
+
+ if (!chromium.Event.attached_[this.eventName_]) {
+ throw new Error("chromium.Event '" + this.eventName_ +
+ "' is not attached.");
+ }
+
+ delete chromium.Event.attached_[this.eventName_];
+ };
+})();
diff --git a/chrome/renderer/resources/renderer_extension_bindings.js b/chrome/renderer/resources/renderer_extension_bindings.js
index 401f82c..deb5c40 100644
--- a/chrome/renderer/resources/renderer_extension_bindings.js
+++ b/chrome/renderer/resources/renderer_extension_bindings.js
@@ -2,92 +2,63 @@ var chromium = chromium || {};
(function () {
native function OpenChannelToExtension(id);
native function PostMessage(portId, msg);
- native function RegisterScriptAPI(private);
- // chromium: Public API.
- // Represents info we know about a chrome extension.
- chromium.Extension = function(id) {
- this.id_ = id;
- };
-
- // Opens a channel to the extension for message passing.
- chromium.Extension.prototype.openChannel = function() {
- portId = OpenChannelToExtension(this.id_);
- if (portId == -1)
- throw new Error('No such extension \"' + this.id_ + '\"');
- return new Port(portId);
+ // Port object. Represents a connection to another script context through
+ // which messages can be passed.
+ chromium.Port = function(portId) {
+ if (chromium.Port.ports_[portId]) {
+ throw new Error("Port '" + portId + "' already exists.");
+ }
+ this.portId_ = portId; // TODO(mpcomplete): readonly
+ this.onmessage = new chromium.Event();
+ chromium.Port.ports_[portId] = this;
+ // Note: this object will never get GCed. If we ever care, we could
+ // add an "ondetach" method to the onmessage Event that gets called
+ // when there are no more listeners.
};
- // Adds a listener that fires when a renderer opens a channel to talk
- // to us.
- chromium.addConnectListener = function(callback) {
- chromium.addEventListener('channel-connect',
- function (e) { callback(e.data.port); });
- };
+ // Map of port IDs to port object.
+ chromium.Port.ports_ = {};
- // Adds a generic event listener.
- chromium.addEventListener = function(type, callback) {
- var listeners = getPrivateData().eventListeners;
- if (!listeners[type])
- listeners[type] = [];
- listeners[type].push(callback);
+ // Called by native code when a channel has been opened to this context.
+ chromium.Port.dispatchOnConnect_ = function(portId) {
+ var port = new chromium.Port(portId);
+ chromium.Event.dispatch_("channel-connect", port);
};
- // Dispatches the given event to anyone listening for that event.
- chromium.dispatchEvent = function(type, data) {
- var event = {type: type, data: data};
- var listeners = getPrivateData().eventListeners;
- for (var i in listeners[type]) {
- listeners[type][i](event);
+ // Called by native code when a message has been sent to the given port.
+ chromium.Port.dispatchOnMessage_ = function(msg, portId) {
+ var port = chromium.Port.ports_[portId];
+ if (port) {
+ port.onmessage.dispatch(msg, port);
}
};
- // Private API.
-
- // Always access privateData through this function, to ensure that we
- // have registered our native API. We do this lazily to avoid registering
- // on pages that don't use these bindings.
- function getPrivateData() {
- if (!scriptAPI.registered_) {
- RegisterScriptAPI(scriptAPI);
- scriptAPI.registered_ = true;
- }
- return privateData;
- }
- var privateData = {
- eventListeners: {},
- ports: {}
+ // Sends a message asynchronously to the context on the other end of this
+ // port.
+ chromium.Port.prototype.postMessage = function(msg) {
+ PostMessage(this.portId_, msg);
};
- // Represents a port through which we can send messages to another process.
- var Port = function(portId) {
- // TODO(mpcomplete): we probably want to hide this portId_ so
- // it can't be guessed at. One idea is to expose v8's SetHiddenValue
- // to our extension.
- this.portId_ = portId;
- getPrivateData().ports[portId] = this;
+ // Extension object.
+ chromium.Extension = function(id) {
+ this.id_ = id;
};
- // Sends a message to the other side of the channel.
- Port.prototype.postMessage = function(msg) {
- PostMessage(this.portId_, msg);
+ // Opens a message channel to the extension. Returns a Port for
+ // message passing.
+ chromium.Extension.prototype.connect = function() {
+ var portId = OpenChannelToExtension(this.id_);
+ if (portId == -1)
+ throw new Error("No such extension: '" + this.id_ + "'");
+ return new chromium.Port(portId);
};
- // Script API: javascript APIs exposed to C++ only.
- // This object allows our native code to call back to us through a
- // private interface that isn't exposed to web content.
- var scriptAPI = {};
-
- // Called by native code when a channel has been opened to this process.
- scriptAPI.dispatchOnConnect = function(portId) {
- chromium.dispatchEvent('channel-connect', {port: new Port(portId)});
+ // Returns a resource URL that can be used to fetch a resource from this
+ // extension.
+ chromium.Extension.prototype.getURL = function(path) {
+ return "chrome-extension://" + this.id_ + "/" + path;
};
- // Called by native code when a message has been sent over the given
- // channel.
- scriptAPI.dispatchOnMessage = function(msg, portId) {
- var port = getPrivateData().ports[portId];
- if (port && port.onMessage)
- port.onMessage(msg, port);
- };
+ chromium.onconnect = new chromium.Event("channel-connect");
})();
diff --git a/chrome/test/data/extensions/good/extension1/1/toolstrip1.html b/chrome/test/data/extensions/good/extension1/1/toolstrip1.html
index 66ba2b3..bbc4392 100644
--- a/chrome/test/data/extensions/good/extension1/1/toolstrip1.html
+++ b/chrome/test/data/extensions/good/extension1/1/toolstrip1.html
@@ -16,8 +16,8 @@ body {
<script>
alert('Sir, I exist');
- chromium.addConnectListener(function (port) {
- port.onMessage = doOnMessage;
+ chromium.onconnect.addListener(function (port) {
+ port.onmessage.addListener(doOnMessage);
port.postMessage('extension onconnect');
});