import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,20 @@
#
# 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/.
TEST_EXTENSIONS_DIR = $(DEPTH)/testing/specialpowers
XPI_PKGNAME = specialpowers@mozilla.org
include $(topsrcdir)/config/rules.mk
libs-preqs = \
$(call mkdir_deps,$(TEST_EXTENSIONS_DIR)) \
$(NULL)
libs:: $(libs-preqs)
(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -)
$(NSINSTALL) -D $(DEPTH)/_tests/testing/mochitest/extensions/specialpowers
cp -RL $(DEPTH)/testing/specialpowers/specialpowers $(DEPTH)/_tests/testing/mochitest/extensions
$(NSINSTALL) -D $(DEPTH)/_tests/reftest/specialpowers
cp -RL $(DEPTH)/testing/specialpowers/specialpowers $(DEPTH)/_tests/reftest

39
testing/specialpowers/bootstrap.js vendored Normal file
View file

@ -0,0 +1,39 @@
/* 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/. */
Components.utils.import("resource://gre/modules/Services.jsm");
var spObserver;
function startup(data, reason) {
let observer = {};
Components.utils.import("chrome://specialpowers/content/SpecialPowersObserver.jsm", observer);
let registrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
registrar.registerFactory(
observer.SpecialPowersObserver.prototype.classID,
"SpecialPowersObserver",
observer.SpecialPowersObserver.prototype.contractID,
observer.SpecialPowersObserverFactory
);
spObserver = new observer.SpecialPowersObserver();
spObserver.init();
}
function shutdown(data, reason) {
let observer = {};
Components.utils.import("chrome://specialpowers/content/SpecialPowersObserver.jsm", observer);
let registrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
registrar.unregisterFactory(
observer.SpecialPowersObserver.prototype.classID,
observer.SpecialPowersObserverFactory
);
spObserver.uninit();
}
function install(data, reason) {}
function uninstall(data, reason) {}

View file

@ -0,0 +1,117 @@
/* 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/. */
this.EXPORTED_SYMBOLS = ["MockColorPicker"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cm = Components.manager;
const Cu = Components.utils;
const CONTRACT_ID = "@mozilla.org/colorpicker;1";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
// Allow stuff from this scope to be accessed from non-privileged scopes. This
// would crash if used outside of automation.
Cu.forcePermissiveCOWs();
var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
var oldClassID = "", oldFactory = null;
var newClassID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID();
var newFactory = function (window) {
return {
createInstance: function(aOuter, aIID) {
if (aOuter)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return new MockColorPickerInstance(window).QueryInterface(aIID);
},
lockFactory: function(aLock) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
};
}
this.MockColorPicker = {
init: function(window) {
this.reset();
this.factory = newFactory(window);
if (!registrar.isCIDRegistered(newClassID)) {
try {
oldClassID = registrar.contractIDToCID(CONTRACT_ID);
oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
} catch(ex) {
oldClassID = "";
oldFactory = null;
dump("TEST-INFO | can't get colorpicker registered component, " +
"assuming there is none");
}
if (oldClassID != "" && oldFactory != null) {
registrar.unregisterFactory(oldClassID, oldFactory);
}
registrar.registerFactory(newClassID, "", CONTRACT_ID, this.factory);
}
},
reset: function() {
this.returnColor = "";
this.showCallback = null;
this.shown = false;
this.showing = false;
},
cleanup: function() {
var previousFactory = this.factory;
this.reset();
this.factory = null;
registrar.unregisterFactory(newClassID, previousFactory);
if (oldClassID != "" && oldFactory != null) {
registrar.registerFactory(oldClassID, "", CONTRACT_ID, oldFactory);
}
}
};
function MockColorPickerInstance(window) {
this.window = window;
};
MockColorPickerInstance.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIColorPicker]),
init: function(aParent, aTitle, aInitialColor) {
this.parent = aParent;
this.initialColor = aInitialColor;
},
initialColor: "",
parent: null,
open: function(aColorPickerShownCallback) {
MockColorPicker.showing = true;
MockColorPicker.shown = true;
this.window.setTimeout(function() {
let result = "";
try {
if (typeof MockColorPicker.showCallback == "function") {
var updateCb = function(color) {
result = color;
aColorPickerShownCallback.update(color);
};
let returnColor = MockColorPicker.showCallback(this, updateCb);
if (typeof returnColor === "string") {
result = returnColor;
}
} else if (typeof MockColorPicker.returnColor === "string") {
result = MockColorPicker.returnColor;
}
} catch(ex) {
dump("TEST-UNEXPECTED-FAIL | Exception in MockColorPicker.jsm open() " +
"method: " + ex + "\n");
}
if (aColorPickerShownCallback) {
aColorPickerShownCallback.done(result);
}
}.bind(this), 0);
}
};

View file

