зеркало из https://github.com/mozilla/gecko-dev.git
220 строки
7.4 KiB
JavaScript
220 строки
7.4 KiB
JavaScript
/* 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]);
|