diff options
Diffstat (limited to 'dom/browser-element/BrowserElementProxy.js')
-rw-r--r-- | dom/browser-element/BrowserElementProxy.js | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/dom/browser-element/BrowserElementProxy.js b/dom/browser-element/BrowserElementProxy.js new file mode 100644 index 000000000..852b93358 --- /dev/null +++ b/dom/browser-element/BrowserElementProxy.js @@ -0,0 +1,219 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cr = Components.results; + +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); +Cu.import('resource://gre/modules/Services.jsm'); + +function defineNoReturnMethod(methodName) { + return function noReturnMethod() { + let args = Array.slice(arguments); + this._sendToParent(methodName, args); + }; +} + +function defineDOMRequestMethod(methodName) { + return function domRequestMethod() { + let args = Array.slice(arguments); + return this._sendDOMRequest(methodName, args); + }; +} + +function defineUnimplementedMethod(methodName) { + return function unimplementedMethod() { + throw Components.Exception( + 'Unimplemented method: ' + methodName, Cr.NS_ERROR_FAILURE); + }; +} + +/** + * The BrowserElementProxy talks to the Browser IFrameElement instance on + * behave of the embedded document. It implements all the methods on + * the Browser IFrameElement and the methods will work if they are applicable. + * + * The message is forwarded to BrowserElementParent.js by creating an + * 'browser-element-api:proxy-call' observer message. + * BrowserElementChildPreload will get notified and send the message through + * to the main process through sendAsyncMessage with message of the same name. + * + * The return message will follow the same route. The message name on the + * return route is 'browser-element-api:proxy'. + * + * Both BrowserElementProxy and BrowserElementParent must be modified if there + * is a new method implemented, or a new event added to the Browser + * IFrameElement. + * + * Other details unmentioned here are checks of message sender and recipients + * to identify proxy instance on different innerWindows or ensure the content + * process has the right permission. + */ +function BrowserElementProxy() { + // Pad the 0th element so that DOMRequest ID will always be a truthy value. + this._pendingDOMRequests = [ undefined ]; +} + +BrowserElementProxy.prototype = { + classDescription: 'BrowserElementProxy allowed embedded frame to control ' + + 'it\'s own embedding browser element frame instance.', + classID: Components.ID('{7e95d54c-9930-49c8-9a10-44fe40fe8251}'), + contractID: '@mozilla.org/dom/browser-element-proxy;1', + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIDOMGlobalPropertyInitializer, + Ci.nsIObserver]), + + _window: null, + _innerWindowID: undefined, + + get allowedAudioChannels() { + return this._window.navigator.mozAudioChannelManager ? + this._window.navigator.mozAudioChannelManager.allowedAudioChannels : + null; + }, + + init: function(win) { + this._window = win; + this._innerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .currentInnerWindowID; + + this._sendToParent('_proxyInstanceInit'); + Services.obs.addObserver(this, 'browser-element-api:proxy', false); + }, + + uninit: function(win) { + this._sendToParent('_proxyInstanceUninit'); + + this._window = null; + this._innerWindowID = undefined; + + Services.obs.removeObserver(this, 'browser-element-api:proxy'); + }, + + observe: function(subject, topic, stringifedData) { + let data = JSON.parse(stringifedData); + + if (subject !== this._window || + data.innerWindowID !== data.innerWindowID) { + return; + } + + if (data.eventName) { + this._fireEvent(data.eventName, JSON.parse(data.eventDetailString)); + + return; + } + + if ('domRequestId' in data) { + let req = this._pendingDOMRequests[data.domRequestId]; + this._pendingDOMRequests[data.domRequestId] = undefined; + + if (!req) { + dump('BrowserElementProxy Error: ' + + 'Multiple observer messages for the same DOMRequest result.\n'); + return; + } + + if ('result' in data) { + let clientObj = Cu.cloneInto(data.result, this._window); + Services.DOMRequest.fireSuccess(req, clientObj); + } else { + let clientObj = Cu.cloneInto(data.error, this._window); + Services.DOMRequest.fireSuccess(req, clientObj); + } + + return; + } + + dump('BrowserElementProxy Error: ' + + 'Received unhandled observer messages ' + stringifedData + '.\n'); + }, + + _sendDOMRequest: function(methodName, args) { + let id = this._pendingDOMRequests.length; + let req = Services.DOMRequest.createRequest(this._window); + + this._pendingDOMRequests.push(req); + this._sendToParent(methodName, args, id); + + return req; + }, + + _sendToParent: function(methodName, args, domRequestId) { + let data = { + methodName: methodName, + args: args, + innerWindowID: this._innerWindowID + }; + + if (domRequestId) { + data.domRequestId = domRequestId; + } + + Services.obs.notifyObservers( + this._window, 'browser-element-api:proxy-call', JSON.stringify(data)); + }, + + _fireEvent: function(name, detail) { + let evt = this._createEvent(name, detail, + /* cancelable = */ false); + this.__DOM_IMPL__.dispatchEvent(evt); + }, + + _createEvent: function(evtName, detail, cancelable) { + // This will have to change if we ever want to send a CustomEvent with null + // detail. For now, it's OK. + if (detail !== undefined && detail !== null) { + detail = Cu.cloneInto(detail, this._window); + return new this._window.CustomEvent(evtName, + { bubbles: false, + cancelable: cancelable, + detail: detail }); + } + + return new this._window.Event(evtName, + { bubbles: false, + cancelable: cancelable }); + }, + + setVisible: defineNoReturnMethod('setVisible'), + setActive: defineNoReturnMethod('setActive'), + sendMouseEvent: defineNoReturnMethod('sendMouseEvent'), + sendTouchEvent: defineNoReturnMethod('sendTouchEvent'), + goBack: defineNoReturnMethod('goBack'), + goForward: defineNoReturnMethod('goForward'), + reload: defineNoReturnMethod('reload'), + stop: defineNoReturnMethod('stop'), + zoom: defineNoReturnMethod('zoom'), + findAll: defineNoReturnMethod('findAll'), + findNext: defineNoReturnMethod('findNext'), + clearMatch: defineNoReturnMethod('clearMatch'), + mute: defineNoReturnMethod('mute'), + unmute: defineNoReturnMethod('unmute'), + setVolume: defineNoReturnMethod('setVolume'), + + getVisible: defineDOMRequestMethod('getVisible'), + download: defineDOMRequestMethod('download'), + purgeHistory: defineDOMRequestMethod('purgeHistory'), + getCanGoBack: defineDOMRequestMethod('getCanGoBack'), + getCanGoForward: defineDOMRequestMethod('getCanGoForward'), + getContentDimensions: defineDOMRequestMethod('getContentDimensions'), + setInputMethodActive: defineDOMRequestMethod('setInputMethodActive'), + executeScript: defineDOMRequestMethod('executeScript'), + getMuted: defineDOMRequestMethod('getMuted'), + getVolume: defineDOMRequestMethod('getVolume'), + + getActive: defineUnimplementedMethod('getActive'), + addNextPaintListener: defineUnimplementedMethod('addNextPaintListener'), + removeNextPaintListener: defineUnimplementedMethod('removeNextPaintListener'), + getScreenshot: defineUnimplementedMethod('getScreenshot') +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([BrowserElementProxy]); |