@ -0,0 +1,235 @@
/* 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/. */
this.EXPORTED_SYMBOLS = ["MockFilePicker"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cm = Components.manager;
const Cu = Components.utils;
const CONTRACT_ID = "@mozilla.org/filepicker;1";
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
// Allow stuff from this scope to be accessed from non-privileged scopes. This
// would crash if used outside of automation.
Cu.forcePermissiveCOWs();
var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
var oldClassID, oldFactory;
var newClassID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID();
var newFactory = function (window) {
return {
createInstance: function(aOuter, aIID) {
if (aOuter)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return new MockFilePickerInstance(window).QueryInterface(aIID);
},
lockFactory: function(aLock) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
};
}
this.MockFilePicker = {
returnOK: Ci.nsIFilePicker.returnOK,
returnCancel: Ci.nsIFilePicker.returnCancel,
returnReplace: Ci.nsIFilePicker.returnReplace,
filterAll: Ci.nsIFilePicker.filterAll,
filterHTML: Ci.nsIFilePicker.filterHTML,
filterText: Ci.nsIFilePicker.filterText,
filterImages: Ci.nsIFilePicker.filterImages,
filterXML: Ci.nsIFilePicker.filterXML,
filterXUL: Ci.nsIFilePicker.filterXUL,
filterApps: Ci.nsIFilePicker.filterApps,
filterAllowURLs: Ci.nsIFilePicker.filterAllowURLs,
filterAudio: Ci.nsIFilePicker.filterAudio,
filterVideo: Ci.nsIFilePicker.filterVideo,
window: null,
init: function(window) {
this.window = window;
this.reset();
this.factory = newFactory(window);
if (!registrar.isCIDRegistered(newClassID)) {
oldClassID = registrar.contractIDToCID(CONTRACT_ID);
oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
registrar.unregisterFactory(oldClassID, oldFactory);
registrar.registerFactory(newClassID, "", CONTRACT_ID, this.factory);
}
},
reset: function() {
this.appendFilterCallback = null;
this.appendFiltersCallback = null;
this.displayDirectory = null;
this.filterIndex = 0;
this.mode = null;
this.returnFiles = [];
this.returnValue = null;
this.showCallback = null;
this.shown = false;
this.showing = false;
},
cleanup: function() {
var previousFactory = this.factory;
this.reset();
this.factory = null;
if (oldFactory) {
registrar.unregisterFactory(newClassID, previousFactory);
registrar.registerFactory(oldClassID, "", CONTRACT_ID, oldFactory);
}
},
useAnyFile: function() {
var file = FileUtils.getDir("TmpD", [], false);
file.append("testfile");
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
this.returnFiles = [file];
},
useBlobFile: function() {
var blob = new this.window.Blob([]);
var file = new this.window.File([blob], 'helloworld.txt', { type: 'plain/text' });
this.returnFiles = [file];
},
useDirectory: function(aPath) {
var directory = new this.window.Directory(aPath);
this.returnFiles = [directory];
},
isNsIFile: function(aFile) {
let ret = false;
try {
if (aFile.QueryInterface(Ci.nsIFile))
ret = true;
} catch(e) {}
return ret;
}
};
function MockFilePickerInstance(window) {
this.window = window;
};
MockFilePickerInstance.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFilePicker]),
init: function(aParent, aTitle, aMode) {
MockFilePicker.mode = aMode;
this.filterIndex = MockFilePicker.filterIndex;
this.parent = aParent;
},
appendFilter: function(aTitle, aFilter) {
if (typeof MockFilePicker.appendFilterCallback == "function")
MockFilePicker.appendFilterCallback(this, aTitle, aFilter);
},
appendFilters: function(aFilterMask) {
if (typeof MockFilePicker.appendFiltersCallback == "function")
MockFilePicker.appendFiltersCallback(this, aFilterMask);
},
defaultString: "",
defaultExtension: "",
parent: null,
filterIndex: 0,
displayDirectory: null,
get file() {
if (MockFilePicker.returnFiles.length >= 1 &&
// window.File does not implement nsIFile
MockFilePicker.isNsIFile(MockFilePicker.returnFiles[0])) {
return MockFilePicker.returnFiles[0];
}
return null;
},
// We don't support directories here.
get domFileOrDirectory() {
if (MockFilePicker.returnFiles.length >= 1) {
// window.File does not implement nsIFile
if (!MockFilePicker.isNsIFile(MockFilePicker.returnFiles[0])) {
return MockFilePicker.returnFiles[0];
}
let utils = this.parent.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return utils.wrapDOMFile(MockFilePicker.returnFiles[0]);
}
return null;
},
get fileURL() {
if (MockFilePicker.returnFiles.length >= 1 &&
// window.File does not implement nsIFile
MockFilePicker.isNsIFile(MockFilePicker.returnFiles[0])) {
return Services.io.newFileURI(MockFilePicker.returnFiles[0]);
}
return null;
},
get files() {
return {
index: 0,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]),
hasMoreElements: function() {
return this.index < MockFilePicker.returnFiles.length;
},
getNext: function() {
// window.File does not implement nsIFile
if (!MockFilePicker.isNsIFile(MockFilePicker.returnFiles[this.index])) {
return null;
}
return MockFilePicker.returnFiles[this.index++];
}
};
},
get domFileOrDirectoryEnumerator() {
let utils = this.parent.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
return {
index: 0,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]),
hasMoreElements: function() {
return this.index < MockFilePicker.returnFiles.length;
},
getNext: function() {
// window.File does not implement nsIFile
if (!MockFilePicker.isNsIFile(MockFilePicker.returnFiles[this.index])) {
return MockFilePicker.returnFiles[this.index++];
}
return utils.wrapDOMFile(MockFilePicker.returnFiles[this.index++]);
}
};
},
show: function() {
MockFilePicker.displayDirectory = this.displayDirectory;
MockFilePicker.shown = true;
if (typeof MockFilePicker.showCallback == "function") {
var returnValue = MockFilePicker.showCallback(this);
if (typeof returnValue != "undefined")
return returnValue;
}
return MockFilePicker.returnValue;
},
open: function(aFilePickerShownCallback) {
MockFilePicker.showing = true;
this.window.setTimeout(function() {
let result = Components.interfaces.nsIFilePicker.returnCancel;
try {
result = this.show();
} catch(ex) {
}
if (aFilePickerShownCallback) {
aFilePickerShownCallback.done(result);
}
}.bind(this), 0);
}
};

View file

@ -0,0 +1,97 @@
/* 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/. */
this.EXPORTED_SYMBOLS = ["MockPermissionPrompt"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cm = Components.manager;
const Cu = Components.utils;
const CONTRACT_ID = "@mozilla.org/content-permission/prompt;1";
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
var oldClassID, oldFactory;
var newClassID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID();
var newFactory = {
createInstance: function(aOuter, aIID) {
if (aOuter)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return new MockPermissionPromptInstance().QueryInterface(aIID);
},
lockFactory: function(aLock) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
};
this.MockPermissionPrompt = {
init: function() {
this.reset();
if (!registrar.isCIDRegistered(newClassID)) {
try {
oldClassID = registrar.contractIDToCID(CONTRACT_ID);
oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
} catch (ex) {
oldClassID = "";
oldFactory = null;
dump("TEST-INFO | can't get permission prompt registered component, " +
"assuming there is none");
}
if (oldFactory) {
registrar.unregisterFactory(oldClassID, oldFactory);
}
registrar.registerFactory(newClassID, "", CONTRACT_ID, newFactory);
}
},
reset: function() {
},
cleanup: function() {
this.reset();
if (oldFactory) {
registrar.unregisterFactory(newClassID, newFactory);
registrar.registerFactory(oldClassID, "", CONTRACT_ID, oldFactory);
}
},
};
function MockPermissionPromptInstance() { };
MockPermissionPromptInstance.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
promptResult: Ci.nsIPermissionManager.UNKNOWN_ACTION,
prompt: function(request) {
let perms = request.types.QueryInterface(Ci.nsIArray);
for (let idx = 0; idx < perms.length; idx++) {
let perm = perms.queryElementAt(idx, Ci.nsIContentPermissionType);
if (Services.perms.testExactPermissionFromPrincipal(
request.principal, perm.type) != Ci.nsIPermissionManager.ALLOW_ACTION) {
request.cancel();
return;
}
}
request.allow();
}
};
// Expose everything to content. We call reset() here so that all of the relevant
// lazy expandos get added.
MockPermissionPrompt.reset();
function exposeAll(obj) {
var props = {};
for (var prop in obj)
props[prop] = 'rw';
obj.__exposedProps__ = props;
}
exposeAll(MockPermissionPrompt);
exposeAll(MockPermissionPromptInstance.prototype);

View file

