зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1481021: Part 2 - Stop loading SpecialPowers into frame script scopes. r=bz,jmaher,aswan
Loading SpecialPowers into frame scripts has side-effects, detailed in part 1, which are undesirable. The main side-effect that I'm trying to get rid of here is the force-enabling of permissive COWs in frame script scopes, which is blocking changes that I need to make elsewhere. But both that and the scope pollution it causes are likely to allow code to work when running in automation which fails in real world usage. This patch changes our special powers frame scripts to load specialpowers.js and specialpowersAPI.js as JSMs, which run in their own global, but define most of the same properties on our frame script globals. Most other callers still load those scripts via <script> tags or the subscript loader, and should ideally migrated in a follow-up. But even so, this patch still gives us a cleaner separation of the frame script and non-frame-script loading code. MozReview-Commit-ID: CR226gCDaGY --HG-- extra : rebase_source : fa253abde2029ec09c724404106d83623f064875
This commit is contained in:
Родитель
c1969dbca2
Коммит
b9e9588050
|
@ -24,6 +24,8 @@ const whitelist = {
|
|||
]),
|
||||
modules: new Set([
|
||||
"chrome://mochikit/content/ShutdownLeaksCollector.jsm",
|
||||
"resource://specialpowers/specialpowers.js",
|
||||
"resource://specialpowers/specialpowersAPI.js",
|
||||
|
||||
// General utilities
|
||||
"resource://gre/modules/AppConstants.jsm",
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
title="bug 89419 test">
|
||||
|
||||
<script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
|
||||
<script type="text/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js"/>
|
||||
<script type="text/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SpecialPowersObserverAPI.js"/>
|
||||
<script type="text/javascript"
|
||||
|
|
|
@ -105,7 +105,7 @@ if (params.runUntilFailure) {
|
|||
|
||||
// closeWhenDone tells us to close the browser when complete
|
||||
if (params.closeWhenDone) {
|
||||
TestRunner.onComplete = SpecialPowers.quit;
|
||||
TestRunner.onComplete = SpecialPowers.quit.bind(SpecialPowers);
|
||||
}
|
||||
|
||||
if (params.failureFile) {
|
||||
|
|
|
@ -16,8 +16,7 @@ var EXPORTED_SYMBOLS = ["SpecialPowersObserver"];
|
|||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
Cu.importGlobalProperties(["File"]);
|
||||
|
||||
const CHILD_SCRIPT = "resource://specialpowers/specialpowers.js";
|
||||
const CHILD_SCRIPT_API = "resource://specialpowers/specialpowersAPI.js";
|
||||
const CHILD_SCRIPT_API = "resource://specialpowers/specialpowersFrameScript.js";
|
||||
const CHILD_LOGGER_SCRIPT = "resource://specialpowers/MozillaLogger.js";
|
||||
|
||||
|
||||
|
@ -82,7 +81,6 @@ SpecialPowersObserver.prototype._loadFrameScript = function() {
|
|||
|
||||
this._messageManager.loadFrameScript(CHILD_LOGGER_SCRIPT, true);
|
||||
this._messageManager.loadFrameScript(CHILD_SCRIPT_API, true);
|
||||
this._messageManager.loadFrameScript(CHILD_SCRIPT, true);
|
||||
this._isFrameScriptLoaded = true;
|
||||
this._createdFiles = null;
|
||||
}
|
||||
|
@ -149,7 +147,6 @@ SpecialPowersObserver.prototype.uninit = function() {
|
|||
|
||||
this._messageManager.removeDelayedFrameScript(CHILD_LOGGER_SCRIPT);
|
||||
this._messageManager.removeDelayedFrameScript(CHILD_SCRIPT_API);
|
||||
this._messageManager.removeDelayedFrameScript(CHILD_SCRIPT);
|
||||
this._isFrameScriptLoaded = false;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,12 +5,21 @@
|
|||
* order to be used as a replacement for UniversalXPConnect
|
||||
*/
|
||||
|
||||
/* import-globals-from specialpowersAPI.js */
|
||||
/* globals addMessageListener, removeMessageListener, sendSyncMessage, sendAsyncMessage */
|
||||
/* globals bindDOMWindowUtils, SpecialPowersAPI */
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function SpecialPowers(window) {
|
||||
Services.scriptloader.loadSubScript("resource://specialpowers/MozillaLogger.js", this);
|
||||
|
||||
var EXPORTED_SYMBOLS = ["SpecialPowers", "attachSpecialPowersToWindow"];
|
||||
|
||||
ChromeUtils.import("resource://specialpowers/specialpowersAPI.js", this);
|
||||
|
||||
Cu.forcePermissiveCOWs();
|
||||
|
||||
function SpecialPowers(window, mm) {
|
||||
this.mm = mm;
|
||||
|
||||
this.window = Cu.getWeakReference(window);
|
||||
this._windowID = window.windowUtils.currentInnerWindowID;
|
||||
this._encounteredCrashDumpFiles = [];
|
||||
|
@ -47,18 +56,18 @@ function SpecialPowers(window) {
|
|||
"SPExtensionMessage",
|
||||
"SPRequestDumpCoverageCounters",
|
||||
"SPRequestResetCoverageCounters"];
|
||||
addMessageListener("SPPingService", this._messageListener);
|
||||
addMessageListener("SpecialPowers.FilesCreated", this._messageListener);
|
||||
addMessageListener("SpecialPowers.FilesError", this._messageListener);
|
||||
mm.addMessageListener("SPPingService", this._messageListener);
|
||||
mm.addMessageListener("SpecialPowers.FilesCreated", this._messageListener);
|
||||
mm.addMessageListener("SpecialPowers.FilesError", this._messageListener);
|
||||
let self = this;
|
||||
Services.obs.addObserver(function onInnerWindowDestroyed(subject, topic, data) {
|
||||
var id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||
if (self._windowID === id) {
|
||||
Services.obs.removeObserver(onInnerWindowDestroyed, "inner-window-destroyed");
|
||||
try {
|
||||
removeMessageListener("SPPingService", self._messageListener);
|
||||
removeMessageListener("SpecialPowers.FilesCreated", self._messageListener);
|
||||
removeMessageListener("SpecialPowers.FilesError", self._messageListener);
|
||||
mm.removeMessageListener("SPPingService", self._messageListener);
|
||||
mm.removeMessageListener("SpecialPowers.FilesCreated", self._messageListener);
|
||||
mm.removeMessageListener("SpecialPowers.FilesError", self._messageListener);
|
||||
} catch (e) {
|
||||
// Ignore the exception which the message manager has been destroyed.
|
||||
if (e.result != Cr.NS_ERROR_ILLEGAL_VALUE) {
|
||||
|
@ -83,33 +92,34 @@ SpecialPowers.prototype._sendSyncMessage = function(msgname, msg) {
|
|||
if (!this.SP_SYNC_MESSAGES.includes(msgname)) {
|
||||
dump("TEST-INFO | specialpowers.js | Unexpected SP message: " + msgname + "\n");
|
||||
}
|
||||
return sendSyncMessage(msgname, msg);
|
||||
let result = this.mm.sendSyncMessage(msgname, msg);
|
||||
return Cu.cloneInto(result, this);
|
||||
};
|
||||
|
||||
SpecialPowers.prototype._sendAsyncMessage = function(msgname, msg) {
|
||||
if (!this.SP_ASYNC_MESSAGES.includes(msgname)) {
|
||||
dump("TEST-INFO | specialpowers.js | Unexpected SP message: " + msgname + "\n");
|
||||
}
|
||||
sendAsyncMessage(msgname, msg);
|
||||
this.mm.sendAsyncMessage(msgname, msg);
|
||||
};
|
||||
|
||||
SpecialPowers.prototype._addMessageListener = function(msgname, listener) {
|
||||
addMessageListener(msgname, listener);
|
||||
sendAsyncMessage("SPPAddNestedMessageListener", { name: msgname });
|
||||
this.mm.addMessageListener(msgname, listener);
|
||||
this.mm.sendAsyncMessage("SPPAddNestedMessageListener", { name: msgname });
|
||||
};
|
||||
|
||||
SpecialPowers.prototype._removeMessageListener = function(msgname, listener) {
|
||||
removeMessageListener(msgname, listener);
|
||||
this.mm.removeMessageListener(msgname, listener);
|
||||
};
|
||||
|
||||
SpecialPowers.prototype.registerProcessCrashObservers = function() {
|
||||
addMessageListener("SPProcessCrashService", this._messageListener);
|
||||
sendSyncMessage("SPProcessCrashService", { op: "register-observer" });
|
||||
this.mm.addMessageListener("SPProcessCrashService", this._messageListener);
|
||||
this.mm.sendSyncMessage("SPProcessCrashService", { op: "register-observer" });
|
||||
};
|
||||
|
||||
SpecialPowers.prototype.unregisterProcessCrashObservers = function() {
|
||||
removeMessageListener("SPProcessCrashService", this._messageListener);
|
||||
sendSyncMessage("SPProcessCrashService", { op: "unregister-observer" });
|
||||
this.mm.removeMessageListener("SPProcessCrashService", this._messageListener);
|
||||
this.mm.sendSyncMessage("SPProcessCrashService", { op: "unregister-observer" });
|
||||
};
|
||||
|
||||
SpecialPowers.prototype._messageReceived = function(aMessage) {
|
||||
|
@ -139,7 +149,7 @@ SpecialPowers.prototype._messageReceived = function(aMessage) {
|
|||
this._createFilesOnSuccess = null;
|
||||
this._createFilesOnError = null;
|
||||
if (createdHandler) {
|
||||
createdHandler(aMessage.data);
|
||||
createdHandler(Cu.cloneInto(aMessage.data, this.mm.content));
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -157,7 +167,7 @@ SpecialPowers.prototype._messageReceived = function(aMessage) {
|
|||
};
|
||||
|
||||
SpecialPowers.prototype.quit = function() {
|
||||
sendAsyncMessage("SpecialPowers.Quit", {});
|
||||
this.mm.sendAsyncMessage("SpecialPowers.Quit", {});
|
||||
};
|
||||
|
||||
// fileRequests is an array of file requests. Each file request is an object.
|
||||
|
@ -172,18 +182,18 @@ SpecialPowers.prototype.createFiles = function(fileRequests, onCreation, onError
|
|||
|
||||
this._createFilesOnSuccess = onCreation;
|
||||
this._createFilesOnError = onError;
|
||||
sendAsyncMessage("SpecialPowers.CreateFiles", fileRequests);
|
||||
this.mm.sendAsyncMessage("SpecialPowers.CreateFiles", fileRequests);
|
||||
};
|
||||
|
||||
// Remove the files that were created using |SpecialPowers.createFiles()|.
|
||||
// This will be automatically called by |SimpleTest.finish()|.
|
||||
SpecialPowers.prototype.removeFiles = function() {
|
||||
sendAsyncMessage("SpecialPowers.RemoveFiles", {});
|
||||
this.mm.sendAsyncMessage("SpecialPowers.RemoveFiles", {});
|
||||
};
|
||||
|
||||
SpecialPowers.prototype.executeAfterFlushingMessageQueue = function(aCallback) {
|
||||
this._pongHandlers.push(aCallback);
|
||||
sendAsyncMessage("SPPingService", { op: "ping" });
|
||||
this.mm.sendAsyncMessage("SPPingService", { op: "ping" });
|
||||
};
|
||||
|
||||
SpecialPowers.prototype.nestedFrameSetup = function() {
|
||||
|
@ -215,9 +225,7 @@ SpecialPowers.prototype.nestedFrameSetup = function() {
|
|||
});
|
||||
});
|
||||
|
||||
mm.loadFrameScript("resource://specialpowers/MozillaLogger.js", false);
|
||||
mm.loadFrameScript("resource://specialpowers/specialpowersAPI.js", false);
|
||||
mm.loadFrameScript("resource://specialpowers/specialpowers.js", false);
|
||||
mm.loadFrameScript("resource://specialpowers/specialpowersFrameScript.js", false);
|
||||
|
||||
let frameScript = "SpecialPowers.prototype.IsInNestedFrame=true;";
|
||||
mm.loadFrameScript("data:," + frameScript, false);
|
||||
|
@ -232,13 +240,13 @@ SpecialPowers.prototype.isServiceWorkerRegistered = function() {
|
|||
};
|
||||
|
||||
// Attach our API to the window.
|
||||
function attachSpecialPowersToWindow(aWindow) {
|
||||
function attachSpecialPowersToWindow(aWindow, mm) {
|
||||
try {
|
||||
if ((aWindow !== null) &&
|
||||
(aWindow !== undefined) &&
|
||||
(aWindow.wrappedJSObject) &&
|
||||
!(aWindow.wrappedJSObject.SpecialPowers)) {
|
||||
let sp = new SpecialPowers(aWindow);
|
||||
let sp = new SpecialPowers(aWindow, mm);
|
||||
aWindow.wrappedJSObject.SpecialPowers = sp;
|
||||
if (sp.IsInNestedFrame) {
|
||||
sp.addPermission("allowXULXBL", true, aWindow.document);
|
||||
|
@ -249,25 +257,6 @@ function attachSpecialPowersToWindow(aWindow) {
|
|||
}
|
||||
}
|
||||
|
||||
// This is a frame script, so it may be running in a content process.
|
||||
// In any event, it is targeted at a specific "tab", so we listen for
|
||||
// the DOMWindowCreated event to be notified about content windows
|
||||
// being created in this context.
|
||||
|
||||
function SpecialPowersManager() {
|
||||
addEventListener("DOMWindowCreated", this, false);
|
||||
}
|
||||
|
||||
SpecialPowersManager.prototype = {
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
var window = aEvent.target.defaultView;
|
||||
attachSpecialPowersToWindow(window);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var specialpowersmanager = new SpecialPowersManager();
|
||||
|
||||
this.SpecialPowers = SpecialPowers;
|
||||
this.attachSpecialPowersToWindow = attachSpecialPowersToWindow;
|
||||
|
||||
|
@ -276,5 +265,5 @@ this.attachSpecialPowersToWindow = attachSpecialPowersToWindow;
|
|||
if (typeof window != "undefined") {
|
||||
window.addMessageListener = function() {};
|
||||
window.removeMessageListener = function() {};
|
||||
window.wrappedJSObject.SpecialPowers = new SpecialPowers(window);
|
||||
window.wrappedJSObject.SpecialPowers = new SpecialPowers(window, window);
|
||||
}
|
||||
|
|
|
@ -8,13 +8,17 @@
|
|||
"use strict";
|
||||
|
||||
/* import-globals-from MozillaLogger.js */
|
||||
/* globals XPCNativeWrapper, content */
|
||||
/* globals XPCNativeWrapper */
|
||||
|
||||
var global = this;
|
||||
var EXPORTED_SYMBOLS = ["SpecialPowersAPI", "bindDOMWindowUtils"];
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
Services.scriptloader.loadSubScript("resource://specialpowers/MozillaLogger.js", this);
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "setTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "MockFilePicker",
|
||||
"resource://specialpowers/MockFilePicker.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "MockColorPicker",
|
||||
|
@ -582,6 +586,9 @@ SpecialPowersAPI.prototype = {
|
|||
let messageId = aMessage.json.id;
|
||||
let name = aMessage.json.name;
|
||||
let message = aMessage.json.message;
|
||||
if (this.mm) {
|
||||
message = new StructuredCloneHolder(message).deserialize(this.mm.content);
|
||||
}
|
||||
// Ignore message from other chrome script
|
||||
if (messageId != id)
|
||||
return;
|
||||
|
@ -754,12 +761,12 @@ SpecialPowersAPI.prototype = {
|
|||
setTimeout(callback, 0);
|
||||
// for mochitest-plain
|
||||
else
|
||||
content.window.setTimeout(callback, 0);
|
||||
this.mm.content.setTimeout(callback, 0);
|
||||
},
|
||||
|
||||
_delayCallbackTwice(callback) {
|
||||
function delayedCallback() {
|
||||
function delayAgain(aCallback) {
|
||||
let delayedCallback = () => {
|
||||
let delayAgain = (aCallback) => {
|
||||
// Using this._setTimeout doesn't work here
|
||||
// It causes failures in mochtests that use
|
||||
// multiple pushPrefEnv calls
|
||||
|
@ -768,10 +775,10 @@ SpecialPowersAPI.prototype = {
|
|||
setTimeout(aCallback, 0);
|
||||
// For mochitest-plain
|
||||
else
|
||||
content.window.setTimeout(aCallback, 0);
|
||||
}
|
||||
delayAgain(delayAgain(callback));
|
||||
}
|
||||
this.mm.content.setTimeout(aCallback, 0);
|
||||
};
|
||||
delayAgain(delayAgain.bind(this, callback));
|
||||
};
|
||||
return delayedCallback;
|
||||
},
|
||||
|
||||
|
@ -1461,10 +1468,10 @@ SpecialPowersAPI.prototype = {
|
|||
// XXX end of problematic APIs
|
||||
|
||||
addChromeEventListener(type, listener, capture, allowUntrusted) {
|
||||
addEventListener(type, listener, capture, allowUntrusted);
|
||||
this.mm.addEventListener(type, listener, capture, allowUntrusted);
|
||||
},
|
||||
removeChromeEventListener(type, listener, capture) {
|
||||
removeEventListener(type, listener, capture);
|
||||
this.mm.removeEventListener(type, listener, capture);
|
||||
},
|
||||
|
||||
// Note: each call to registerConsoleListener MUST be paired with a
|
||||
|
@ -1752,7 +1759,7 @@ SpecialPowersAPI.prototype = {
|
|||
// With aWindow, it is called in SimpleTest.waitForFocus to allow popup window opener focus switching
|
||||
if (aWindow)
|
||||
aWindow.focus();
|
||||
var mm = global;
|
||||
var mm = this.mm;
|
||||
if (aWindow) {
|
||||
let windowMM = aWindow.docShell.messageManager;
|
||||
if (windowMM) {
|
||||
|
@ -1775,7 +1782,7 @@ SpecialPowersAPI.prototype = {
|
|||
// in e10s b-c tests |content.window| is a CPOW whereas |window| works fine.
|
||||
// for some non-e10s mochi tests, |window| is null whereas |content.window|
|
||||
// works fine. So we take whatever is non-null!
|
||||
xferable.init(this._getDocShell(typeof(window) == "undefined" ? content.window : window)
|
||||
xferable.init(this._getDocShell(typeof(window) == "undefined" ? this.mm.content.window : window)
|
||||
.QueryInterface(Ci.nsILoadContext));
|
||||
xferable.addDataFlavor(flavor);
|
||||
Services.clipboard.getData(xferable, whichClipboard);
|
||||
|
@ -2095,7 +2102,7 @@ SpecialPowersAPI.prototype = {
|
|||
state = "unloaded";
|
||||
resolveUnload();
|
||||
} else if (msg.data.type in handler) {
|
||||
handler[msg.data.type](...msg.data.args);
|
||||
handler[msg.data.type](...Cu.cloneInto(msg.data.args, this.window));
|
||||
} else {
|
||||
dump(`Unexpected: ${msg.data.type}\n`);
|
||||
}
|
||||
|
@ -2116,7 +2123,7 @@ SpecialPowersAPI.prototype = {
|
|||
|
||||
createChromeCache(name, url) {
|
||||
let principal = this._getPrincipalFromArg(url);
|
||||
return wrapIfUnwrapped(new content.window.CacheStorage(name, principal));
|
||||
return wrapIfUnwrapped(new this.mm.content.CacheStorage(name, principal));
|
||||
},
|
||||
|
||||
loadChannelAndReturnStatus(url, loadUsingSystemPrincipal) {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
"use strict";
|
||||
|
||||
/* globals attachSpecialPowersToWindow */
|
||||
|
||||
let mm = this;
|
||||
|
||||
ChromeUtils.import("resource://specialpowers/specialpowersAPI.js", this);
|
||||
ChromeUtils.import("resource://specialpowers/specialpowers.js", this);
|
||||
|
||||
// This is a frame script, so it may be running in a content process.
|
||||
// In any event, it is targeted at a specific "tab", so we listen for
|
||||
// the DOMWindowCreated event to be notified about content windows
|
||||
// being created in this context.
|
||||
|
||||
function SpecialPowersManager() {
|
||||
addEventListener("DOMWindowCreated", this, false);
|
||||
}
|
||||
|
||||
SpecialPowersManager.prototype = {
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
var window = aEvent.target.defaultView;
|
||||
attachSpecialPowersToWindow(window, mm);
|
||||
}
|
||||
};
|
||||
|
||||
var specialpowersmanager = new SpecialPowersManager();
|
|
@ -23,6 +23,7 @@ FINAL_TARGET_FILES.content += [
|
|||
'content/MozillaLogger.js',
|
||||
'content/specialpowers.js',
|
||||
'content/specialpowersAPI.js',
|
||||
'content/specialpowersFrameScript.js',
|
||||
'content/SpecialPowersObserver.jsm',
|
||||
'content/SpecialPowersObserverAPI.js',
|
||||
]
|
||||
|
|
Загрузка…
Ссылка в новой задаче