summaryrefslogtreecommitdiffstats
path: root/dom/browser-element/BrowserElementProxy.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/browser-element/BrowserElementProxy.js')
-rw-r--r--dom/browser-element/BrowserElementProxy.js219
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]);