@ -0,0 +1,134 @@
/**
* MozillaLogger, a base class logger that just logs to stdout.
*/
"use strict";
function MozillaLogger(aPath) {
}
function formatLogMessage(msg) {
return msg.info.join(' ') + "\n";
}
MozillaLogger.prototype = {
init : function(path) {},
getLogCallback : function() {
return function (msg) {
var data = formatLogMessage(msg);
dump(data);
};
},
log : function(msg) {
dump(msg);
},
close : function() {}
};
/**
* SpecialPowersLogger, inherits from MozillaLogger and utilizes SpecialPowers.
* intented to be used in content scripts to write to a file
*/
function SpecialPowersLogger(aPath) {
// Call the base constructor
MozillaLogger.call(this);
this.prototype = new MozillaLogger(aPath);
this.init(aPath);
}
SpecialPowersLogger.prototype = {
init : function (path) {
SpecialPowers.setLogFile(path);
},
getLogCallback : function () {
return function (msg) {
var data = formatLogMessage(msg);
SpecialPowers.log(data);
if (data.indexOf("SimpleTest FINISH") >= 0) {
SpecialPowers.closeLogFile();
}
};
},
log : function (msg) {
SpecialPowers.log(msg);
},
close : function () {
SpecialPowers.closeLogFile();
}
};
/**
* MozillaFileLogger, a log listener that can write to a local file.
* intended to be run from chrome space
*/
/** Init the file logger with the absolute path to the file.
It will create and append if the file already exists **/
function MozillaFileLogger(aPath) {
// Call the base constructor
MozillaLogger.call(this);
this.prototype = new MozillaLogger(aPath);
this.init(aPath);
}
MozillaFileLogger.prototype = {
init : function (path) {
var PR_WRITE_ONLY = 0x02; // Open for writing only.
var PR_CREATE_FILE = 0x08;
var PR_APPEND = 0x10;
this._file = Components.classes["@mozilla.org/file/local;1"].
createInstance(Components.interfaces.nsILocalFile);
this._file.initWithPath(path);
this._foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
this._foStream.init(this._file, PR_WRITE_ONLY | PR_CREATE_FILE | PR_APPEND,
436 /* 0664 */, 0);
this._converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"].
createInstance(Components.interfaces.nsIConverterOutputStream);
this._converter.init(this._foStream, "UTF-8", 0, 0);
},
getLogCallback : function() {
return function (msg) {
var data = formatLogMessage(msg);
if (MozillaFileLogger._converter) {
this._converter.writeString(data);
}
if (data.indexOf("SimpleTest FINISH") >= 0) {
MozillaFileLogger.close();
}
};
},
log : function(msg) {
if (this._converter) {
this._converter.writeString(msg);
}
},
close : function() {
if (this._converter) {
this._converter.flush();
this._converter.close();
}
this._foStream = null;
this._converter = null;
this._file = null;
}
};
this.MozillaLogger = MozillaLogger;
this.SpecialPowersLogger = SpecialPowersLogger;
this.MozillaFileLogger = MozillaFileLogger;

View file

