diff options
author | mpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-08 18:35:34 +0000 |
---|---|---|
committer | mpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-08 18:35:34 +0000 |
commit | a40caa97fbdae3760f52f95f6b265bd1f39b19ae (patch) | |
tree | b98dceab49c4efb854c9923660735cbf96addbcd /chrome/renderer/resources | |
parent | 1b812ea42f713908a9034fcf2a26e8d4a8a86a04 (diff) | |
download | chromium_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
Diffstat (limited to 'chrome/renderer/resources')
-rw-r--r-- | chrome/renderer/resources/event_bindings.js | 120 | ||||
-rw-r--r-- | chrome/renderer/resources/renderer_extension_bindings.js | 113 |
2 files changed, 162 insertions, 71 deletions
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"); })(); |