зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 8ecfc1f41ddc (bug 1044736)
This commit is contained in:
Родитель
c1d7fa8e0a
Коммит
5f6cec13d2
|
@ -8,10 +8,122 @@ const {utils: Cu, interfaces: Ci} = Components;
|
|||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/BrowserElementParent.jsm");
|
||||
|
||||
const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
|
||||
const BROWSER_FRAMES_ENABLED_PREF = "dom.mozBrowserFramesEnabled";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserElementParentBuilder",
|
||||
"resource://gre/modules/BrowserElementParent.jsm",
|
||||
"BrowserElementParentBuilder");
|
||||
|
||||
function debug(msg) {
|
||||
//dump("BrowserElementParent.js - " + msg + "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* BrowserElementParent implements one half of <iframe mozbrowser>. (The other
|
||||
* half is, unsurprisingly, BrowserElementChild.)
|
||||
*
|
||||
* BrowserElementParentFactory detects when we create a windows or docshell
|
||||
* contained inside a <iframe mozbrowser> and creates a BrowserElementParent
|
||||
* object for that window.
|
||||
*
|
||||
* It creates a BrowserElementParent that injects script to listen for
|
||||
* certain event.
|
||||
*/
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([BrowserElementParent]);
|
||||
|
||||
function BrowserElementParentFactory() {
|
||||
this._initialized = false;
|
||||
}
|
||||
|
||||
BrowserElementParentFactory.prototype = {
|
||||
classID: Components.ID("{ddeafdac-cb39-47c4-9cb8-c9027ee36d26}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
/**
|
||||
* Called on app startup, and also when the browser frames enabled pref is
|
||||
* changed.
|
||||
*/
|
||||
_init: function() {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the pref is disabled, do nothing except wait for the pref to change.
|
||||
// (This is important for tests, if nothing else.)
|
||||
if (!this._browserFramesPrefEnabled()) {
|
||||
Services.prefs.addObserver(BROWSER_FRAMES_ENABLED_PREF, this, /* ownsWeak = */ true);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("_init");
|
||||
this._initialized = true;
|
||||
|
||||
// Maps frame elements to BrowserElementParent objects. We never look up
|
||||
// anything in this map; the purpose is to keep the BrowserElementParent
|
||||
// alive for as long as its frame element lives.
|
||||
this._bepMap = new WeakMap();
|
||||
|
||||
Services.obs.addObserver(this, 'remote-browser-pending', /* ownsWeak = */ true);
|
||||
Services.obs.addObserver(this, 'inprocess-browser-shown', /* ownsWeak = */ true);
|
||||
},
|
||||
|
||||
_browserFramesPrefEnabled: function() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref(BROWSER_FRAMES_ENABLED_PREF);
|
||||
}
|
||||
catch(e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
_observeInProcessBrowserFrameShown: function(frameLoader) {
|
||||
// Ignore notifications that aren't from a BrowserOrApp
|
||||
if (!frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsBrowserOrAppFrame) {
|
||||
return;
|
||||
}
|
||||
debug("In-process browser frame shown " + frameLoader);
|
||||
this._createBrowserElementParent(frameLoader,
|
||||
/* hasRemoteFrame = */ false,
|
||||
/* pending frame */ false);
|
||||
},
|
||||
|
||||
_observeRemoteBrowserFramePending: function(frameLoader) {
|
||||
// Ignore notifications that aren't from a BrowserOrApp
|
||||
if (!frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsBrowserOrAppFrame) {
|
||||
return;
|
||||
}
|
||||
debug("Remote browser frame shown " + frameLoader);
|
||||
this._createBrowserElementParent(frameLoader,
|
||||
/* hasRemoteFrame = */ true,
|
||||
/* pending frame */ true);
|
||||
},
|
||||
|
||||
_createBrowserElementParent: function(frameLoader, hasRemoteFrame, isPendingFrame) {
|
||||
let frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
|
||||
this._bepMap.set(frameElement, BrowserElementParentBuilder.create(
|
||||
frameLoader, hasRemoteFrame, isPendingFrame));
|
||||
},
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
switch(topic) {
|
||||
case 'app-startup':
|
||||
this._init();
|
||||
break;
|
||||
case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
|
||||
if (data == BROWSER_FRAMES_ENABLED_PREF) {
|
||||
this._init();
|
||||
}
|
||||
break;
|
||||
case 'remote-browser-pending':
|
||||
this._observeRemoteBrowserFramePending(subject);
|
||||
break;
|
||||
case 'inprocess-browser-shown':
|
||||
this._observeInProcessBrowserFrameShown(subject);
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([BrowserElementParentFactory]);
|
||||
|
|
|
@ -14,7 +14,7 @@ let Cr = Components.results;
|
|||
* appropriate action here in the parent.
|
||||
*/
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["BrowserElementParent"];
|
||||
this.EXPORTED_SYMBOLS = ["BrowserElementParentBuilder"];
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
@ -25,8 +25,10 @@ XPCOMUtils.defineLazyGetter(this, "DOMApplicationRegistry", function () {
|
|||
return DOMApplicationRegistry;
|
||||
});
|
||||
|
||||
const TOUCH_EVENTS_ENABLED_PREF = "dom.w3c_touch_events.enabled";
|
||||
|
||||
function debug(msg) {
|
||||
//dump("BrowserElementParent - " + msg + "\n");
|
||||
//dump("BrowserElementParent.jsm - " + msg + "\n");
|
||||
}
|
||||
|
||||
function getIntPref(prefName, def) {
|
||||
|
@ -57,57 +59,101 @@ function visibilityChangeHandler(e) {
|
|||
}
|
||||
}
|
||||
|
||||
function defineNoReturnMethod(fn) {
|
||||
return function method() {
|
||||
if (!this._domRequestReady) {
|
||||
// Remote browser haven't been created, we just queue the API call.
|
||||
let args = Array.slice(arguments);
|
||||
args.unshift(this);
|
||||
this._pendingAPICalls.push(method.bind.apply(fn, args));
|
||||
return;
|
||||
this.BrowserElementParentBuilder = {
|
||||
create: function create(frameLoader, hasRemoteFrame, isPendingFrame) {
|
||||
return new BrowserElementParent(frameLoader, hasRemoteFrame);
|
||||
}
|
||||
if (this._isAlive()) {
|
||||
fn.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function defineDOMRequestMethod(msgName) {
|
||||
return function() {
|
||||
return this._sendDOMRequest(msgName);
|
||||
};
|
||||
}
|
||||
|
||||
function BrowserElementParent() {
|
||||
debug("Creating new BrowserElementParent object");
|
||||
function BrowserElementParent(frameLoader, hasRemoteFrame, isPendingFrame) {
|
||||
debug("Creating new BrowserElementParent object for " + frameLoader);
|
||||
this._domRequestCounter = 0;
|
||||
this._domRequestReady = false;
|
||||
this._pendingAPICalls = [];
|
||||
this._pendingDOMRequests = {};
|
||||
this._pendingSetInputMethodActive = [];
|
||||
this._hasRemoteFrame = hasRemoteFrame;
|
||||
this._nextPaintListeners = [];
|
||||
|
||||
Services.obs.addObserver(this, 'ask-children-to-exit-fullscreen', /* ownsWeak = */ true);
|
||||
Services.obs.addObserver(this, 'oop-frameloader-crashed', /* ownsWeak = */ true);
|
||||
Services.obs.addObserver(this, 'copypaste-docommand', /* ownsWeak = */ true);
|
||||
}
|
||||
|
||||
BrowserElementParent.prototype = {
|
||||
|
||||
classDescription: "BrowserElementAPI implementation",
|
||||
classID: Components.ID("{9f171ac4-0939-4ef8-b360-3408aedc3060}"),
|
||||
contractID: "@mozilla.org/dom/browser-element-api;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserElementAPI,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
setFrameLoader: function(frameLoader) {
|
||||
this._frameLoader = frameLoader;
|
||||
this._frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
|
||||
let self = this;
|
||||
if (!this._frameElement) {
|
||||
debug("No frame element?");
|
||||
return;
|
||||
}
|
||||
|
||||
Services.obs.addObserver(this, 'ask-children-to-exit-fullscreen', /* ownsWeak = */ true);
|
||||
Services.obs.addObserver(this, 'oop-frameloader-crashed', /* ownsWeak = */ true);
|
||||
Services.obs.addObserver(this, 'copypaste-docommand', /* ownsWeak = */ true);
|
||||
|
||||
let defineMethod = function(name, fn) {
|
||||
XPCNativeWrapper.unwrap(self._frameElement)[name] = Cu.exportFunction(function() {
|
||||
if (self._isAlive()) {
|
||||
return fn.apply(self, arguments);
|
||||
}
|
||||
}, self._frameElement);
|
||||
}
|
||||
|
||||
let defineNoReturnMethod = function(name, fn) {
|
||||
XPCNativeWrapper.unwrap(self._frameElement)[name] = Cu.exportFunction(function method() {
|
||||
if (!self._domRequestReady) {
|
||||
// Remote browser haven't been created, we just queue the API call.
|
||||
let args = Array.slice(arguments);
|
||||
args.unshift(self);
|
||||
self._pendingAPICalls.push(method.bind.apply(fn, args));
|
||||
return;
|
||||
}
|
||||
if (self._isAlive()) {
|
||||
fn.apply(self, arguments);
|
||||
}
|
||||
}, self._frameElement);
|
||||
};
|
||||
|
||||
let defineDOMRequestMethod = function(domName, msgName) {
|
||||
XPCNativeWrapper.unwrap(self._frameElement)[domName] = Cu.exportFunction(function() {
|
||||
return self._sendDOMRequest(msgName);
|
||||
}, self._frameElement);
|
||||
}
|
||||
|
||||
// Define methods on the frame element.
|
||||
defineNoReturnMethod('setVisible', this._setVisible);
|
||||
defineDOMRequestMethod('getVisible', 'get-visible');
|
||||
|
||||
// Not expose security sensitive browser API for widgets
|
||||
if (!this._frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsWidget) {
|
||||
defineNoReturnMethod('sendMouseEvent', this._sendMouseEvent);
|
||||
|
||||
// 0 = disabled, 1 = enabled, 2 - auto detect
|
||||
if (getIntPref(TOUCH_EVENTS_ENABLED_PREF, 0) != 0) {
|
||||
defineNoReturnMethod('sendTouchEvent', this._sendTouchEvent);
|
||||
}
|
||||
defineNoReturnMethod('goBack', this._goBack);
|
||||
defineNoReturnMethod('goForward', this._goForward);
|
||||
defineNoReturnMethod('reload', this._reload);
|
||||
defineNoReturnMethod('stop', this._stop);
|
||||
defineMethod('download', this._download);
|
||||
defineDOMRequestMethod('purgeHistory', 'purge-history');
|
||||
defineMethod('getScreenshot', this._getScreenshot);
|
||||
defineNoReturnMethod('zoom', this._zoom);
|
||||
|
||||
defineDOMRequestMethod('getCanGoBack', 'get-can-go-back');
|
||||
defineDOMRequestMethod('getCanGoForward', 'get-can-go-forward');
|
||||
defineDOMRequestMethod('getContentDimensions', 'get-contentdimensions');
|
||||
}
|
||||
|
||||
defineMethod('addNextPaintListener', this._addNextPaintListener);
|
||||
defineMethod('removeNextPaintListener', this._removeNextPaintListener);
|
||||
defineNoReturnMethod('setActive', this._setActive);
|
||||
defineMethod('getActive', 'this._getActive');
|
||||
|
||||
let principal = this._frameElement.ownerDocument.nodePrincipal;
|
||||
let perm = Services.perms
|
||||
.testExactPermissionFromPrincipal(principal, "input-manage");
|
||||
if (perm === Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
defineMethod('setInputMethodActive', this._setInputMethodActive);
|
||||
}
|
||||
|
||||
// Listen to visibilitychange on the iframe's owner window, and forward
|
||||
// changes down to the child. We want to do this while registering as few
|
||||
// visibilitychange listeners on _window as possible, because such a listener
|
||||
|
@ -130,9 +176,20 @@ BrowserElementParent.prototype = {
|
|||
|
||||
// Insert ourself into the prompt service.
|
||||
BrowserElementPromptService.mapFrameToBrowserElementParent(this._frameElement, this);
|
||||
if (!isPendingFrame) {
|
||||
this._setupMessageListener();
|
||||
this._registerAppManifest();
|
||||
},
|
||||
} else {
|
||||
// if we are a pending frame, we setup message manager after
|
||||
// observing remote-browser-frame-shown
|
||||
Services.obs.addObserver(this, 'remote-browser-frame-shown', /* ownsWeak = */ true);
|
||||
}
|
||||
}
|
||||
|
||||
BrowserElementParent.prototype = {
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
_runPendingAPICall: function() {
|
||||
if (!this._pendingAPICalls) {
|
||||
|
@ -535,27 +592,20 @@ BrowserElementParent.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
setVisible: defineNoReturnMethod(function(visible) {
|
||||
_setVisible: function(visible) {
|
||||
this._sendAsyncMsg('set-visible', {visible: visible});
|
||||
this._frameLoader.visible = visible;
|
||||
}),
|
||||
},
|
||||
|
||||
getVisible: defineDOMRequestMethod('get-visible'),
|
||||
|
||||
setActive: defineNoReturnMethod(function(active) {
|
||||
_setActive: function(active) {
|
||||
this._frameLoader.visible = active;
|
||||
}),
|
||||
|
||||
getActive: function() {
|
||||
if (!this._isAlive()) {
|
||||
throw Components.Exception("Dead content process",
|
||||
Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
},
|
||||
|
||||
_getActive: function() {
|
||||
return this._frameLoader.visible;
|
||||
},
|
||||
|
||||
sendMouseEvent: defineNoReturnMethod(function(type, x, y, button, clickCount, modifiers) {
|
||||
_sendMouseEvent: function(type, x, y, button, clickCount, modifiers) {
|
||||
this._sendAsyncMsg("send-mouse-event", {
|
||||
"type": type,
|
||||
"x": x,
|
||||
|
@ -564,9 +614,9 @@ BrowserElementParent.prototype = {
|
|||
"clickCount": clickCount,
|
||||
"modifiers": modifiers
|
||||
});
|
||||
}),
|
||||
},
|
||||
|
||||
sendTouchEvent: defineNoReturnMethod(function(type, identifiers, touchesX, touchesY,
|
||||
_sendTouchEvent: function(type, identifiers, touchesX, touchesY,
|
||||
radiisX, radiisY, rotationAngles, forces,
|
||||
count, modifiers) {
|
||||
|
||||
|
@ -596,45 +646,35 @@ BrowserElementParent.prototype = {
|
|||
"modifiers": modifiers
|
||||
});
|
||||
}
|
||||
}),
|
||||
},
|
||||
|
||||
getCanGoBack: defineDOMRequestMethod('get-can-go-back'),
|
||||
getCanGoForward: defineDOMRequestMethod('get-can-go-forward'),
|
||||
getContentDimensions: defineDOMRequestMethod('get-contentdimensions'),
|
||||
|
||||
goBack: defineNoReturnMethod(function() {
|
||||
_goBack: function() {
|
||||
this._sendAsyncMsg('go-back');
|
||||
}),
|
||||
},
|
||||
|
||||
goForward: defineNoReturnMethod(function() {
|
||||
_goForward: function() {
|
||||
this._sendAsyncMsg('go-forward');
|
||||
}),
|
||||
},
|
||||
|
||||
reload: defineNoReturnMethod(function(hardReload) {
|
||||
_reload: function(hardReload) {
|
||||
this._sendAsyncMsg('reload', {hardReload: hardReload});
|
||||
}),
|
||||
},
|
||||
|
||||
stop: defineNoReturnMethod(function() {
|
||||
_stop: function() {
|
||||
this._sendAsyncMsg('stop');
|
||||
}),
|
||||
},
|
||||
|
||||
/*
|
||||
* The valid range of zoom scale is defined in preference "zoom.maxPercent" and "zoom.minPercent".
|
||||
*/
|
||||
zoom: defineNoReturnMethod(function(zoom) {
|
||||
_zoom: function(zoom) {
|
||||
zoom *= 100;
|
||||
zoom = Math.min(getIntPref("zoom.maxPercent", 300), zoom);
|
||||
zoom = Math.max(getIntPref("zoom.minPercent", 50), zoom);
|
||||
this._sendAsyncMsg('zoom', {zoom: zoom / 100.0});
|
||||
}),
|
||||
},
|
||||
|
||||
purgeHistory: defineDOMRequestMethod('purge-history'),
|
||||
|
||||
|
||||
download: function(_url, _options) {
|
||||
if (!this._isAlive()) {
|
||||
return null;
|
||||
}
|
||||
_download: function(_url, _options) {
|
||||
let ioService =
|
||||
Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
|
||||
let uri = ioService.newURI(_url, null, null);
|
||||
|
@ -754,12 +794,7 @@ BrowserElementParent.prototype = {
|
|||
return req;
|
||||
},
|
||||
|
||||
getScreenshot: function(_width, _height, _mimeType) {
|
||||
if (!this._isAlive()) {
|
||||
throw Components.Exception("Dead content process",
|
||||
Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
|
||||
_getScreenshot: function(_width, _height, _mimeType) {
|
||||
let width = parseInt(_width);
|
||||
let height = parseInt(_height);
|
||||
let mimeType = (typeof _mimeType === 'string') ?
|
||||
|
@ -779,18 +814,16 @@ BrowserElementParent.prototype = {
|
|||
this._nextPaintListeners = [];
|
||||
for (let listener of listeners) {
|
||||
try {
|
||||
listener.recvNextPaint();
|
||||
listener();
|
||||
} catch (e) {
|
||||
// If a listener throws we'll continue.
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addNextPaintListener: function(listener) {
|
||||
if (!this._isAlive()) {
|
||||
throw Components.Exception("Dead content process",
|
||||
Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
_addNextPaintListener: function(listener) {
|
||||
if (typeof listener != 'function')
|
||||
throw Components.Exception("Invalid argument", Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
let self = this;
|
||||
let run = function() {
|
||||
|
@ -804,11 +837,9 @@ BrowserElementParent.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
removeNextPaintListener: function(listener) {
|
||||
if (!this._isAlive()) {
|
||||
throw Components.Exception("Dead content process",
|
||||
Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
_removeNextPaintListener: function(listener) {
|
||||
if (typeof listener != 'function')
|
||||
throw Components.Exception("Invalid argument", Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
let self = this;
|
||||
let run = function() {
|
||||
|
@ -829,12 +860,7 @@ BrowserElementParent.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
setInputMethodActive: function(isActive) {
|
||||
if (!this._isAlive()) {
|
||||
throw Components.Exception("Dead content process",
|
||||
Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
|
||||
_setInputMethodActive: function(isActive) {
|
||||
if (typeof isActive !== 'boolean') {
|
||||
throw Components.Exception("Invalid argument",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
@ -896,10 +922,18 @@ BrowserElementParent.prototype = {
|
|||
case 'ask-children-to-exit-fullscreen':
|
||||
if (this._isAlive() &&
|
||||
this._frameElement.ownerDocument == subject &&
|
||||
this._frameLoader.QueryInterface(Ci.nsIFrameLoader).tabParent) {
|
||||
this._hasRemoteFrame) {
|
||||
this._sendAsyncMsg('exit-fullscreen');
|
||||
}
|
||||
break;
|
||||
case 'remote-browser-frame-shown':
|
||||
if (this._frameLoader == subject) {
|
||||
if (!this._mm) {
|
||||
this._setupMessageListener();
|
||||
this._registerAppManifest();
|
||||
}
|
||||
Services.obs.removeObserver(this, 'remote-browser-frame-shown');
|
||||
}
|
||||
case 'copypaste-docommand':
|
||||
if (this._isAlive() && this._frameElement.isEqualNode(subject.wrappedJSObject)) {
|
||||
this._sendAsyncMsg('do-command', { command: data });
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
component {9f171ac4-0939-4ef8-b360-3408aedc3060} BrowserElementParent.js
|
||||
contract @mozilla.org/dom/browser-element-api;1 {9f171ac4-0939-4ef8-b360-3408aedc3060}
|
||||
component {ddeafdac-cb39-47c4-9cb8-c9027ee36d26} BrowserElementParent.js
|
||||
contract @mozilla.org/browser-element-parent-factory;1 {ddeafdac-cb39-47c4-9cb8-c9027ee36d26}
|
||||
category app-startup BrowserElementParentFactory service,@mozilla.org/browser-element-parent-factory;1
|
||||
|
|
Загрузка…
Ссылка в новой задаче