@ -0,0 +1,313 @@
/* 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/. */
// Based on:
// https://bugzilla.mozilla.org/show_bug.cgi?id=549539
// https://bug549539.bugzilla.mozilla.org/attachment.cgi?id=429661
// https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_1.9.3
// https://developer.mozilla.org/en/how_to_build_an_xpcom_component_in_javascript
var EXPORTED_SYMBOLS = ["SpecialPowersObserver", "SpecialPowersObserverFactory"];
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.importGlobalProperties(['File']);
if (typeof(Cc) == "undefined") {
const Cc = Components.classes;
const Ci = Components.interfaces;
}
const CHILD_SCRIPT = "chrome://specialpowers/content/specialpowers.js"
const CHILD_SCRIPT_API = "chrome://specialpowers/content/specialpowersAPI.js"
const CHILD_LOGGER_SCRIPT = "chrome://specialpowers/content/MozillaLogger.js"
// Glue to add in the observer API to this object. This allows us to share code with chrome tests
var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserverAPI.js");
/* XPCOM gunk */
this.SpecialPowersObserver = function SpecialPowersObserver() {
this._isFrameScriptLoaded = false;
this._messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
getService(Ci.nsIMessageBroadcaster);
}
SpecialPowersObserver.prototype = new SpecialPowersObserverAPI();
SpecialPowersObserver.prototype.classDescription = "Special powers Observer for use in testing.";
SpecialPowersObserver.prototype.classID = Components.ID("{59a52458-13e0-4d93-9d85-a637344f29a1}");
SpecialPowersObserver.prototype.contractID = "@mozilla.org/special-powers-observer;1";
SpecialPowersObserver.prototype.QueryInterface = XPCOMUtils.generateQI([Components.interfaces.nsIObserver]);
SpecialPowersObserver.prototype.observe = function(aSubject, aTopic, aData)
{
switch (aTopic) {
case "chrome-document-global-created":
this._loadFrameScript();
break;
case "http-on-modify-request":
if (aSubject instanceof Ci.nsIChannel) {
let uri = aSubject.URI.spec;
this._sendAsyncMessage("specialpowers-http-notify-request", { uri: uri });
}
break;
default:
this._observe(aSubject, aTopic, aData);
break;
}
};
SpecialPowersObserver.prototype._loadFrameScript = function()
{
if (!this._isFrameScriptLoaded) {
// Register for any messages our API needs us to handle
this._messageManager.addMessageListener("SPPrefService", this);
this._messageManager.addMessageListener("SPProcessCrashService", this);
this._messageManager.addMessageListener("SPPingService", this);
this._messageManager.addMessageListener("SpecialPowers.Quit", this);
this._messageManager.addMessageListener("SpecialPowers.Focus", this);
this._messageManager.addMessageListener("SpecialPowers.CreateFiles", this);
this._messageManager.addMessageListener("SpecialPowers.RemoveFiles", this);
this._messageManager.addMessageListener("SPPermissionManager", this);
this._messageManager.addMessageListener("SPObserverService", this);
this._messageManager.addMessageListener("SPLoadChromeScript", this);
this._messageManager.addMessageListener("SPImportInMainProcess", this);
this._messageManager.addMessageListener("SPChromeScriptMessage", this);
this._messageManager.addMessageListener("SPQuotaManager", this);
this._messageManager.addMessageListener("SPSetTestPluginEnabledState", this);
this._messageManager.addMessageListener("SPLoadExtension", this);
this._messageManager.addMessageListener("SPStartupExtension", this);
this._messageManager.addMessageListener("SPUnloadExtension", this);
this._messageManager.addMessageListener("SPExtensionMessage", this);
this._messageManager.addMessageListener("SPCleanUpSTSData", this);
this._messageManager.addMessageListener("SPClearAppPrivateData", this);
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;
}
};
SpecialPowersObserver.prototype._sendAsyncMessage = function(msgname, msg)
{
this._messageManager.broadcastAsyncMessage(msgname, msg);
};
SpecialPowersObserver.prototype._receiveMessage = function(aMessage) {
return this._receiveMessageAPI(aMessage);
};
SpecialPowersObserver.prototype.init = function()
{
var obs = Services.obs;
obs.addObserver(this, "chrome-document-global-created", false);
// Register special testing modules.
var testsURI = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties).
get("ProfD", Ci.nsILocalFile);
testsURI.append("tests.manifest");
var ioSvc = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var manifestFile = ioSvc.newFileURI(testsURI).
QueryInterface(Ci.nsIFileURL).file;
Components.manager.QueryInterface(Ci.nsIComponentRegistrar).
autoRegister(manifestFile);
obs.addObserver(this, "http-on-modify-request", false);
this._loadFrameScript();
};
SpecialPowersObserver.prototype.uninit = function()
{
var obs = Services.obs;
obs.removeObserver(this, "chrome-document-global-created");
obs.removeObserver(this, "http-on-modify-request");
this._registerObservers._topics.forEach(function(element) {
obs.removeObserver(this._registerObservers, element);
});
this._removeProcessCrashObservers();
if (this._isFrameScriptLoaded) {
this._messageManager.removeMessageListener("SPPrefService", this);
this._messageManager.removeMessageListener("SPProcessCrashService", this);
this._messageManager.removeMessageListener("SPPingService", this);
this._messageManager.removeMessageListener("SpecialPowers.Quit", this);
this._messageManager.removeMessageListener("SpecialPowers.Focus", this);
this._messageManager.removeMessageListener("SpecialPowers.CreateFiles", this);
this._messageManager.removeMessageListener("SpecialPowers.RemoveFiles", this);
this._messageManager.removeMessageListener("SPPermissionManager", this);
this._messageManager.removeMessageListener("SPObserverService", this);
this._messageManager.removeMessageListener("SPLoadChromeScript", this);
this._messageManager.removeMessageListener("SPImportInMainProcess", this);
this._messageManager.removeMessageListener("SPChromeScriptMessage", this);
this._messageManager.removeMessageListener("SPQuotaManager", this);
this._messageManager.removeMessageListener("SPSetTestPluginEnabledState", this);
this._messageManager.removeMessageListener("SPLoadExtension", this);
this._messageManager.removeMessageListener("SPStartupExtension", this);
this._messageManager.removeMessageListener("SPUnloadExtension", this);
this._messageManager.removeMessageListener("SPExtensionMessage", this);
this._messageManager.removeMessageListener("SPCleanUpSTSData", this);
this._messageManager.removeMessageListener("SPClearAppPrivateData", this);
this._messageManager.removeDelayedFrameScript(CHILD_LOGGER_SCRIPT);
this._messageManager.removeDelayedFrameScript(CHILD_SCRIPT_API);
this._messageManager.removeDelayedFrameScript(CHILD_SCRIPT);
this._isFrameScriptLoaded = false;
}
};
SpecialPowersObserver.prototype._addProcessCrashObservers = function() {
if (this._processCrashObserversRegistered) {
return;
}
var obs = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
obs.addObserver(this, "plugin-crashed", false);
obs.addObserver(this, "ipc:content-shutdown", false);
this._processCrashObserversRegistered = true;
};
SpecialPowersObserver.prototype._removeProcessCrashObservers = function() {
if (!this._processCrashObserversRegistered) {
return;
}
var obs = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
obs.removeObserver(this, "plugin-crashed");
obs.removeObserver(this, "ipc:content-shutdown");
this._processCrashObserversRegistered = false;
};
SpecialPowersObserver.prototype._registerObservers = {
_self: null,
_topics: [],
_add: function(topic) {
if (this._topics.indexOf(topic) < 0) {
this._topics.push(topic);
Services.obs.addObserver(this, topic, false);
}
},
observe: function (aSubject, aTopic, aData) {
var msg = { aData: aData };
switch (aTopic) {
case "perm-changed":
var permission = aSubject.QueryInterface(Ci.nsIPermission);
// specialPowersAPI will consume this value, and it is used as a
// fake permission, but only type and principal.appId will be used.
//
// We need to ensure that it looks the same as a real permission,
// so we fake these properties.
msg.permission = {
principal: {
originAttributes: {appId: permission.principal.appId}
},
type: permission.type
};
default:
this._self._sendAsyncMessage("specialpowers-" + aTopic, msg);
}
}
};
/**
* messageManager callback function
* This will get requests from our API in the window and process them in chrome for it
**/
SpecialPowersObserver.prototype.receiveMessage = function(aMessage) {
switch(aMessage.name) {
case "SPPingService":
if (aMessage.json.op == "ping") {
aMessage.target
.QueryInterface(Ci.nsIFrameLoaderOwner)
.frameLoader
.messageManager
.sendAsyncMessage("SPPingService", { op: "pong" });
}
break;
case "SpecialPowers.Quit":
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
appStartup.quit(Ci.nsIAppStartup.eForceQuit);
break;
case "SpecialPowers.Focus":
aMessage.target.focus();
break;
case "SpecialPowers.CreateFiles":
let filePaths = new Array;
if (!this._createdFiles) {
this._createdFiles = new Array;
}
let createdFiles = this._createdFiles;
try {
aMessage.data.forEach(function(request) {
const filePerms = 0666;
let testFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
if (request.name) {
testFile.appendRelativePath(request.name);
} else {
testFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, filePerms);
}
let outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
outStream.init(testFile, 0x02 | 0x08 | 0x20, // PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE
filePerms, 0);
if (request.data) {
outStream.write(request.data, request.data.length);
}
outStream.close();
filePaths.push(File.createFromFileName(testFile.path, request.options));
createdFiles.push(testFile);
});
aMessage.target
.QueryInterface(Ci.nsIFrameLoaderOwner)
.frameLoader
.messageManager
.sendAsyncMessage("SpecialPowers.FilesCreated", filePaths);
} catch (e) {
aMessage.target
.QueryInterface(Ci.nsIFrameLoaderOwner)
.frameLoader
.messageManager
.sendAsyncMessage("SpecialPowers.FilesError", e.toString());
}
break;
case "SpecialPowers.RemoveFiles":
if (this._createdFiles) {
this._createdFiles.forEach(function (testFile) {
try {
testFile.remove(false);
} catch (e) {}
});
this._createdFiles = null;
}
break;
default:
return this._receiveMessage(aMessage);
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SpecialPowersObserver]);
this.SpecialPowersObserverFactory = Object.freeze({
createInstance: function(outer, id) {
if (outer) { throw Components.results.NS_ERROR_NO_AGGREGATION };
return new SpecialPowersObserver();
},
loadFactory: function(lock){},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
});

View file

