Skip to content
Browse files

Merge pull request #3 from ochameau/1.4-refactor

ABH refactored & slimmed by Alex for SDK 1.4+ compatibility
  • Loading branch information...
2 parents ffb9d7b + 7638093 commit 7da28e4260a5a12b7321828ffa779fb79badf5f0 @csuwildcat csuwildcat committed Jan 24, 2012
View
2 .gitignore
@@ -1,2 +0,0 @@
-examples/sample-web-page/simple1.xpi
-examples/sample-web-page/simple2.xpi
View
48 README.md
@@ -21,15 +21,13 @@ the Mozilla Add-ons Builder (formerly known as FlightDeck).
## API ##
-<tt>window.mozFlightDeck.**send**(*request*)</tt>
+<tt>var promise = window.mozFlightDeck.**send**(*command*, ...)</tt>
+<tt>promise.**then**(function (*response*) { ... })</tt>
-Sends the JSON-able object `request` to the addon and returns a
-response object. For the purposes of this documentation, we will call
-the latter object `response`.
-
-`request` must have at least one property,
-`request.cmd`, which is a string specifying a command to
-send the addon.
+Sends a command request to the addon, with first argument being the command
+name, then optional arguments specific to each command. These arguments
+need to be JSON-able objects. This `send` method returns a
+`response` object through a promise pattern.
`response` has at least one boolean property,
`response.success`, indicating whether the command executed
@@ -38,39 +36,31 @@ is a string explaining why.
Valid command strings are:
-* `isInstalled` - Queries if an addon is currently
- installed in development mode, placing the boolean result
- in `response.isInstalled`. `response.installedID` will be
- the addon's ID.
+* `version` - Queries addon builder helper addon version, placing the
+ integer result in `response.msg`.
+
+* `toggleConsole` - Toggle visiblity of the XUL JS Console.
+
+* `isInstalled` - Queries if an addon is currently installed,
+ placing the boolean result in `response.isInstalled`.
-* `uninstall` - If an addon is currently installed in development
- mode, uninstalls it. If no addon is currently installed, this
- command does nothing.
+* `uninstall` - If an addon is currently installed, uninstalls it.
+ If no addon is currently installed, this command does nothing.
-* `install` - Installs an addon in development mode, uninstalling
- any predecessor. `obj.contents` must be a string representing binary
- XPI data; due to bug 541828, corrupt values can actually crash
- some versions of Firefox, so be careful!
+* `install` - Installs an addon, uninstalling any predecessor.
+ `obj.contents` must be a string representing binary XPI data;
+ due to bug 541828, corrupt values can actually crash some versions
+ of Firefox, so be careful!
-See `examples/sample-web-page/index.html` for example code that uses
-this API.
## Limitations ##
Haven't yet made Bugzilla bugs for these.
-* It's currently only possible for one addon to be "installed in
- development mode" at a time. Could be nice in the long-term to
- allow for multiple ones to be installed.
-
* The Addon Builder Helper doesn't currently deal well with the case
where addons raise exceptions while being installed or uninstalled
in development mode.
-* Addons installed in development mode don't stay installed after
- Firefox restarts. Some think this is a feature, though, not
- a bug.
-
* If the user has an addon installed via the Firefox Addon Manager
and then tries installing the same addon in development mode
using the Addon Builder Helper, an explosion occurs.
View
23 build.py
@@ -1,23 +0,0 @@
-import subprocess
-import os.path
-import shutil
-
-try:
- import json
-except ImportError:
- import simplejson as json
-
-def make_simple_xpi(msg, outfile):
- dest = os.path.join("examples", "sample-web-page", outfile)
- args = [
- "cfx", "xpi",
- "--pkgdir", os.path.join("examples", "simple"),
- "--static-args", json.dumps({"message": msg})
- ]
- print "executing: %s" % " ".join(args)
- subprocess.check_call(args, stdout=subprocess.PIPE)
- print "renaming generated addon to %s." % outfile
- shutil.move("simple.xpi", dest)
-
-make_simple_xpi("addon ONE", "simple1.xpi")
-make_simple_xpi("addon TWO", "simple2.xpi")
View
BIN data/abh-unit-test@mozilla.com.xpi
Binary file not shown.
View
8 data/addon-config.json
@@ -1,8 +1,8 @@
{
"trustedOrigins": [
- "https://builder-addons.allizom.org",
- "https://builder-addons-dev.allizom.org",
- "https://builder-addons-next.allizom.org",
- "https://builder.addons.mozilla.org/"
+ "https://builder-addons.allizom.org/*",
+ "https://builder-addons-dev.allizom.org/*",
+ "https://builder-addons-next.allizom.org/*",
+ "https://builder.addons.mozilla.org/*"
]
}
View
26 tests/test-tab-events.html → data/test-tab-events.html
@@ -1,13 +1,13 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>hi</title>
-<p>Hello.</p>
-<script>
-window.gotFoo = false;
-
-window.addEventListener("DOMContentLoaded", function() {
- document.body.addEventListener("foo", function() {
- window.gotFoo = true;
- }, false);
-}, false);
-</script>
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>hi</title>
+<p>Hello.</p>
+<script>
+window.gotFoo = false;
+
+window.addEventListener("DOMContentLoaded", function() {
+ document.body.addEventListener("foo", function() {
+ window.gotFoo = true;
+ }, false);
+}, false);
+</script>
View
89 examples/sample-web-page/index.html
@@ -1,89 +0,0 @@
-<html>
-<head>
- <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
- <title>Sample</title>
-</head>
-<body>
-<p>Click on one of the entries below to install a Jetpack-based XPI
-that requires no restart.</p>
-
-<p>This page requires the experimental FlightDeck Addon.</p>
-
-<ul style="color: blue;">
- <li><span onclick="install('simple1.xpi');"
- style="cursor: pointer;">Install Addon ONE</span></li>
- <li><span onclick="install('simple2.xpi');"
- style="cursor: pointer;">Install Addon TWO</span></li>
- <li><span onclick="isInstalled();"
- style="cursor: pointer;">Get Installation Status</span></li>
- <li><span onclick="uninstall();"
- style="cursor: pointer;">Uninstall Current Addon</span></li>
-</ul>
-
-<pre id="log" style="white-space: pre-wrap;"></pre>
-
-<script>
-function log(msg) {
- var elem = document.getElementById("log");
- elem.textContent += msg + "\n";
-}
-
-function verifyMozFlightDeck() {
- if (!window.mozFlightDeck) {
- log("window.mozFlightDeck not found. You may need to install " +
- "the Addon Builder Helper extension and/or make sure that " +
- "this page's origin is in the data/addon-config.json file.");
- return false;
- }
- return true;
-}
-
-function onError() {
- log("an error occurred.");
-}
-
-function uninstall() {
- if (!verifyMozFlightDeck()) return;
- log("uninstalling current addon");
- var msg = {cmd: "uninstall"};
- var result = window.mozFlightDeck.send(msg);
- log("result: " + JSON.stringify(result));
-}
-
-function isInstalled() {
- if (!verifyMozFlightDeck()) return;
- var msg = {cmd: "isInstalled"};
- log("result: " + JSON.stringify(window.mozFlightDeck.send(msg)));
-}
-
-function install(url) {
- if (!verifyMozFlightDeck()) return;
- var req = new XMLHttpRequest();
- req.open("GET", url);
- req.overrideMimeType('text/plain; charset=x-user-defined');
- req.addEventListener('error', onError, false);
- req.addEventListener('abort', onError, false);
- req.addEventListener('load', function() {
- // Send a message to the browser instructing it to
- // install the addon.
- var msg = {cmd: "install", contents: req.responseText};
- log("installing " + url);
- var result = window.mozFlightDeck.send(msg);
- log("response: " + JSON.stringify(result));
- }, false);
- req.send(null);
-}
-
-onload = function() {
- document.body.addEventListener("addonbuilderhelperstart", function() {
- log("message received: addonbuilderhelperstart");
- log(" 'mozFlightDeck' in window = " + ('mozFlightDeck' in window));
- }, false);
- document.body.addEventListener("addonbuilderhelperfinish", function() {
- log("message received: addonbuilderhelperfinish");
- log(" 'mozFlightDeck' in window = " + ('mozFlightDeck' in window));
- }, false);
-};
-</script>
-</body>
-</html>
View
9 examples/simple/lib/main.js
@@ -1,9 +0,0 @@
-exports.main = function(options, callbacks) {
- var args = options.staticArgs || {};
- if (typeof(args) == "string")
- args = JSON.parse(args);
- require("unload").when(function() {
- console.log("unload " + args.message);
- });
- console.log("load " + args.message);
-}
View
3 examples/simple/package.json
@@ -1,3 +0,0 @@
-{
- "id": "jid0-PZshXA4AIoK0D0dt0Cvtf9ZjBHs"
-}
View
76 lib/addon-install.js
@@ -0,0 +1,76 @@
+/* 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/. */
+
+const { Cc, Ci, Cu } = require('chrome');
+const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm");
+
+const { Base } = require("api-utils/base");
+
+/**
+ * Class to manage an addon: install and uninstall it.
+ */
+exports.AddonInstall = Base.extend({
+ /**
+ * Immediatly install an addon. Note that the given xpi file will be
+ * automatically removed when `callback` is being called.
+ *
+ * @param {String} xpiPath
+ * file path to an xpi to install
+ * @param {Function} callback
+ * function called when addon install finished. First argument being a
+ * boolean to say if the installation was successfull
+ */
+ initialize: function (xpiPath, callback) {
+
+ let self = this;
+ let installListener = {
+ onInstallEnded: function(aInstall, aAddon) {
+ self._addon = aAddon;
+ onInstalled(aInstall, true);
+ },
+ onInstallFailed: function (aInstall) {
+ onInstalled(aInstall, false);
+ }
+ };
+ function onInstalled(aInstall, success) {
+ aInstall.removeListener(installListener);
+ file.remove(xpiPath);
+ callback(success);
+ }
+
+ // Create nsIFile for the xpi file
+ let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
+ file.initWithPath(xpiPath);
+
+ // Order AddonManager to install it!
+ AddonManager.getInstallForFile(file, function(aInstall) {
+ aInstall.addListener(installListener);
+ aInstall.install();
+ });
+
+ },
+
+ get id() this._addon ? this._addon.id : null,
+
+ get isInstalled() "_addon" in this,
+
+ unload: function (callback) {
+ // Order Addonmanager to uninstall our addon
+ if (!"_addon" in this)
+ return callback();
+ let addon = this._addon;
+ let self = this;
+ let uninstallListener = {
+ onUninstalled: function onUninstalled(aAddon) {
+ if (aAddon.id != addon.id)
+ return;
+ AddonManager.removeAddonListener(uninstallListener);
+ delete self._addon;
+ callback();
+ }
+ };
+ AddonManager.addAddonListener(uninstallListener);
+ addon.uninstall();
+ }
+});
View
327 lib/addons-builder-helper.js
@@ -1,132 +1,160 @@
-var file = require("file");
-var shellUtils = require("shell-utils");
-var print;
+/* 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/. */
-var {Cc,Ci} = require('chrome');
-var windowManager = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator);
-var consoleWindow = null;
+const { Cc, Ci, Cu } = require('chrome');
+const windowManager = Cc['@mozilla.org/appshell/window-mediator;1'].
+ getService(Ci.nsIWindowMediator);
-const CONFIG_FILENAME = "addon-config.json";
-const CONFIG_PREF = "extensions.addonBuilderHelper.trustedOrigins";
+const { PageMod } = require("addon-kit/page-mod");
-function installAndRun(installPath, xpiPath) {
- // TODO: If the XPI is corrupted, Firefox will crash. Figure out how
- // to bail gracefully.
- if (file.exists(installPath))
- shellUtils.removeDirRecursive(installPath);
- shellUtils.makeDir(installPath);
- var zip = require("zip-file").open(xpiPath);
- zip.extractAll(installPath);
- var manifestFile = file.join(installPath, "harness-options.json");
- var manifest = JSON.parse(file.read(manifestFile));
- return {
- id: manifest.jetpackID,
- __proto__: require("bootstrap").run(manifest, installPath, print)
- };
-}
+const { AddonInstall } = require("addon-install");
+const { createTemporaryFileFromData } = require("file-utils");
-function installExtension(xpiData) {
- var profileDir = require("directory-service").getPath("ProfD");
- var tempXPI = file.join(profileDir, "temp.xpi");
- var myDir = file.join(profileDir, "flightdeck");
-
- var xpi = file.open(tempXPI, "wb");
- xpi.write(xpiData);
- xpi.close();
+const CONFIG_FILENAME = "addon-config.json";
+const CONFIG_PREF = "extensions.addonBuilderHelper.trustedOrigins";
- var ext = installAndRun(myDir, tempXPI);
- return ext;
-}
-function channelErrorWrapper(func, console) {
- return function() {
- try {
- return func.apply(this, arguments);
- } catch (e) {
- console.exception(e);
- return {success: false, msg: "internal error"};
- }
- };
+/**
+ * Utility method to toggle the XUL JS Console.
+ * Return `message` attribute send back to webpage.
+ */
+let consoleWindow = null;
+function toggleConsoleWindow(command) {
+ consoleWindow = windowManager.getMostRecentWindow('global:console');
+ switch(command) {
+ case 'open':
+ consoleWindow = consoleWindow ? consoleWindow.focus() :
+ windowManager.getMostRecentWindow(null)
+ .open('chrome://global/content/console.xul',
+ '_blank',
+ 'chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar');
+ break;
+ case 'close':
+ consoleWindow = consoleWindow ? consoleWindow.close() : null;
+ break;
+ case 'isOpen':
+ return consoleWindow ? true : false;
+ break;
+ default:
+ return 'An unrecognized command was passed through the "contents" ' +
+ 'property of the mozFlightDeck send object. Available commands ' +
+ 'are "open", "close", and "isOpen"';
+ }
+ return null;
}
-function attachApiToChannel(channel, addonManager) {
- function onMessage(data) {
- if (!('cmd' in data && typeof(data.cmd) == 'string'))
- return {success: false, msg: "bad request"};
- switch (data.cmd) {
+/**
+ * Implement API exposed to the content script
+ * @param {SingleAddonManager} addonManager
+ * Utility object that handle low level addon install/uninstall
+ * @param {Array} args
+ * Arguments passed to `mozFlightDeck.send` method from webpage
+ * @param {function} callback
+ * Function called to send back result to the webpage's `then` promise method
+ */
+function onContentScriptRequest(addonManager, args, callback) {
+ try {
+ // mozFlighDeck.`send` accepts n-arguments.
+ // First one is always the command name,
+ // next ones are specific to each command.
+ let command = args[0];
+ if (typeof command != 'string')
+ return callback({success: false, msg: "bad request: invalid command"});
+ switch (command) {
case "isInstalled":
- return {success: true,
- isInstalled: addonManager.isInstalled,
- installedID: addonManager.installedID};
+ callback({
+ success: true,
+ isInstalled: addonManager.isInstalled,
+ installedID: addonManager.installedID
+ });
+ break;
case "version":
- return {success: true,
- msg: require('self').version};
+ callback({
+ success: true,
+ msg: require('self').version
+ });
+ break;
case "uninstall":
- addonManager.uninstall();
- return {success: true,
- msg: "uninstalled"};
+ addonManager.uninstall(function () {
+ callback({
+ success: true,
+ msg: "uninstalled"
+ });
+ });
+ break;
case "install":
- if (!('contents' in data && typeof(data.contents) == 'string'))
- return {success: false,
- msg: "need data"};
- addonManager.install(data.contents);
- return {success: true,
- msg: "installed"};
- case "toggleConsole":
- consoleWindow = windowManager.getMostRecentWindow('global:console');
- var message = null;
- switch(data.contents){
- case 'open': consoleWindow = (consoleWindow) ? consoleWindow.focus() : windowManager.getMostRecentWindow(null).open('chrome://global/content/console.xul', '_blank', 'chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar');
- break;
- case 'close': consoleWindow = (consoleWindow) ? consoleWindow.close() : null;
- break;
- case 'isOpen': message = (consoleWindow) ? true : false;
- break;
- default:
- message = 'An unrecognized command was passed through the "contents" property of the mozFlightDeck send object. Available commands are "open", "close", and "isOpen"';
- }
- return {
- success: true,
- msg: message
- };
+ let contents = args[1];
+ if (typeof contents != 'string')
+ return callback({
+ success: false,
+ msg: "need xpi contents"
+ });
+ addonManager.install(contents, function (success) {
+ callback({
+ success: success,
+ msg: success ? "installed" : "failed to install"
+ });
+ });
+ break;
+ case "toggleConsole":
+ let consoleCommand = args[1];
+ let message = toggleConsoleWindow(consoleCommand);
+ callback({
+ success: true,
+ msg: message
+ });
+ break;
default:
- return {success: false,
- msg: "unknown command: " + data.cmd};
+ callback({
+ success: false,
+ msg: "unknown command: " + command
+ });
+ break;
}
}
-
- channel.whenMessaged(channelErrorWrapper(onMessage));
+ catch(e) {
+ console.exception(e);
+ callback({
+ success: false,
+ msg: "internal error"
+ });
+ }
+ return null;
}
-function SingleAddonManager(installExtension) {
+function SingleAddonManager() {
var self = this;
var currExtension = null;
- self.__defineGetter__(
- "isInstalled",
- function isInstalled() {
- return (currExtension != null);
- });
+ return {
+ get isInstalled() {
+ return (currExtension != null && currExtension.isInstalled);
+ },
- self.__defineGetter__(
- "installedID",
- function installedID() {
+ get installedID() {
if (!currExtension)
return null;
return currExtension.id;
- });
+ },
- self.install = function install(xpiData) {
- if (currExtension)
- self.uninstall();
- currExtension = installExtension(xpiData);
- };
+ install: function install(xpiData, callback) {
+ if (currExtension) {
+ let self = this;
+ self.uninstall(function () {
+ self.install(xpiData, callback);
+ });
+ }
+ else
+ currExtension = AddonInstall.new(createTemporaryFileFromData(xpiData), callback);
+ },
- self.uninstall = function uninstall() {
- if (currExtension) {
- var oldExtension = currExtension;
- currExtension = null;
- oldExtension.unload();
+ uninstall: function uninstall(callback) {
+ if (currExtension) {
+ var oldExtension = currExtension;
+ currExtension = null;
+ oldExtension.unload(callback);
+ }
}
};
};
@@ -142,45 +170,102 @@ function safeGetConfig() {
}
if (!('trustedOrigins' in config &&
- config.trustedOrigins &&
- config.trustedOrigins.constructor &&
- config.trustedOrigins.constructor.name == 'Array'))
+ Array.isArray(config.trustedOrigins)))
config.trustedOrigins = [];
return config;
}
-exports.main = function main(options, callbacks) {
- var manager = require("json-channel").createManager("mozFlightDeck");
- var addonManager = new SingleAddonManager(installExtension);
+exports.main = function main() {
+ // Read config file from data folder
var config = safeGetConfig();
- print = callbacks.print;
-
+ // Read trusted-origins from preference
var pref = require("preferences-service").get(CONFIG_PREF, null);
-
if (typeof(pref) == "string") {
- try {
+ try {
config.trustedOrigins = config.trustedOrigins.concat(pref.split(","));
} catch (e) {
console.log("Error when reading preference " + CONFIG_PREF);
console.exception(e);
}
}
- function attach(window) {
- attachApiToChannel(manager.addChannel(window), addonManager);
- }
+ PageMod({
+ include: config.trustedOrigins,
+ contentScriptWhen: "start",
+ contentScript: "new " + function ContentScriptScope() {
+
+ // Utility class to build a promise pattern
+ function Promise(self) {
+ let done = false;
+ let args = null;
+ let listeners = [];
+ return {
+ listen: function listen(callback) {
+ if (done)
+ callback.apply(self, args);
+ else
+ listeners.push(callback);
+ },
+ end: function end() {
+ // Avoid multiple promises calls
+ if (done)
+ return;
+ done = true;
+ args = Array.slice(arguments);
+ // Call already-registered promises
+ for each(let callback in listeners)
+ callback.apply(self, args);
+ }
+ }
+ };
+
+ // Public API we expose to web content:
+ let msgCount = 1;
+ let mozFlightDeck = {
+ send: function () {
+ let promise = Promise();
+ let messageId = msgCount++;
+ // First listen for the addon response,
+ self.port.once(messageId, promise.end);
+ // before sending our request to it:
+ self.port.emit("content-request", messageId, Array.slice(arguments));
- attach = require("trusted-origin-filter").wrap(config.trustedOrigins,
- attach);
+ // Return a promise
+ return {
+ then: promise.listen
+ };
+ }
+ };
+
+ // Expose `mozFlightDeck` to the web page
+ unsafeWindow.mozFlightDeck = mozFlightDeck;
+
+ },
+
+ onAttach: function (worker) {
+ let addonManager = SingleAddonManager();
+ // Listen to all requests made by the content script
+ worker.port.on("content-request", function (messageId, args) {
+ let called = false;
+ onContentScriptRequest(addonManager, args, function() {
+ // Ensure sending only one response
+ if (called)
+ return;
+ called = true;
+ // Send response to the content script
+ worker.port.emit.apply(null,
+ [messageId].concat(Array.slice(arguments)));
+ });
+ });
+ }
+ });
- require("tab-browser").whenContentLoaded(attach);
- require("tab-events").forEachWindow(attach);
+ // Dipatch helper start/finish events to web content
require("tab-events").dispatchTrivialEvent("addonbuilderhelperstart");
require("unload").when(
function() {
- manager.unload();
require("tab-events").dispatchTrivialEvent("addonbuilderhelperfinish");
});
};
View
12 lib/directory-service.js
@@ -1,12 +0,0 @@
-const {Cc, Ci, Cr} = require("chrome");
-
-var dirSvc = Cc["@mozilla.org/file/directory_service;1"]
- .getService(Ci.nsIProperties);
-
-exports.getPath = function getPath(name) {
- try {
- return dirSvc.get(name, Ci.nsIFile).path;
- } catch (e if e.result == Cr.NS_ERROR_FAILURE) {
- throw new Error("Directory service entry not found: " + name);
- }
-};
View
37 lib/file-utils.js
@@ -0,0 +1,37 @@
+/* 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/. */
+
+// Create a temporary file for a given string data
+const file = require("api-utils/file");
+exports.createTemporaryFileFromData = function (data, tmpName) {
+ var profileDir = require("system").pathFor("ProfD");
+ var path = file.join(profileDir, tmpName ? tmpName : "tmp-file");
+
+ var tmpFile = file.open(path, "wb");
+ tmpFile.write(data);
+ tmpFile.close();
+
+ return path;
+}
+
+exports.createTemporaryFileFromURL = function (url, tmpName) {
+ let data = exports.readBinaryURI(url);
+ return exports.createTemporaryFileFromData(data, tmpName);
+}
+
+// Utility function that synchronously reads local resource from the given
+// `uri` and returns content string. Read in binary mode.
+const {ByteReader} = require("api-utils/byte-streams");
+const {Cc, Ci} = require("chrome");
+exports.readBinaryURI = function readBinaryURI(uri) {
+ let ioservice = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+ let channel = ioservice.newChannel(uri, "UTF-8", null);
+ let stream = channel.open();
+
+ let reader = new ByteReader(stream);
+ let data = reader.read();
+ stream.close();
+
+ return data;
+}
View
131 lib/json-channel.js
@@ -1,131 +0,0 @@
-const {Cu} = require("chrome");
-
-if (!this.XPCSafeJSObjectWrapper)
- this.XPCSafeJSObjectWrapper = function(f) { return f; };
-
-// This function is decompiled and evaluated in a sandbox.
-function buildChannelInContent(sandbox) {
- var sendImpl = sandbox.sendToChrome;
- var whenMessagedImpl = sandbox.whenMessaged;
-
- sandbox.sendToChrome = null;
- sandbox.whenMessaged = null;
-
- sandbox.demolish = function() {
- sendImpl = null;
- whenMessagedImpl = null;
- delete sandbox.window[sandbox.name];
- };
-
- sandbox.window[sandbox.name] = {
- send: function send(data) {
- var result = sendImpl(JSON.stringify(data));
- if (typeof(result) == "string")
- return JSON.parse(result);
- return result;
- },
- whenMessaged: function whenMessaged(cb) {
- whenMessagedImpl(function(data) { cb(JSON.parse(data)); });
- }
- };
-}
-
-function Channel(window, manager) {
- memory.track(this);
- this._receivedFromContent = null;
- this._sendToContent = null;
- this._manager = manager;
- this._window = window;
- this._window.addEventListener("unload", this, true);
- this._inject();
-}
-
-Channel.prototype = {
- _inject: function _inject() {
- var self = this;
- var unsafeWindow = self._window.wrappedJSObject;
- var sandbox = Cu.Sandbox(unsafeWindow);
-
- function whenMessaged(cb) {
- self._sendToContent = XPCSafeJSObjectWrapper(cb);
- };
- sandbox.importFunction(whenMessaged);
-
- function sendToChrome(data) {
- if (self._receivedFromContent) {
- data = XPCSafeJSObjectWrapper(data);
- var result = self._receivedFromContent.call(undefined,
- JSON.parse(data));
- if (typeof(result) == "object")
- return JSON.stringify(result);
- return result;
- }
- return undefined;
- };
- sandbox.importFunction(sendToChrome);
-
- sandbox.name = self._manager.name;
- sandbox.window = unsafeWindow;
- self._sandbox = sandbox;
- Cu.evalInSandbox("(" + buildChannelInContent + ")(this);", sandbox);
- },
- handleEvent: function handleEvent(event) {
- this.close();
- },
- whenMessaged: function whenMessaged(cb) {
- this._receivedFromContent = cb;
- },
- send: function send(data) {
- if (this._sendToContent)
- this._sendToContent.call(undefined, JSON.stringify(data));
- },
- close: function close() {
- if (this._manager) {
- try {
- Cu.evalInSandbox("demolish();", this._sandbox);
- } catch (e) {
- console.exception(e);
- };
- this._sandbox = null;
- this._manager.onCloseChannel(this);
- this._window.removeEventListener("unload", this, true);
- this._receivedFromContent = null;
- this._sendToContent = null;
- this._window = null;
- this._manager = null;
- }
- }
-};
-
-require("errors").catchAndLogProps(Channel.prototype, "handleEvent");
-
-function Manager(name) {
- memory.track(this);
- this.name = name;
- this._channels = [];
- require("unload").ensure(this);
-}
-
-Manager.prototype = {
- get channels() {
- return this._channels.slice();
- },
- addChannel: function addChannel(window) {
- var channel = new Channel(window, this);
- this._channels.push(channel);
- return channel;
- },
- onCloseChannel: function onCloseChannel(channel) {
- var index = this._channels.indexOf(channel);
- if (index == -1)
- throw new Error("unknown channel");
- this._channels.splice(index, 1);
- },
- unload: function unload() {
- this.channels.forEach(function(channel) { channel.close(); });
- }
-};
-
-exports.createManager = function createManager(name) {
- return new Manager(name);
-};
View
98 lib/shell-utils.js
@@ -1,98 +0,0 @@
-/*
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-# http://www.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
-#
-# The Original Code is the Extension Manager.
-#
-# The Initial Developer of the Original Code is Ben Goodger.
-# Portions created by the Initial Developer are Copyright (C) 2004
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Ben Goodger <ben@mozilla.org> (Google Inc.)
-# Benjamin Smedberg <benjamin@smedbergs.us>
-# Jens Bannmann <jens.b@web.de>
-# Robert Strong <robert.bugzilla@gmail.com>
-# Dave Townsend <dtownsend@oxymoronical.com>
-# Daniel Veditz <dveditz@mozilla.com>
-# Alexander J. Vincent <ajvincent@gmail.com>
-# Atul Varma <atul@mozilla.com>
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK *****
-*/
-
-const {Cc, Ci, Cu} = require("chrome");
-
-var FileUtils = {};
-Cu.import("resource://gre/modules/FileUtils.jsm", FileUtils);
-FileUtils = FileUtils.FileUtils;
-
-function MozFile(path) {
- var file = Cc['@mozilla.org/file/local;1']
- .createInstance(Ci.nsILocalFile);
- file.initWithPath(path);
- return file;
-}
-
-// TODO: This should really be moved to the 'file' module.
-exports.makeDir = function makeDir(path) {
- MozFile(path).create(Ci.nsILocalFile.DIRECTORY_TYPE,
- FileUtils.PERMS_DIRECTORY);
-};
-
-/**
- * Deletes a directory and its children. First it tries nsIFile::Remove(true).
- * If that fails it will fall back to recursing, setting the appropriate
- * permissions, and deleting the current entry. This is needed for when we have
- * rights to delete a directory but there are entries that have a read-only
- * attribute (e.g. a copy restore from a read-only CD, etc.)
- * @param dir
- * A string path to the directory to be deleted
- */
-exports.removeDirRecursive = function removeDirRecursive(dir) {
- dir = MozFile(dir);
-
- try {
- dir.remove(true);
- return;
- }
- catch (e) {
- }
-
- var dirEntries = dir.directoryEntries;
- while (dirEntries.hasMoreElements()) {
- var entry = dirEntries.getNext().QueryInterface(Ci.nsIFile);
-
- if (entry.isDirectory()) {
- removeDirRecursive(entry);
- }
- else {
- entry.permissions = FileUtils.PERMS_FILE;
- entry.remove(false);
- }
- }
- dir.permissions = FileUtils.PERMS_DIRECTORY;
- dir.remove(true);
-};
View
2 lib/trusted-origin-filter.js
@@ -2,7 +2,7 @@ const {Cc, Ci} = require("chrome");
var global = this;
-exports.wrap = function wrap(origins, func, console) {
+exports.wrap = function wrap(origins, func) {
var ios = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
View
91 lib/zip-file.js
@@ -1,91 +0,0 @@
-const {Cc, Ci, Cu} = require("chrome");
-
-var FileUtils = {};
-Cu.import("resource://gre/modules/FileUtils.jsm", FileUtils);
-FileUtils = FileUtils.FileUtils;
-
-const ZIP_DIR_REGEXP = /.*\/$/;
-
-function MozFile(path) {
- var file = Cc['@mozilla.org/file/local;1']
- .createInstance(Ci.nsILocalFile);
- file.initWithPath(path);
- return file;
-}
-
-function withZip(file, cb, args) {
- var result;
- try {
- var zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
- createInstance(Ci.nsIZipReader);
- memory.track(zipReader, "ZipReader");
- var finalArgs = [zipReader];
- if (args)
- finalArgs = finalArgs.concat(args);
- zipReader.open(file);
- result = cb.apply(this, finalArgs);
- zipReader.close();
- } catch (e) {
- try {
- zipReader.close();
- } catch (e2) {}
- throw e;
- }
- return result;
-}
-
-function addPath(file, path) {
- var clone = file.clone();
- path.split("/").forEach(
- function(part) {
- if (part) clone.append(part);
- });
- return clone;
-}
-
-function getEntries(zipReader) {
- var entries = [];
- var enumerator = zipReader.findEntries(null);
- while (enumerator.hasMore())
- entries.push(enumerator.getNext());
- return entries;
-}
-
-function extractAll(zipReader, baseOutDir) {
- var entries = getEntries(zipReader);
- var dirNames = [entry for each (entry in entries)
- if (entry.match(ZIP_DIR_REGEXP))];
- dirNames.sort();
- dirNames.forEach(
- function(name) {
- var outDir = addPath(baseOutDir, name);
- if (!outDir.exists()) {
- outDir.create(Ci.nsILocalFile.DIRECTORY_TYPE,
- FileUtils.PERMS_DIRECTORY);
- }
- });
-
- var fileNames = [entry for each (entry in entries)
- if (!entry.match(ZIP_DIR_REGEXP))];
- fileNames.forEach(
- function(name) {
- var outFile = addPath(baseOutDir, name);
- if (!outFile.exists()) {
- zipReader.extract(name, outFile);
- outFile.permissions |= FileUtils.PERMS_FILE;
- }
- });
-}
-
-exports.open = function open(path) {
- var file = MozFile(path);
-
- return {
- get entries() {
- return withZip(file, getEntries);
- },
- extractAll: function(path) {
- return withZip(file, extractAll, [MozFile(path)]);
- }
- };
-};
View
3 package.json
@@ -5,8 +5,7 @@
"url": "http://github.com/mozilla/addon-builder-helper",
"version": "1.3",
"dependencies": [
- "api-utils",
- "development-mode"
+ "api-utils"
],
"main": "addons-builder-helper",
"fullName": "Add-on Builder Helper",
View
1 tests/corrupt.zip
@@ -1 +0,0 @@
-hi there.
View
BIN tests/foo.zip
Binary file not shown.
View
46 tests/test-addon-install.js
@@ -0,0 +1,46 @@
+/* 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/. */
+
+const { Cc, Ci, Cu } = require("chrome");
+const { AddonInstall } = require("addon-install");
+const { Services } = Components.utils.import("resource://gre/modules/Services.jsm");
+
+const SELF = require("self");
+const ADDON_URL = SELF.data.url("abh-unit-test@mozilla.com.xpi");
+const ADDON_PATH = require("file-utils").createTemporaryFileFromURL(ADDON_URL);
+
+exports.testInstall = function (test) {
+ test.waitUntilDone();
+
+ // Save all events distpatched by bootstrap.js of the installed addon
+ let events = [];
+ let eventsObserver = {
+ observe: function (subject, topic, data) {
+ events.push(data);
+ }
+ };
+ Services.obs.addObserver(eventsObserver, "abh-unit-test", false);
+
+ // Install the test addon
+ let install = AddonInstall.new(ADDON_PATH, function onInstalled(success) {
+ test.assert(success, "Installed successfully");
+ test.assertEqual(install.id, "abh-unit-test@mozilla.com", "`install.id` is valid");
+ test.assert(install.isInstalled, "`install.isInstalled` is true on 'onInstalled' call");
+
+ // Now uninstall it
+ install.unload(function () {
+ test.assert(!install.isInstalled, "`install.isInstalled` is false when unload callback is called");
+ test.assert(!install.id, "`install.id` is empty after uninstall");
+
+ // Ensure that bootstrap.js methods of the addon have been called
+ // successfully and in the right order
+ Services.obs.removeObserver(eventsObserver, "abh-unit-test");
+ test.assertEqual(JSON.stringify(events),
+ JSON.stringify(["install", "startup", "shutdown", "uninstall"]),
+ "addon's bootstrap.js functions have been called");
+
+ test.done();
+ });
+ });
+}
View
305 tests/test-addons-builder-helper.js
@@ -1,120 +1,203 @@
-// Need to do this to be able to require parent-loader and get
-// the global scope for our module, so we can test private functions.
-require("chrome");
+/* 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/. */
-function getModuleGlobal(module) {
- var sb = require("parent-loader").findSandboxForModule(module);
- return sb.globalScope;
+const { Cc, Ci, Cu } = require("chrome");
+const tabs = require("addon-kit/tabs");
+const { Services } = Components.utils.import("resource://gre/modules/Services.jsm");
+
+const { readBinaryURI } = require("file-utils");
+
+const TEST_ADDON_URL = require("self").data.url("abh-unit-test@mozilla.com.xpi");
+
+// First register "data:*" URLs as being AddonBuilder trusted URLs
+require("api-utils/preferences-service").set(
+ "extensions.addonBuilderHelper.trustedOrigins",
+ "data:*");
+
+// Then, load Addon builder helper addon
+require("addons-builder-helper").main();
+
+// Utility function that opens a tab and attach a unit worker in it
+function createTest(contentScript, onWorkerReady) {
+ return function (test) {
+ test.waitUntilDone();
+
+ tabs.open({
+ url: "data:text/html,",
+ onReady: function(tab) {
+ let worker = tab.attach({
+ contentScript: [
+ // Add some unit test method in global content script scope
+ "new " + function ContentScriptScope() {
+ assert = function assert(value, msg)
+ self.port.emit("assert", value, msg);
+ assertEqual = function assertEqual(a, b, msg)
+ self.port.emit("assertEqual", a, b, msg);
+ done = function done() self.port.emit("done")
+ },
+ // Add given content script
+ contentScript
+ ]
+ });
+
+ function done() {
+ tab.close();
+ test.done();
+ }
+
+ worker.port.on("assert", function (value, msg) {
+ test.assert(value, msg);
+ });
+ worker.port.on("assertEqual", function (a, b, msg) {
+ test.assertEqual(a, b, msg);
+ });
+ worker.port.on("done", done);
+
+ if (typeof onWorkerReady == "function")
+ onWorkerReady(test, worker, done);
+ }
+ });
+ };
}
-var abh = getModuleGlobal("addons-builder-helper");
-
-exports.testAddonConfigJsonIsValid = function(test) {
- var config = abh.safeGetConfig();
- test.assert(config.trustedOrigins.length > 0,
- "At least one trusted origin exists");
- // Ensure that trusted origins can be passed to the filter
- // without any errors being logged.
- require("trusted-origin-filter").wrap(config.trustedOrigins, function() {});
-};
-
-exports.testAttachApiToChannel = function(test) {
- var onMessage;
- var fakeChannel = {
- whenMessaged: function(cb) {
- onMessage = cb;
+// Use "000" to execute this test first
+exports.test000createTest = createTest(
+ "new " + function ContentScriptScope() {
+ assert(true, "`assert` works");
+ assertEqual("a", "a", "`assertEqual` works");
+ self.port.on("test-worker-events", done);
+ },
+ function (test, worker, done) {
+ worker.port.emit("test-worker-events");
+ }
+);
+
+// Use "111" to execute this test second
+exports.test111Minimal = createTest(
+ "new " + function ContentScriptScope() {
+ assert("mozFlightDeck" in unsafeWindow, "`mozFlightDeck` is set");
+ let FD = unsafeWindow.mozFlightDeck;
+ assertEqual(typeof FD.send, "function", "`mozFlightDeck` has a `send` method");
+ assertEqual(typeof FD.send("version").then, "function", "`send` return an object with `then` method");
+ done();
+ },
+ function (test, worker, done) {
+
+ }
+);
+
+exports.testVersion = createTest(
+ "new " + function ContentScriptScope() {
+ unsafeWindow.mozFlightDeck.send("version").then(function (data) {
+ assert(data.success, "'version' succeed");
+ self.port.emit("version", data.msg);
+ });
+ },
+ function (test, worker, done) {
+ worker.port.on("version", function (v) {
+ let version = require("self").version;
+ test.assertEqual(version, v, "'version' returns the correct version number");
+ done();
+ });
+ }
+);
+
+exports.testIsInstalled = createTest(
+ "new " + function ContentScriptScope() {
+ unsafeWindow.mozFlightDeck.send("isInstalled").then(function (data) {
+ assert(data.success, "'isInstalled' succeed");
+ assert(!data.isInstalled, "'isInstalled' returns false before calling `install`");
+ done();
+ });
+ },
+ function (test, worker, done) {}
+);
+
+exports.testIsInstalled = createTest(
+ "new " + function ContentScriptScope() {
+ let toggleCount = 1;
+ function toggle(command, result, callback) {
+ unsafeWindow.mozFlightDeck.send("toggleConsole", command).then(function (data) {
+ let n = toggleCount++;
+ assert(data.success, "'toggleConsole' with '" + command + "' succeed [" + n + "]");
+ assertEqual(data.msg, result, "'toggleConsole' returns " + result + " on '" + command + "' [" + n + "]");
+ callback();
+ });
}
- };
- var lastXpiData;
- var uninstallCalled = 0;
- var fakeManager = {
- isInstalled: false,
- installedID: null,
- install: function(xpiData) {
- lastXpiData = xpiData;
- },
- uninstall: function() {
- uninstallCalled++;
+ toggle("isOpen", false, function () {
+ toggle("open", null, function () {
+ toggle("isOpen", true, function () {
+ toggle("close", null, function () {
+ toggle("isOpen", false, function () {
+ done();
+ });
+ });
+ });
+ });
+ });
+ },
+ function (test, worker, done) {}
+);
+
+exports.testInstall = createTest(
+ "new " + function ContentScriptScope() {
+ function assertIsInstalled(addonId, msg, next) {
+ unsafeWindow.mozFlightDeck.send("isInstalled").then(function (data) {
+ assert(data.success, "'isInstalled' succeed");
+ if (addonId) {
+ assert(data.isInstalled, msg);
+ assertEqual(data.installedID, addonId, "`installedID` refer to the correct id");
+ }
+ else {
+ assert(!data.isInstalled, msg);
+ assertEqual(data.installedID, null, "`installedID` is null when isInstalled is false");
+ }
+ next();
+ });
}
- };
- abh.attachApiToChannel(fakeChannel, fakeManager);
-
- test.assertEqual(JSON.stringify(onMessage({})),
- JSON.stringify({success: false, msg: "bad request"}));
- test.assertEqual(JSON.stringify(onMessage({cmd: 'isInstalled'})),
- JSON.stringify({success: true, isInstalled: false,
- installedID: null}));
- fakeManager.isInstalled = true;
- fakeManager.installedID = 'bleh';
- test.assertEqual(JSON.stringify(onMessage({cmd: 'isInstalled'})),
- JSON.stringify({success: true, isInstalled: true,
- installedID: 'bleh'}));
- test.assertEqual(JSON.stringify(onMessage({cmd: 'install'})),
- JSON.stringify({success: false, msg: "need data"}));
-
- test.assertEqual(lastXpiData, undefined);
- test.assertEqual(JSON.stringify(onMessage({cmd: 'install',
- contents: 'u'})),
- JSON.stringify({success: true, msg: "installed"}));
- test.assertEqual(lastXpiData, 'u');
- test.assertEqual(uninstallCalled, 0);
- test.assertEqual(JSON.stringify(onMessage({cmd: 'uninstall'})),
- JSON.stringify({success: true, msg: "uninstalled"}));
- test.assertEqual(uninstallCalled, 1);
- test.assertEqual(JSON.stringify(onMessage({cmd: 'blap'})),
- JSON.stringify({success: false,
- msg: "unknown command: blap"}));
-};
-
-exports.testSingleAddonManager = function(test) {
- var unloadCalled = 0;
-
- function fakeInstallExtension(xpiData) {
- test.assertEqual(typeof(xpiData), "string");
- return {
- id: 'blargle',
- unload: function() {
- unloadCalled++;
+ self.port.on("xpiData", function (xpiData) {
+ assertIsInstalled(false, "'isInstalled' is false before calling `install`", install);
+ function install() {
+ unsafeWindow.mozFlightDeck.send("install", xpiData).then(function (data) {
+ assert(data.success, "'install' succeed");
+ assertEqual(data.msg, "installed", "'install' msg is valid");
+ assertIsInstalled("abh-unit-test@mozilla.com", "'isInstalled' is true after successfull `install`", uninstall);
+ });
}
- };
- };
-
- var sam = new abh.SingleAddonManager(fakeInstallExtension);
- test.assertEqual(sam.isInstalled, false);
- test.assertEqual(sam.installedID, null);
- sam.install("foo");
- test.assertEqual(sam.isInstalled, true);
- test.assertEqual(sam.installedID, 'blargle');
- test.assertEqual(unloadCalled, 0);
- sam.install("bar");
- test.assertEqual(sam.isInstalled, true);
- test.assertEqual(unloadCalled, 1);
- sam.uninstall();
- test.assertEqual(unloadCalled, 2);
- sam.uninstall();
- test.assertEqual(unloadCalled, 2);
-};
-
-exports.testChannelErrorWrapper = function(test) {
- var func = abh.channelErrorWrapper(function() { return {msg: 'hi'}; });
- test.assertEqual(JSON.stringify(func()),
- JSON.stringify({msg: 'hi'}),
- "channelErrorWrapper must passthrough return values");
-
- var exceptions = [];
- var mockConsole = {
- exception: function(e) {
- exceptions.push(e);
+ });
+ function uninstall() {
+ unsafeWindow.mozFlightDeck.send("uninstall").then(function (data) {
+ assert(data.success, "'uninstall' succeed");
+ assertEqual(data.msg, "uninstalled", "'uninstall' msg is valid");
+ assertIsInstalled(false, "'isInstalled' is false after calling `uninstall`", end);
+ });
}
- };
+ function end() {
+ self.port.emit("uninstalled");
+ }
+ },
+ function (test, worker, done) {
+
+ // Save all events distpatched by bootstrap.js of the installed addon
+ let events = [];
+ let eventsObserver = {
+ observe: function (subject, topic, data) {
+ events.push(data);
+ }
+ };
+ Services.obs.addObserver(eventsObserver, "abh-unit-test", false);
- func = abh.channelErrorWrapper(function() { o(); },
- mockConsole);
- test.assertEqual(JSON.stringify(func()),
- JSON.stringify({success: false, msg: "internal error"}),
- "channelErrorWrapper must return JSON response on err");
+ // We can't use self.data.load as it doesn't read in binary mode!
+ let xpiData = readBinaryURI(TEST_ADDON_URL);
+ worker.port.emit("xpiData", xpiData);
- test.assertEqual(exceptions[0].toString(),
- "ReferenceError: o is not defined",
- "channelErrorWrapper must log exceptions");
-};
+ worker.port.on("uninstalled", function () {
+ Services.obs.removeObserver(eventsObserver, "abh-unit-test");
+ test.assertEqual(JSON.stringify(events),
+ JSON.stringify(["install", "startup", "shutdown", "uninstall"]),
+ "addon's bootstrap.js functions have been called");
+ test.done();
+ });
+ }
+);
View
11 tests/test-directory-service.js
@@ -1,11 +0,0 @@
-var dirSvc = require("directory-service");
-
-exports.testGet = function(test) {
- test.assertEqual(typeof(dirSvc.getPath("ProfD")), "string",
- "Profile directory should exist");
- test.assertRaises(
- function() require("directory-service").getPath("blargy"),
- "Directory service entry not found: blargy",
- "Getting nonexistent entries should raise exceptions"
- );
-};
View
23 tests/test-json-channel.html
@@ -1,23 +0,0 @@
-<html>
-<head>
- <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
- <title>FlightDeck</title>
-</head>
-<body>
-Hello.
-<pre id="foo"></pre>
-</body>
-<script>
-function onLoad() {
- var foo = document.getElementById("foo");
- foo.textContent = "BLAH";
- window.boop.whenMessaged(function(data) {
- foo.textContent = "got " + JSON.stringify(data);
- });
- var result = window.boop.send("HAI2u!");
- if (!(result && result.msg == 'gotcha'))
- throw new Error('expected result.msg == "gotcha"');
-}
-window.addEventListener("load", onLoad, false);
-</script>
-</html>
View
52 tests/test-json-channel.js
@@ -1,52 +0,0 @@
-var timer = require("timer");
-var tabBrowser = require("tab-browser");
-
-// TODO: Replace arb timeout w/ "real" deterministic event handling.
-const ARB_TIMEOUT = 100;
-
-exports.testBasic = function(test) {
- var manager = require("json-channel").createManager("boop");
- var file = require("file");
- var url = require("url");
- var mydir = file.dirname(url.toFilename(__url__));
- var indexUrl = url.fromFilename(file.join(mydir, "test-json-channel.html"));
-
- var tracker = tabBrowser.whenContentLoaded(
- function(window) {
- if (window.location == indexUrl) {
- var foo = window.document.getElementById("foo");
- var channel = manager.addChannel(window);
- channel.whenMessaged(
- function(data) {
- test.assertEqual(data, "HAI2u!",
- "chrome channel.whenMessaged() works.");
- channel.send("O YEA!");
- timer.setTimeout(
- function() {
- test.assertEqual(foo.textContent,
- 'got "O YEA!"',
- "content channel.whenMessaged works.");
- test.assertEqual(manager.channels.length,
- 1,
- "channel count is correct");
- window.close();
- timer.setTimeout(function() {
- test.assertEqual(
- manager.channels.length,
- 0,
- "channel closed on window close"
- );
- manager.unload();
- tracker.unload();
- test.done();
- }, ARB_TIMEOUT);
- },
- ARB_TIMEOUT);
- return {msg: 'gotcha'};
- });
- }
- });
-
- tabBrowser.addTab(indexUrl);
- test.waitUntilDone(5000);
-};
View
9 tests/test-tab-events.js
@@ -1,15 +1,12 @@
var tabEvents = require("tab-events");
-var tabBrowser = require("tab-browser");
-var timer = require("timer");
+var tabBrowser = require("api-utils/tab-browser");
+var timer = require("api-utils/timer");
// TODO: Replace arb timeout w/ "real" deterministic event handling.
const ARB_TIMEOUT = 100;
exports.testDispatchTrivialEvent = function(test) {
- var file = require("file");
- var url = require("url");
- var mydir = file.dirname(url.toFilename(__url__));
- var indexUrl = url.fromFilename(file.join(mydir, "test-tab-events.html"));
+ var indexUrl = require("self").data.url("test-tab-events.html");
var tracker = tabBrowser.whenContentLoaded(
function(window) {
View
71 tests/test-trusted-origin-filter.js
@@ -1,71 +0,0 @@
-var timer = require("timer");
-var tabBrowser = require("tab-browser");
-var tof = require("trusted-origin-filter");
-
-// TODO: Replace arb timeout w/ "real" deterministic event handling.
-const ARB_TIMEOUT = 100;
-
-exports.testMalformedOriginLogsErrors = function(test) {
- var errors = [];
- var fakeConsole = {
- error: function(msg) {
- errors.push(msg);
- }
- };
-
- function testErrors(against, msg) {
- test.assertEqual(JSON.stringify(errors), JSON.stringify(against),
- msg);
- errors = [];
- }
-
- tof.wrap([1], undefined, fakeConsole);
- testErrors(["malformed origin URI: 1"]);
-
- tof.wrap(["chrome://nou"], undefined, fakeConsole);
- testErrors(["malformed origin URI: chrome://nou"]);
-
- tof.wrap(["http://localhost:8000/"], undefined, fakeConsole);
- testErrors([], "wrapping a valid URL doesn't log errors");
-};
-
-exports.testBasic = function(test) {
- var urlsToLoad = [__url__,
- "data:text/html,hi"];
- var expectedLog = [__url__ + " is trusted",
- "data:text/html,hi is untrusted"];
- var wasTrustedCalled;
-
- function loadNextUrl() {
- if (urlsToLoad.length > 0) {
- tabBrowser.addTab(urlsToLoad.pop());
- } else {
- timer.setTimeout(function() { tracker.unload();
- test.done(); },
- ARB_TIMEOUT);
- }
- }
-
- var trustedFunc = tof.wrap(
- [__url__],
- function(window) {
- wasTrustedCalled = true;
- });
-
- var tracker = tabBrowser.whenContentLoaded(
- function(window) {
- wasTrustedCalled = false;
- trustedFunc(window);
- var log;
- if (wasTrustedCalled)
- log = window.location.href + " is trusted";
- else
- log = window.location.href + " is untrusted";
- test.assertEqual(log, expectedLog.pop());
- window.close();
- loadNextUrl();
- });
-
- loadNextUrl();
- test.waitUntilDone(5000);
-};
View
95 tests/test-xpi-installation.js
@@ -1,95 +0,0 @@
-var {Cu, Ci, Cc} = require("chrome");
-var errors = require("errors");
-
-const XPI_FILENAME = "simple1.xpi";
-const XPI_NAME = "simple";
-const XPI_VERSION = "1.0";
-const XPI_JID = "jid0-PZshXA4AIoK0D0dt0Cvtf9ZjBHs";
-const XPI_ID = XPI_JID + "@jetpack";
-const XPI_CONTRACT_ID = "@mozilla.org/harness-service;1?id=" + XPI_JID;
-const XPI_APP_READY_TOPIC = XPI_JID + "_APPLICATION_READY";
-
-function getDataFile(filename) {
- var myFilename = require("url").toFilename(__url__);
- var file = Cc['@mozilla.org/file/local;1']
- .createInstance(Ci.nsILocalFile);
- file.initWithPath(myFilename);
- file = file.parent.parent;
- file.append('examples');
- file.append('sample-web-page');
- file.append(filename);
- return file;
-}
-
-function getAddonManager() {
- var jsm = {};
- Cu.import("resource://gre/modules/AddonManager.jsm", jsm);
- return jsm.AddonManager;
-}
-
-function cleanseEnvironment() {
- // TODO: XPIs really shouldn't be looking at the environment
- // before checking for harness-options.json, but they
- // currently are. Here we remove the environment variable
- // so the XPI we load actually looks at its own
- // harness-options.json.
- var environ = Cc["@mozilla.org/process/environment;1"]
- .getService(Ci.nsIEnvironment);
- environ.set("HARNESS_OPTIONS", null);
-}
-
-exports.testXPI = function(test) {
- function failIfThrows(cb) {
- return errors.catchAndLog(cb, null, function(e) {
- console.exception(e);
- test.done();
- });
- }
-
- var AddonManager = getAddonManager();
- var file = getDataFile(XPI_FILENAME);
- var appReadyTopicHasFired = false;
-
- if (!file.exists()) {
- test.fail("Please run build.py to build sample XPIs needed by " +
- "this test.");
- return;
- }
-
- cleanseEnvironment();
-
- AddonManager.getInstallForFile(file, function(install) {
- test.assertEqual(install.version, XPI_VERSION,
- "Addon version is " + XPI_VERSION);
-
- test.assertEqual(install.name, XPI_NAME,
- "Addon name is " + XPI_NAME);
-
- require("observer-service").add(XPI_APP_READY_TOPIC, function() {
- appReadyTopicHasFired = true;
- });
-
- install.addListener({
- onInstallEnded: failIfThrows(function(install, addon) {
- test.assertEqual(addon.id, XPI_ID,
- "Addon id is " + XPI_ID);
- test.assert(addon.appDisabled === false,
- "Addon is not disabled");
- test.assert(addon.isActive, "Addon is currently functional");
-
- test.assert(appReadyTopicHasFired,
- "App ready topic has fired: " + XPI_APP_READY_TOPIC);
-
- var factory = require("xpcom").getClass(XPI_CONTRACT_ID);
- test.pass("Factory exists w/ contract ID " + XPI_CONTRACT_ID);
-
- var hsvc = factory.wrappedJSObject.singleton;
- test.assertEqual(hsvc.contractID, XPI_CONTRACT_ID,
- "harnessService contract ID is correct");
- test.done();
- })
- });
- install.install();
- }, null);
- test.waitUntilDone();
-};
View
43 tests/test-zip-file.js
@@ -1,43 +0,0 @@
-var zipFile = require("zip-file");
-var url = require("url");
-var shellUtils = require("shell-utils");
-var file = require("file");
-
-var mydir = file.dirname(url.toFilename(__url__));
-var fooPath = file.join(mydir, "foo.zip");
-var corruptPath = file.join(mydir, "corrupt.zip");
-
-exports.testCorrupted = function(test) {
- var zip = zipFile.open(corruptPath);
- try {
- zip.entries;
- test.fail("getting entries from corrupt zip file should fail");
- } catch (e if e.result == require("chrome").Cr.NS_ERROR_FILE_CORRUPTED) {
- test.pass("opening corrupted files throws error");
- }
-};
-
-exports.testEntries = function(test) {
- var zip = zipFile.open(fooPath);
- test.assertEqual(JSON.stringify(zip.entries),
- '["foo/bar.txt","foo/"]',
- "Entries should be readable.");
-};
-
-exports.testExtractAll = function(test) {
- var profileDir = require("directory-service").getPath("ProfD");
- var tempDir = file.join(profileDir, "test-zip-file-temp");
- if (file.exists(tempDir))
- shellUtils.removeDirRecursive(tempDir);
- test.assert(!file.exists(tempDir),
- "tempDir must not exist.");
- shellUtils.makeDir(tempDir);
-
- var zip = zipFile.open(fooPath);
- zip.extractAll(tempDir);
- test.assertEqual(file.read(file.join(tempDir, "foo", "bar.txt")),
- "This is a test file.\n",
- "Extraction of dirs and files should work.");
-
- shellUtils.removeDirRecursive(tempDir);
-};

0 comments on commit 7da28e4

Please sign in to comment.
Something went wrong with that request. Please try again.