@ -0,0 +1,635 @@
/* 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";
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");
if (typeof(Ci) == 'undefined') {
var Ci = Components.interfaces;
}
if (typeof(Cc) == 'undefined') {
var Cc = Components.classes;
}
this.SpecialPowersError = function(aMsg) {
Error.call(this);
let {stack} = new Error();
this.message = aMsg;
this.name = "SpecialPowersError";
}
SpecialPowersError.prototype = Object.create(Error.prototype);
SpecialPowersError.prototype.toString = function() {
return `${this.name}: ${this.message}`;
};
this.SpecialPowersObserverAPI = function SpecialPowersObserverAPI() {
this._crashDumpDir = null;
this._processCrashObserversRegistered = false;
this._chromeScriptListeners = [];
this._extensions = new Map();
}
function parseKeyValuePairs(text) {
var lines = text.split('\n');
var data = {};
for (let i = 0; i < lines.length; i++) {
if (lines[i] == '')
continue;
// can't just .split() because the value might contain = characters
let eq = lines[i].indexOf('=');
if (eq != -1) {
let [key, value] = [lines[i].substring(0, eq),
lines[i].substring(eq + 1)];
if (key && value)
data[key] = value.replace(/\\n/g, "\n").replace(/\\\\/g, "\\");
}
}
return data;
}
function parseKeyValuePairsFromFile(file) {
var fstream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
fstream.init(file, -1, 0, 0);
var is = Cc["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Ci.nsIConverterInputStream);
is.init(fstream, "UTF-8", 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
var str = {};
var contents = '';
while (is.readString(4096, str) != 0) {
contents += str.value;
}
is.close();
fstream.close();
return parseKeyValuePairs(contents);
}
function getTestPlugin(pluginName) {
var ph = Cc["@mozilla.org/plugin/host;1"]
.getService(Ci.nsIPluginHost);
var tags = ph.getPluginTags();
var name = pluginName || "Test Plug-in";
for (var tag of tags) {
if (tag.name == name) {
return tag;
}
}
return null;
}
SpecialPowersObserverAPI.prototype = {
_observe: function(aSubject, aTopic, aData) {
function addDumpIDToMessage(propertyName) {
try {
var id = aSubject.getPropertyAsAString(propertyName);
} catch(ex) {
var id = null;
}
if (id) {
message.dumpIDs.push({id: id, extension: "dmp"});
message.dumpIDs.push({id: id, extension: "extra"});
}
}
switch(aTopic) {
case "plugin-crashed":
case "ipc:content-shutdown":
var message = { type: "crash-observed", dumpIDs: [] };
aSubject = aSubject.QueryInterface(Ci.nsIPropertyBag2);
if (aTopic == "plugin-crashed") {
addDumpIDToMessage("pluginDumpID");
addDumpIDToMessage("browserDumpID");
let pluginID = aSubject.getPropertyAsAString("pluginDumpID");
let extra = this._getExtraData(pluginID);
if (extra && ("additional_minidumps" in extra)) {
let dumpNames = extra.additional_minidumps.split(',');
for (let name of dumpNames) {
message.dumpIDs.push({id: pluginID + "-" + name, extension: "dmp"});
}
}
} else { // ipc:content-shutdown
addDumpIDToMessage("dumpID");
}
this._sendAsyncMessage("SPProcessCrashService", message);
break;
}
},
_getCrashDumpDir: function() {
if (!this._crashDumpDir) {
this._crashDumpDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
this._crashDumpDir.append("minidumps");
}
return this._crashDumpDir;
},
_getExtraData: function(dumpId) {
let extraFile = this._getCrashDumpDir().clone();
extraFile.append(dumpId + ".extra");
if (!extraFile.exists()) {
return null;
}
return parseKeyValuePairsFromFile(extraFile);
},
_deleteCrashDumpFiles: function(aFilenames) {
var crashDumpDir = this._getCrashDumpDir();
if (!crashDumpDir.exists()) {
return false;
}
var success = aFilenames.length != 0;
aFilenames.forEach(function(crashFilename) {
var file = crashDumpDir.clone();
file.append(crashFilename);
if (file.exists()) {
file.remove(false);
} else {
success = false;
}
});
return success;
},
_findCrashDumpFiles: function(aToIgnore) {
var crashDumpDir = this._getCrashDumpDir();
var entries = crashDumpDir.exists() && crashDumpDir.directoryEntries;
if (!entries) {
return [];
}
var crashDumpFiles = [];
while (entries.hasMoreElements()) {
var file = entries.getNext().QueryInterface(Ci.nsIFile);
var path = String(file.path);
if (path.match(/\.(dmp|extra)$/) && !aToIgnore[path]) {
crashDumpFiles.push(path);
}
}
return crashDumpFiles.concat();
},
_getURI: function (url) {
return Services.io.newURI(url, null, null);
},
_readUrlAsString: function(aUrl) {
// Fetch script content as we can't use scriptloader's loadSubScript
// to evaluate http:// urls...
var scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
.getService(Ci.nsIScriptableInputStream);
var channel = NetUtil.newChannel({
uri: aUrl,
loadUsingSystemPrincipal: true
});
var input = channel.open2();
scriptableStream.init(input);
var str;
var buffer = [];
while ((str = scriptableStream.read(4096))) {
buffer.push(str);
}
var output = buffer.join("");
scriptableStream.close();
input.close();
var status;
try {
channel.QueryInterface(Ci.nsIHttpChannel);
status = channel.responseStatus;
} catch(e) {
/* The channel is not a nsIHttpCHannel, but that's fine */
dump("-*- _readUrlAsString: Got an error while fetching " +
"chrome script '" + aUrl + "': (" + e.name + ") " + e.message + ". " +
"Ignoring.\n");
}
if (status == 404) {
throw new SpecialPowersError(
"Error while executing chrome script '" + aUrl + "':\n" +
"The script doesn't exists. Ensure you have registered it in " +
"'support-files' in your mochitest.ini.");
}
return output;
},
_sendReply: function(aMessage, aReplyName, aReplyMsg) {
let mm = aMessage.target
.QueryInterface(Ci.nsIFrameLoaderOwner)
.frameLoader
.messageManager;
mm.sendAsyncMessage(aReplyName, aReplyMsg);
},
_notifyCategoryAndObservers: function(subject, topic, data) {
const serviceMarker = "service,";
// First create observers from the category manager.
let cm =
Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
let enumerator = cm.enumerateCategory(topic);
let observers = [];
while (enumerator.hasMoreElements()) {
let entry =
enumerator.getNext().QueryInterface(Ci.nsISupportsCString).data;
let contractID = cm.getCategoryEntry(topic, entry);
let factoryFunction;
if (contractID.substring(0, serviceMarker.length) == serviceMarker) {
contractID = contractID.substring(serviceMarker.length);
factoryFunction = "getService";
}
else {
factoryFunction = "createInstance";
}
try {
let handler = Cc[contractID][factoryFunction]();
if (handler) {
let observer = handler.QueryInterface(Ci.nsIObserver);
observers.push(observer);
}
} catch(e) { }
}
// Next enumerate the registered observers.
enumerator = Services.obs.enumerateObservers(topic);
while (enumerator.hasMoreElements()) {
try {
let observer = enumerator.getNext().QueryInterface(Ci.nsIObserver);
if (observers.indexOf(observer) == -1) {
observers.push(observer);
}
} catch (e) { }
}
observers.forEach(function (observer) {
try {
observer.observe(subject, topic, data);
} catch(e) { }
});
},
/**
* messageManager callback function
* This will get requests from our API in the window and process them in chrome for it
**/
_receiveMessageAPI: function(aMessage) {
// We explicitly return values in the below code so that this function
// doesn't trigger a flurry of warnings about "does not always return
// a value".
switch(aMessage.name) {
case "SPPrefService": {
let prefs = Services.prefs;
let prefType = aMessage.json.prefType.toUpperCase();
let prefName = aMessage.json.prefName;
let prefValue = "prefValue" in aMessage.json ? aMessage.json.prefValue : null;
if (aMessage.json.op == "get") {
if (!prefName || !prefType)
throw new SpecialPowersError("Invalid parameters for get in SPPrefService");
// return null if the pref doesn't exist
if (prefs.getPrefType(prefName) == prefs.PREF_INVALID)
return null;
} else if (aMessage.json.op == "set") {
if (!prefName || !prefType || prefValue === null)
throw new SpecialPowersError("Invalid parameters for set in SPPrefService");
} else if (aMessage.json.op == "clear") {
if (!prefName)
throw new SpecialPowersError("Invalid parameters for clear in SPPrefService");
} else {
throw new SpecialPowersError("Invalid operation for SPPrefService");
}
// Now we make the call
switch(prefType) {
case "BOOL":
if (aMessage.json.op == "get")
return(prefs.getBoolPref(prefName));
else
return(prefs.setBoolPref(prefName, prefValue));
case "INT":
if (aMessage.json.op == "get")
return(prefs.getIntPref(prefName));
else
return(prefs.setIntPref(prefName, prefValue));
case "CHAR":
if (aMessage.json.op == "get")
return(prefs.getCharPref(prefName));
else
return(prefs.setCharPref(prefName, prefValue));
case "COMPLEX":
if (aMessage.json.op == "get")
return(prefs.getComplexValue(prefName, prefValue[0]));
else
return(prefs.setComplexValue(prefName, prefValue[0], prefValue[1]));
case "":
if (aMessage.json.op == "clear") {
prefs.clearUserPref(prefName);
return undefined;
}
}
return undefined; // See comment at the beginning of this function.
}
case "SPProcessCrashService": {
switch (aMessage.json.op) {
case "register-observer":
this._addProcessCrashObservers();
break;
case "unregister-observer":
this._removeProcessCrashObservers();
break;
case "delete-crash-dump-files":
return this._deleteCrashDumpFiles(aMessage.json.filenames);
case "find-crash-dump-files":
return this._findCrashDumpFiles(aMessage.json.crashDumpFilesToIgnore);
default:
throw new SpecialPowersError("Invalid operation for SPProcessCrashService");
}
return undefined; // See comment at the beginning of this function.
}
case "SPPermissionManager": {
let msg = aMessage.json;
let principal = msg.principal;
switch (msg.op) {
case "add":
Services.perms.addFromPrincipal(principal, msg.type, msg.permission, msg.expireType, msg.expireTime);
break;
case "remove":
Services.perms.removeFromPrincipal(principal, msg.type);
break;
case "has":
let hasPerm = Services.perms.testPermissionFromPrincipal(principal, msg.type);
return hasPerm == Ci.nsIPermissionManager.ALLOW_ACTION;
case "test":
let testPerm = Services.perms.testPermissionFromPrincipal(principal, msg.type, msg.value);
return testPerm == msg.value;
default:
throw new SpecialPowersError(
"Invalid operation for SPPermissionManager");
}
return undefined; // See comment at the beginning of this function.
}
case "SPSetTestPluginEnabledState": {
var plugin = getTestPlugin(aMessage.data.pluginName);
if (!plugin) {
return undefined;
}
var oldEnabledState = plugin.enabledState;
plugin.enabledState = aMessage.data.newEnabledState;
return oldEnabledState;
}
case "SPObserverService": {
let topic = aMessage.json.observerTopic;
switch (aMessage.json.op) {
case "notify":
let data = aMessage.json.observerData
Services.obs.notifyObservers(null, topic, data);
break;
case "add":
this._registerObservers._self = this;
this._registerObservers._add(topic);
break;
default:
throw new SpecialPowersError("Invalid operation for SPObserverervice");
}
return undefined; // See comment at the beginning of this function.
}
case "SPLoadChromeScript": {
let id = aMessage.json.id;
let jsScript;
let scriptName;
if (aMessage.json.url) {
jsScript = this._readUrlAsString(aMessage.json.url);
scriptName = aMessage.json.url;
} else if (aMessage.json.function) {
jsScript = aMessage.json.function.body;
scriptName = aMessage.json.function.name
|| "<loadChromeScript anonymous function>";
} else {
throw new SpecialPowersError("SPLoadChromeScript: Invalid script");
}
// Setup a chrome sandbox that has access to sendAsyncMessage
// and addMessageListener in order to communicate with
// the mochitest.
let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
let sb = Components.utils.Sandbox(systemPrincipal);
let mm = aMessage.target
.QueryInterface(Ci.nsIFrameLoaderOwner)
.frameLoader
.messageManager;
sb.sendAsyncMessage = (name, message) => {
mm.sendAsyncMessage("SPChromeScriptMessage",
{ id: id, name: name, message: message });
};
sb.addMessageListener = (name, listener) => {
this._chromeScriptListeners.push({ id: id, name: name, listener: listener });
};
sb.browserElement = aMessage.target;
// Also expose assertion functions
let reporter = function (err, message, stack) {
// Pipe assertions back to parent process
mm.sendAsyncMessage("SPChromeScriptAssert",
{ id, name: scriptName, err, message,
stack });
};
Object.defineProperty(sb, "assert", {
get: function () {
let scope = Components.utils.createObjectIn(sb);
Services.scriptloader.loadSubScript("chrome://specialpowers/content/Assert.jsm",
scope);
let assert = new scope.Assert(reporter);
delete sb.assert;
return sb.assert = assert;
},
configurable: true
});
// Evaluate the chrome script
try {
Components.utils.evalInSandbox(jsScript, sb, "1.8", scriptName, 1);
} catch(e) {
throw new SpecialPowersError(
"Error while executing chrome script '" + scriptName + "':\n" +
e + "\n" +
e.fileName + ":" + e.lineNumber);
}
return undefined; // See comment at the beginning of this function.
}
case "SPChromeScriptMessage": {
let id = aMessage.json.id;
let name = aMessage.json.name;
let message = aMessage.json.message;
return this._chromeScriptListeners
.filter(o => (o.name == name && o.id == id))
.map(o => o.listener(message));
}
case "SPImportInMainProcess": {
var message = { hadError: false, errorMessage: null };
try {
Components.utils.import(aMessage.data);
} catch (e) {
message.hadError = true;
message.errorMessage = e.toString();
}
return message;
}
case "SPCleanUpSTSData": {
let origin = aMessage.data.origin;
let flags = aMessage.data.flags;
let uri = Services.io.newURI(origin, null, null);
let sss = Cc["@mozilla.org/ssservice;1"].
getService(Ci.nsISiteSecurityService);
sss.removeState(Ci.nsISiteSecurityService.HEADER_HSTS, uri, flags);
return undefined;
}
case "SPLoadExtension": {
let {Extension} = Components.utils.import("resource://gre/modules/Extension.jsm", {});
let id = aMessage.data.id;
let ext = aMessage.data.ext;
let extension = Extension.generate(ext);
let resultListener = (...args) => {
this._sendReply(aMessage, "SPExtensionMessage", {id, type: "testResult", args});
};
let messageListener = (...args) => {
args.shift();
this._sendReply(aMessage, "SPExtensionMessage", {id, type: "testMessage", args});
};
// Register pass/fail handlers.
extension.on("test-result", resultListener);
extension.on("test-eq", resultListener);
extension.on("test-log", resultListener);
extension.on("test-done", resultListener);
extension.on("test-message", messageListener);
this._extensions.set(id, extension);
return undefined;
}
case "SPStartupExtension": {
let {ExtensionData, Management} = Components.utils.import("resource://gre/modules/Extension.jsm", {});
let id = aMessage.data.id;
let extension = this._extensions.get(id);
let startupListener = (msg, ext) => {
if (ext == extension) {
this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionSetId", args: [extension.id]});
Management.off("startup", startupListener);
}
};
Management.on("startup", startupListener);
// Make sure the extension passes the packaging checks when
// they're run on a bare archive rather than a running instance,
// as the add-on manager runs them.
let extensionData = new ExtensionData(extension.rootURI);
extensionData.readManifest().then(
() => {
return extensionData.initAllLocales().then(() => {
if (extensionData.errors.length) {
return Promise.reject("Extension contains packaging errors");
}
});
},
() => {
// readManifest() will throw if we're loading an embedded
// extension, so don't worry about locale errors in that
// case.
}
).then(() => {
return extension.startup();
}).then(() => {
this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionStarted", args: []});
}).catch(e => {
dump(`Extension startup failed: ${e}\n${e.stack}`);
Management.off("startup", startupListener);
this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionFailed", args: []});
});
return undefined;
}
case "SPExtensionMessage": {
let id = aMessage.data.id;
let extension = this._extensions.get(id);
extension.testMessage(...aMessage.data.args);
return undefined;
}
case "SPUnloadExtension": {
let id = aMessage.data.id;
let extension = this._extensions.get(id);
this._extensions.delete(id);
extension.shutdown();
this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionUnloaded", args: []});
return undefined;
}
case "SPClearAppPrivateData": {
let appId = aMessage.data.appId;
let browserOnly = aMessage.data.browserOnly;
let attributes = { appId: appId };
if (browserOnly) {
attributes.inIsolatedMozBrowser = true;
}
this._notifyCategoryAndObservers(null,
"clear-origin-attributes-data",
JSON.stringify(attributes));
let subject = {
appId: appId,
browserOnly: browserOnly,
QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams])
};
this._notifyCategoryAndObservers(subject, "webapps-clear-data", null);
return undefined;
}
default:
throw new SpecialPowersError("Unrecognized Special Powers API");
}
// We throw an exception before reaching this explicit return because
// we should never be arriving here anyway.
throw new SpecialPowersError("Unreached code");
return undefined;
}
};

View file

@ -0,0 +1,278 @@
/* 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/. */
/* This code is loaded in every child process that is started by mochitest in
* order to be used as a replacement for UniversalXPConnect
*/
function SpecialPowers(window) {
this.window = Components.utils.getWeakReference(window);
this._windowID = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils)
.currentInnerWindowID;
this._encounteredCrashDumpFiles = [];
this._unexpectedCrashDumpFiles = { };
this._crashDumpDir = null;
this.DOMWindowUtils = bindDOMWindowUtils(window);
Object.defineProperty(this, 'Components', {
configurable: true, enumerable: true, get: function() {
var win = this.window.get();
if (!win)
return null;
return getRawComponents(win);
}});
this._pongHandlers = [];
this._messageListener = this._messageReceived.bind(this);
this._grandChildFrameMM = null;
this._createFilesOnError = null;
this._createFilesOnSuccess = null;
this.SP_SYNC_MESSAGES = ["SPChromeScriptMessage",
"SPLoadChromeScript",
"SPImportInMainProcess",
"SPObserverService",
"SPPermissionManager",
"SPPrefService",
"SPProcessCrashService",
"SPSetTestPluginEnabledState",
"SPCleanUpSTSData"];
this.SP_ASYNC_MESSAGES = ["SpecialPowers.Focus",
"SpecialPowers.Quit",
"SpecialPowers.CreateFiles",
"SpecialPowers.RemoveFiles",
"SPPingService",
"SPLoadExtension",
"SPStartupExtension",
"SPUnloadExtension",
"SPExtensionMessage",
"SPClearAppPrivateData"];
addMessageListener("SPPingService", this._messageListener);
addMessageListener("SpecialPowers.FilesCreated", this._messageListener);
addMessageListener("SpecialPowers.FilesError", this._messageListener);
let self = this;
Services.obs.addObserver(function onInnerWindowDestroyed(subject, topic, data) {
var id = subject.QueryInterface(Components.interfaces.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);
} catch (e if e.result == Components.results.NS_ERROR_ILLEGAL_VALUE) {
// Ignore the exception which the message manager has been destroyed.
;
}
}
}, "inner-window-destroyed", false);
}
SpecialPowers.prototype = new SpecialPowersAPI();
SpecialPowers.prototype.toString = function() { return "[SpecialPowers]"; };
SpecialPowers.prototype.sanityCheck = function() { return "foo"; };
// This gets filled in in the constructor.
SpecialPowers.prototype.DOMWindowUtils = undefined;
SpecialPowers.prototype.Components = undefined;
SpecialPowers.prototype.IsInNestedFrame = false;
SpecialPowers.prototype._sendSyncMessage = function(msgname, msg) {
if (this.SP_SYNC_MESSAGES.indexOf(msgname) == -1) {
dump("TEST-INFO | specialpowers.js | Unexpected SP message: " + msgname + "\n");
}
return sendSyncMessage(msgname, msg);
};
SpecialPowers.prototype._sendAsyncMessage = function(msgname, msg) {
if (this.SP_ASYNC_MESSAGES.indexOf(msgname) == -1) {
dump("TEST-INFO | specialpowers.js | Unexpected SP message: " + msgname + "\n");
}
sendAsyncMessage(msgname, msg);
};
SpecialPowers.prototype._addMessageListener = function(msgname, listener) {
addMessageListener(msgname, listener);
sendAsyncMessage("SPPAddNestedMessageListener", { name: msgname });
};
SpecialPowers.prototype._removeMessageListener = function(msgname, listener) {
removeMessageListener(msgname, listener);
};
SpecialPowers.prototype.registerProcessCrashObservers = function() {
addMessageListener("SPProcessCrashService", this._messageListener);
sendSyncMessage("SPProcessCrashService", { op: "register-observer" });
};
SpecialPowers.prototype.unregisterProcessCrashObservers = function() {
removeMessageListener("SPProcessCrashService", this._messageListener);
sendSyncMessage("SPProcessCrashService", { op: "unregister-observer" });
};
SpecialPowers.prototype._messageReceived = function(aMessage) {
switch (aMessage.name) {
case "SPProcessCrashService":
if (aMessage.json.type == "crash-observed") {
for (let e of aMessage.json.dumpIDs) {
this._encounteredCrashDumpFiles.push(e.id + "." + e.extension);
}
}
break;
case "SPPingService":
if (aMessage.json.op == "pong") {
var handler = this._pongHandlers.shift();
if (handler) {
handler();
}
if (this._grandChildFrameMM) {
this._grandChildFrameMM.sendAsyncMessage("SPPingService", { op: "pong" });
}
}
break;
case "SpecialPowers.FilesCreated":
var handler = this._createFilesOnSuccess;
this._createFilesOnSuccess = null;
this._createFilesOnError = null;
if (handler) {
handler(aMessage.data);
}
break;
case "SpecialPowers.FilesError":
var handler = this._createFilesOnError;
this._createFilesOnSuccess = null;
this._createFilesOnError = null;
if (handler) {
handler(aMessage.data);
}
break;
}
return true;
};
SpecialPowers.prototype.quit = function() {
sendAsyncMessage("SpecialPowers.Quit", {});
};
// fileRequests is an array of file requests. Each file request is an object.
// A request must have a field |name|, which gives the base of the name of the
// file to be created in the profile directory. If the request has a |data| field
// then that data will be written to the file.
SpecialPowers.prototype.createFiles = function(fileRequests, onCreation, onError) {
if (this._createFilesOnSuccess || this._createFilesOnError) {
onError("Already waiting for SpecialPowers.createFiles() to finish.");
return;
}
this._createFilesOnSuccess = onCreation;
this._createFilesOnError = onError;
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", {});
};
SpecialPowers.prototype.executeAfterFlushingMessageQueue = function(aCallback) {
this._pongHandlers.push(aCallback);
sendAsyncMessage("SPPingService", { op: "ping" });
};
SpecialPowers.prototype.nestedFrameSetup = function() {
let self = this;
Services.obs.addObserver(function onRemoteBrowserShown(subject, topic, data) {
let frameLoader = subject;
// get a ref to the app <iframe>
frameLoader.QueryInterface(Components.interfaces.nsIFrameLoader);
let frame = frameLoader.ownerElement;
let frameId = frame.getAttribute('id');
if (frameId === "nested-parent-frame") {
Services.obs.removeObserver(onRemoteBrowserShown, "remote-browser-shown");
let mm = frame.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader.messageManager;
self._grandChildFrameMM = mm;
self.SP_SYNC_MESSAGES.forEach(function (msgname) {
mm.addMessageListener(msgname, function (msg) {
return self._sendSyncMessage(msgname, msg.data)[0];
});
});
self.SP_ASYNC_MESSAGES.forEach(function (msgname) {
mm.addMessageListener(msgname, function (msg) {
self._sendAsyncMessage(msgname, msg.data);
});
});
mm.addMessageListener("SPPAddNestedMessageListener", function(msg) {
self._addMessageListener(msg.json.name, function(aMsg) {
mm.sendAsyncMessage(aMsg.name, aMsg.data);
});
});
mm.loadFrameScript("chrome://specialpowers/content/MozillaLogger.js", false);
mm.loadFrameScript("chrome://specialpowers/content/specialpowersAPI.js", false);
mm.loadFrameScript("chrome://specialpowers/content/specialpowers.js", false);
let frameScript = "SpecialPowers.prototype.IsInNestedFrame=true;";
mm.loadFrameScript("data:," + frameScript, false);
}
}, "remote-browser-shown", false);
};
SpecialPowers.prototype.isServiceWorkerRegistered = function() {
var swm = Components.classes["@mozilla.org/serviceworkers/manager;1"]
.getService(Components.interfaces.nsIServiceWorkerManager);
return swm.getAllRegistrations().length != 0;
};
// Attach our API to the window.
function attachSpecialPowersToWindow(aWindow) {
try {
if ((aWindow !== null) &&
(aWindow !== undefined) &&
(aWindow.wrappedJSObject) &&
!(aWindow.wrappedJSObject.SpecialPowers)) {
let sp = new SpecialPowers(aWindow);
aWindow.wrappedJSObject.SpecialPowers = sp;
if (sp.IsInNestedFrame) {
sp.addPermission("allowXULXBL", true, aWindow.document);
}
}
} catch(ex) {
dump("TEST-INFO | specialpowers.js | Failed to attach specialpowers to window exception: " + ex + "\n");
}
}
// 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;
// In the case of Chrome mochitests that inject specialpowers.js as
// a regular content script
if (typeof window != 'undefined') {
window.addMessageListener = function() {}
window.removeMessageListener = function() {}
window.wrappedJSObject.SpecialPowers = new SpecialPowers(window);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>special-powers@mozilla.org</em:id>
<em:version>2015.11.16</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<!-- Target Application this extension can install into,
with minimum and maximum supported versions. -->
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
#expand <em:minVersion>__MOZILLA_VERSION_U__</em:minVersion>
<!-- Set to * so toolkit/mozapps/update/chrome tests pass. -->
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Special Powers</em:name>
<em:description>Special powers for use in testing.</em:description>
<em:creator>Mozilla</em:creator>
</Description>
</RDF>

View file

@ -0,0 +1,11 @@
specialpowers.jar:
% content specialpowers %content/ contentaccessible=true
content/specialpowers.js (content/specialpowers.js)
* content/specialpowersAPI.js (content/specialpowersAPI.js)
content/SpecialPowersObserverAPI.js (content/SpecialPowersObserverAPI.js)
content/SpecialPowersObserver.jsm (content/SpecialPowersObserver.jsm)
content/MozillaLogger.js (content/MozillaLogger.js)
content/MockFilePicker.jsm (content/MockFilePicker.jsm)
content/MockColorPicker.jsm (content/MockColorPicker.jsm)
content/MockPermissionPrompt.jsm (content/MockPermissionPrompt.jsm)
content/Assert.jsm (../modules/Assert.jsm)

View file

@ -0,0 +1,20 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
XPI_NAME = 'specialpowers'
JAR_MANIFESTS += ['jar.mn']
USE_EXTENSION_MANIFEST = True
NO_JS_MANIFEST = True
FINAL_TARGET_PP_FILES += [
'install.rdf',
]
FINAL_TARGET_FILES += [
'bootstrap.js',
]