diff options
Diffstat (limited to 'tools/addon-sdk-1.12/lib')
128 files changed, 0 insertions, 25796 deletions
diff --git a/tools/addon-sdk-1.12/lib/sdk/addon-page.js b/tools/addon-sdk-1.12/lib/sdk/addon-page.js deleted file mode 100644 index 6440b25..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/addon-page.js +++ /dev/null @@ -1,38 +0,0 @@ -/* 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'; - -module.metadata = { - 'stability': 'experimental' -}; - -const { WindowTracker } = require('./deprecated/window-utils'); -const { isXULBrowser } = require('./window/utils'); -const { add, remove } = require('./util/array'); -const { getTabs, closeTab, getURI } = require('./tabs/utils'); -const { data } = require('./self'); - -const addonURL = data.url('index.html'); - -WindowTracker({ - onTrack: function onTrack(window) { - if (isXULBrowser(window)) - add(window.XULBrowserWindow.inContentWhitelist, addonURL); - }, - onUntrack: function onUntrack(window) { - if (isXULBrowser(window)) - getTabs(window).filter(tabFilter).forEach(untrackTab.bind(null, window)); - } -}); - -function tabFilter(tab) { - return getURI(tab) === addonURL; -} - -function untrackTab(window, tab) { - // Note: `onUntrack` will be called for all windows on add-on unloads, - // so we want to clean them up from these URLs. - remove(window.XULBrowserWindow.inContentWhitelist, addonURL); - closeTab(tab); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/addon/installer.js b/tools/addon-sdk-1.12/lib/sdk/addon/installer.js deleted file mode 100644 index f72b21e..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/addon/installer.js +++ /dev/null @@ -1,106 +0,0 @@ -/* 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/. */ - -module.metadata = { - "stability": "experimental" -}; - -const { Cc, Ci, Cu } = require("chrome"); -const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm"); -const { defer } = require("../core/promise"); -const { setTimeout } = require("../timers"); - -/** - * `install` method error codes: - * - * https://developer.mozilla.org/en/Addons/Add-on_Manager/AddonManager#AddonInstall_errors - */ -exports.ERROR_NETWORK_FAILURE = AddonManager.ERROR_NETWORK_FAILURE; -exports.ERROR_INCORRECT_HASH = AddonManager.ERROR_INCORRECT_HASH; -exports.ERROR_CORRUPT_FILE = AddonManager.ERROR_CORRUPT_FILE; -exports.ERROR_FILE_ACCESS = AddonManager.ERROR_FILE_ACCESS; - -/** - * Immediatly install an addon. - * - * @param {String} xpiPath - * file path to an xpi file to install - * @return {Promise} - * A promise resolved when the addon is finally installed. - * Resolved with addon id as value or rejected with an error code. - */ -exports.install = function install(xpiPath) { - let { promise, resolve, reject } = defer(); - - // Create nsIFile for the xpi file - let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile); - try { - file.initWithPath(xpiPath); - } - catch(e) { - reject(exports.ERROR_FILE_ACCESS); - return promise; - } - - // Listen for installation end - let listener = { - onInstallEnded: function(aInstall, aAddon) { - aInstall.removeListener(listener); - // Bug 749745: on FF14+, onInstallEnded is called just before `startup()` - // is called, but we expect to resolve the promise only after it. - // As startup is called synchronously just after onInstallEnded, - // a simple setTimeout(0) is enough - setTimeout(resolve, 0, aAddon.id); - }, - onInstallFailed: function (aInstall) { - console.log("failed"); - aInstall.removeListener(listener); - reject(aInstall.error); - }, - onDownloadFailed: function(aInstall) { - this.onInstallFailed(aInstall); - } - }; - - // Order AddonManager to install the addon - AddonManager.getInstallForFile(file, function(install) { - install.addListener(listener); - install.install(); - }); - - return promise; -}; - -exports.uninstall = function uninstall(addonId) { - let { promise, resolve, reject } = defer(); - - // Listen for uninstallation end - let listener = { - onUninstalled: function onUninstalled(aAddon) { - if (aAddon.id != addonId) - return; - AddonManager.removeAddonListener(listener); - resolve(); - } - }; - AddonManager.addAddonListener(listener); - - // Order Addonmanager to uninstall the addon - AddonManager.getAddonByID(addonId, function (addon) { - addon.uninstall(); - }); - - return promise; -}; - -exports.disable = function disable(addonId) { - let { promise, resolve, reject } = defer(); - - AddonManager.getAddonByID(addonId, function (addon) { - addon.userDisabled = true; - resolve(); - }); - - return promise; -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/addon/runner.js b/tools/addon-sdk-1.12/lib/sdk/addon/runner.js deleted file mode 100644 index 0552de2..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/addon/runner.js +++ /dev/null @@ -1,153 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 expandtab */ -/* 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/. - */ - -module.metadata = { - "stability": "experimental" -}; - -const { Cc, Ci } = require('chrome'); -const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader'); -const { once } = require('../system/events'); -const { exit, env, staticArgs, name } = require('../system'); -const { when: unload } = require('../system/unload'); -const { loadReason } = require('../self'); -const { rootURI } = require("@loader/options"); -const globals = require('../system/globals'); - -const NAME2TOPIC = { - 'Firefox': 'sessionstore-windows-restored', - 'Fennec': 'sessionstore-windows-restored', - 'SeaMonkey': 'sessionstore-windows-restored', - 'Thunderbird': 'mail-startup-done', - '*': 'final-ui-startup' -}; - -// Gets the topic that fit best as application startup event, in according with -// the current application (e.g. Firefox, Fennec, Thunderbird...) -const APP_STARTUP = NAME2TOPIC[name] || NAME2TOPIC['*']; - -// Initializes default preferences -function setDefaultPrefs(prefsURI) { - const prefs = Cc['@mozilla.org/preferences-service;1']. - getService(Ci.nsIPrefService). - QueryInterface(Ci.nsIPrefBranch2); - const branch = prefs.getDefaultBranch(''); - const sandbox = Sandbox({ - name: prefsURI, - prototype: { - pref: function(key, val) { - switch (typeof val) { - case 'boolean': - branch.setBoolPref(key, val); - break; - case 'number': - if (val % 1 == 0) // number must be a integer, otherwise ignore it - branch.setIntPref(key, val); - break; - case 'string': - branch.setCharPref(key, val); - break; - } - } - } - }); - // load preferences. - evaluate(sandbox, prefsURI); -} - -function definePseudo(loader, id, exports) { - let uri = resolveURI(id, loader.mapping); - loader.modules[uri] = { exports: exports }; -} - -function wait(reason, options) { - once(APP_STARTUP, function() { - startup(null, options); - }); -} - -function startup(reason, options) { - if (reason === 'startup') - return wait(reason, options); - - // Inject globals ASAP in order to have console API working ASAP - Object.defineProperties(options.loader.globals, descriptor(globals)); - - // Load localization manifest and .properties files. - // Run the addon even in case of error (best effort approach) - require('../l10n/loader'). - load(rootURI). - then(null, function failure(error) { - console.info("Error while loading localization: " + error.message); - }). - then(function onLocalizationReady(data) { - // Exports data to a pseudo module so that api-utils/l10n/core - // can get access to it - if (data) - definePseudo(options.loader, '@l10n/data', data); - run(options); - }); -} - -function run(options) { - try { - // Try initializing HTML localization before running main module. Just print - // an exception in case of error, instead of preventing addon to be run. - try { - // Do not enable HTML localization while running test as it is hard to - // disable. Because unit tests are evaluated in a another Loader who - // doesn't have access to this current loader. - if (options.main !== 'test-harness/run-tests') - require('../l10n/html').enable(); - } - catch(error) { - console.exception(error); - } - // Initialize inline options localization, without preventing addon to be - // run in case of error - try { - require('../l10n/prefs'); - } - catch(error) { - console.exception(error); - } - - // TODO: When bug 564675 is implemented this will no longer be needed - // Always set the default prefs, because they disappear on restart - setDefaultPrefs(options.prefsURI); - - // this is where the addon's main.js finally run. - let program = main(options.loader, options.main); - - if (typeof(program.onUnload) === 'function') - unload(program.onUnload); - - if (typeof(program.main) === 'function') { - - program.main({ - loadReason: loadReason, - staticArgs: staticArgs - }, { - print: function print(_) { dump(_ + '\n') }, - quit: exit - }); - } - } catch (error) { - console.exception(error); - throw error; - } -} -exports.startup = startup; - -// If add-on is lunched via `cfx run` we need to use `system.exit` to let -// cfx know we're done (`cfx test` will take care of exit so we don't do -// anything here). -if (env.CFX_COMMAND === 'run') { - unload(function(reason) { - if (reason === 'shutdown') - exit(0); - }); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/base64.js b/tools/addon-sdk-1.12/lib/sdk/base64.js deleted file mode 100644 index 42fea57..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/base64.js +++ /dev/null @@ -1,41 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Cu } = require("chrome"); - -// If an object is not given as second argument, the JavaScript Module scope is -// returned, so we can obtain from it the `atob` and `btoa` functions -const { atob, btoa } = Cu.import("resource://gre/modules/Services.jsm"); - -function isUTF8(charset) { - let type = typeof charset; - - if (type === "undefined") - return false; - - if (type === "string" && charset.toLowerCase() === "utf-8") - return true; - - throw new Error("The charset argument can be only 'utf-8'"); -} - -exports.decode = function (data, charset) { - if (isUTF8(charset)) - return decodeURIComponent(escape(atob(data))) - - return atob(data); -} - -exports.encode = function (data, charset) { - if (isUTF8(charset)) - return btoa(unescape(encodeURIComponent(data))) - - return btoa(data); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/clipboard.js b/tools/addon-sdk-1.12/lib/sdk/clipboard.js deleted file mode 100644 index 58a3d7e..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/clipboard.js +++ /dev/null @@ -1,333 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "stable" -}; - -const { Cc, Ci } = require("chrome"); -const { DataURL } = require("./url"); -const errors = require("./deprecated/errors"); -const apiUtils = require("./deprecated/api-utils"); -/* -While these data flavors resemble Internet media types, they do -no directly map to them. -*/ -const kAllowableFlavors = [ - "text/unicode", - "text/html", - "image/png" - /* CURRENTLY UNSUPPORTED FLAVORS - "text/plain", - "image/jpg", - "image/jpeg", - "image/gif", - "text/x-moz-text-internal", - "AOLMAIL", - "application/x-moz-file", - "text/x-moz-url", - "text/x-moz-url-data", - "text/x-moz-url-desc", - "text/x-moz-url-priv", - "application/x-moz-nativeimage", - "application/x-moz-nativehtml", - "application/x-moz-file-promise-url", - "application/x-moz-file-promise-dest-filename", - "application/x-moz-file-promise", - "application/x-moz-file-promise-dir" - */ -]; - -/* -Aliases for common flavors. Not all flavors will -get an alias. New aliases must be approved by a -Jetpack API druid. -*/ -const kFlavorMap = [ - { short: "text", long: "text/unicode" }, - { short: "html", long: "text/html" }, - { short: "image", long: "image/png" } -]; - -let clipboardService = Cc["@mozilla.org/widget/clipboard;1"]. - getService(Ci.nsIClipboard); - -let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"]. - getService(Ci.nsIClipboardHelper); - -let imageTools = Cc["@mozilla.org/image/tools;1"]. - getService(Ci.imgITools); - -exports.set = function(aData, aDataType) { - - let options = { - data: aData, - datatype: aDataType || "text" - }; - - // If `aDataType` is not given or if it's "image", the data is parsed as - // data URL to detect a better datatype - if (aData && (!aDataType || aDataType === "image")) { - try { - let dataURL = new DataURL(aData); - - options.datatype = dataURL.mimeType; - options.data = dataURL.data; - } - catch (e if e.name === "URIError") { - // Not a valid Data URL - } - } - - options = apiUtils.validateOptions(options, { - data: { - is: ["string"] - }, - datatype: { - is: ["string"] - } - }); - - let flavor = fromJetpackFlavor(options.datatype); - - if (!flavor) - throw new Error("Invalid flavor for " + options.datatype); - - // Additional checks for using the simple case - if (flavor == "text/unicode") { - clipboardHelper.copyString(options.data); - return true; - } - - // Below are the more complex cases where we actually have to work with a - // nsITransferable object - var xferable = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - if (!xferable) - throw new Error("Couldn't set the clipboard due to an internal error " + - "(couldn't create a Transferable object)."); - // Bug 769440: Starting with FF16, transferable have to be inited - if ("init" in xferable) - xferable.init(null); - - switch (flavor) { - case "text/html": - // add text/html flavor - let (str = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString)) - { - str.data = options.data; - xferable.addDataFlavor(flavor); - xferable.setTransferData(flavor, str, str.data.length * 2); - } - - // add a text/unicode flavor (html converted to plain text) - let (str = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString), - converter = Cc["@mozilla.org/feed-textconstruct;1"]. - createInstance(Ci.nsIFeedTextConstruct)) - { - converter.type = "html"; - converter.text = options.data; - str.data = converter.plainText(); - xferable.addDataFlavor("text/unicode"); - xferable.setTransferData("text/unicode", str, str.data.length * 2); - } - break; - - // Set images to the clipboard is not straightforward, to have an idea how - // it works on platform side, see: - // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsCopySupport.cpp?rev=7857c5bff017#530 - case "image/png": - let image = options.data; - - let container = {}; - - try { - let input = Cc["@mozilla.org/io/string-input-stream;1"]. - createInstance(Ci.nsIStringInputStream); - - input.setData(image, image.length); - - imageTools.decodeImageData(input, flavor, container); - } - catch (e) { - throw new Error("Unable to decode data given in a valid image."); - } - - // Store directly the input stream makes the cliboard's data available - // for Firefox but not to the others application or to the OS. Therefore, - // a `nsISupportsInterfacePointer` object that reference an `imgIContainer` - // with the image is needed. - var imgPtr = Cc["@mozilla.org/supports-interface-pointer;1"]. - createInstance(Ci.nsISupportsInterfacePointer); - - imgPtr.data = container.value; - - xferable.addDataFlavor(flavor); - xferable.setTransferData(flavor, imgPtr, -1); - - break; - default: - throw new Error("Unable to handle the flavor " + flavor + "."); - } - - // TODO: Not sure if this will ever actually throw. -zpao - try { - clipboardService.setData( - xferable, - null, - clipboardService.kGlobalClipboard - ); - } catch (e) { - throw new Error("Couldn't set clipboard data due to an internal error: " + e); - } - return true; -}; - - -exports.get = function(aDataType) { - let options = { - datatype: aDataType - }; - - // Figure out the best data type for the clipboard's data, if omitted - if (!aDataType) { - if (~currentFlavors().indexOf("image")) - options.datatype = "image"; - else - options.datatype = "text"; - } - - options = apiUtils.validateOptions(options, { - datatype: { - is: ["string"] - } - }); - - var xferable = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - if (!xferable) - throw new Error("Couldn't set the clipboard due to an internal error " + - "(couldn't create a Transferable object)."); - // Bug 769440: Starting with FF16, transferable have to be inited - if ("init" in xferable) - xferable.init(null); - - var flavor = fromJetpackFlavor(options.datatype); - - // Ensure that the user hasn't requested a flavor that we don't support. - if (!flavor) - throw new Error("Getting the clipboard with the flavor '" + flavor + - "' is not supported."); - - // TODO: Check for matching flavor first? Probably not worth it. - - xferable.addDataFlavor(flavor); - // Get the data into our transferable. - clipboardService.getData( - xferable, - clipboardService.kGlobalClipboard - ); - - var data = {}; - var dataLen = {}; - try { - xferable.getTransferData(flavor, data, dataLen); - } catch (e) { - // Clipboard doesn't contain data in flavor, return null. - return null; - } - - // There's no data available, return. - if (data.value === null) - return null; - - // TODO: Add flavors here as we support more in kAllowableFlavors. - switch (flavor) { - case "text/unicode": - case "text/html": - data = data.value.QueryInterface(Ci.nsISupportsString).data; - break; - case "image/png": - let dataURL = new DataURL(); - - dataURL.mimeType = flavor; - dataURL.base64 = true; - - let image = data.value; - - // Due to the differences in how images could be stored in the clipboard - // the checks below are needed. The clipboard could already provide the - // image as byte streams, but also as pointer, or as image container. - // If it's not possible obtain a byte stream, the function returns `null`. - if (image instanceof Ci.nsISupportsInterfacePointer) - image = image.data; - - if (image instanceof Ci.imgIContainer) - image = imageTools.encodeImage(image, flavor); - - if (image instanceof Ci.nsIInputStream) { - let binaryStream = Cc["@mozilla.org/binaryinputstream;1"]. - createInstance(Ci.nsIBinaryInputStream); - - binaryStream.setInputStream(image); - - dataURL.data = binaryStream.readBytes(binaryStream.available()); - - data = dataURL.toString(); - } - else - data = null; - - break; - default: - data = null; - } - - return data; -}; - -function currentFlavors() { - // Loop over kAllowableFlavors, calling hasDataMatchingFlavors for each. - // This doesn't seem like the most efficient way, but we can't get - // confirmation for specific flavors any other way. This is supposed to be - // an inexpensive call, so performance shouldn't be impacted (much). - var currentFlavors = []; - for each (var flavor in kAllowableFlavors) { - var matches = clipboardService.hasDataMatchingFlavors( - [flavor], - 1, - clipboardService.kGlobalClipboard - ); - if (matches) - currentFlavors.push(toJetpackFlavor(flavor)); - } - return currentFlavors; -}; - -Object.defineProperty(exports, "currentFlavors", { get : currentFlavors }); - -// SUPPORT FUNCTIONS //////////////////////////////////////////////////////// - -function toJetpackFlavor(aFlavor) { - for each (let flavorMap in kFlavorMap) - if (flavorMap.long == aFlavor) - return flavorMap.short; - // Return null in the case where we don't match - return null; -} - -function fromJetpackFlavor(aJetpackFlavor) { - // TODO: Handle proper flavors better - for each (let flavorMap in kFlavorMap) - if (flavorMap.short == aJetpackFlavor || flavorMap.long == aJetpackFlavor) - return flavorMap.long; - // Return null in the case where we don't match. - return null; -} diff --git a/tools/addon-sdk-1.12/lib/sdk/console/plain-text.js b/tools/addon-sdk-1.12/lib/sdk/console/plain-text.js deleted file mode 100644 index 9e3b748..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/console/plain-text.js +++ /dev/null @@ -1,88 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const {Cc,Ci} = require("chrome"); -const self = require("../self"); - -function stringify(arg) { - try { - return String(arg); - } - catch(ex) { - return "<toString() error>"; - } -} - -function stringifyArgs(args) { - return Array.map(args, stringify).join(" "); -} - -function message(print, level, args, name) { - print(level + ": " + name + ": " + - stringifyArgs(args) + "\n", level); -} - -var Console = exports.PlainTextConsole = function PlainTextConsole(print) { - if (!print) - print = dump; - if (print === dump) { - // If we're just using dump(), auto-enable preferences so - // that the developer actually sees the console output. - var prefs = Cc["@mozilla.org/preferences-service;1"] - .getService(Ci.nsIPrefBranch); - prefs.setBoolPref("browser.dom.window.dump.enabled", true); - } - this.print = print; - - // Binding all the public methods to an instance so that they can be used - // as callback / listener functions straightaway. - this.log = this.log.bind(this); - this.info = this.info.bind(this); - this.warn = this.warn.bind(this); - this.error = this.error.bind(this); - this.debug = this.debug.bind(this); - this.exception = this.exception.bind(this); - this.trace = this.trace.bind(this); -}; - -Console.prototype = { - log: function log() { - message(this.print, "info", arguments, self.name); - }, - - info: function info() { - message(this.print, "info", arguments, self.name); - }, - - warn: function warn() { - message(this.print, "warning", arguments, self.name); - }, - - error: function error() { - message(this.print, "error", arguments, self.name); - }, - - debug: function debug() { - message(this.print, "debug", arguments, self.name); - }, - - exception: function exception(e) { - var fullString = ("An exception occurred.\n" + - require("./traceback").format(e) + "\n" + e); - this.error(fullString); - }, - - trace: function trace() { - var traceback = require("./traceback"); - var stack = traceback.get(); - stack.splice(-1, 1); - message(this.print, "info", [traceback.format(stack)], self.name); - } -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/console/traceback.js b/tools/addon-sdk-1.12/lib/sdk/console/traceback.js deleted file mode 100644 index 6b23be1..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/console/traceback.js +++ /dev/null @@ -1,118 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "experimental" -}; - -const { Cc, Ci, components } = require("chrome"); -const { readURISync } = require("../net/url"); - -// Undo the auto-parentification of URLs done in bug 418356. -function deParentifyURL(url) { - return url ? url.split(" -> ").slice(-1)[0] : url; -} - -function safeGetFileLine(path, line) { - try { - var scheme = require("../url").URL(path).scheme; - // TODO: There should be an easier, more accurate way to figure out - // what's the case here. - if (!(scheme == "http" || scheme == "https")) - return readURISync(path).split("\n")[line - 1]; - } catch (e) {} - return null; -} - -function errorStackToJSON(stack) { - var lines = stack.split("\n"); - - var frames = []; - lines.forEach( - function(line) { - if (!line) - return; - var atIndex = line.indexOf("@"); - var colonIndex = line.lastIndexOf(":"); - var filename = deParentifyURL(line.slice(atIndex + 1, colonIndex)); - var lineNo = parseInt(line.slice(colonIndex + 1)); - var funcSig = line.slice(0, atIndex); - var endFuncName = funcSig.indexOf("("); - // Bug 751149: FF15 changed function signature - // Instead of: runTest([object Object]) - // We now have: runTest - var funcName = endFuncName != -1 - ? funcSig.slice(0, endFuncName) - : funcSig; - frames.unshift({filename: filename, - funcName: funcName, - lineNo: lineNo}); - }); - - return frames; -}; - -function nsIStackFramesToJSON(frame) { - var stack = []; - - while (frame) { - if (frame.filename) { - var filename = deParentifyURL(frame.filename); - stack.splice(0, 0, {filename: filename, - lineNo: frame.lineNumber, - funcName: frame.name}); - } - frame = frame.caller; - } - - return stack; -}; - -var fromException = exports.fromException = function fromException(e) { - if (e instanceof Ci.nsIException) - return nsIStackFramesToJSON(e.location); - if (e.stack && e.stack.length) - return errorStackToJSON(e.stack); - if (e.fileName && typeof(e.lineNumber == "number")) - return [{filename: deParentifyURL(e.fileName), - lineNo: e.lineNumber, - funcName: null}]; - return []; -}; - -var get = exports.get = function get() { - return nsIStackFramesToJSON(components.stack.caller); -}; - -var format = exports.format = function format(tbOrException) { - if (tbOrException === undefined) { - tbOrException = get(); - tbOrException.splice(-1, 1); - } - - var tb; - if (typeof(tbOrException) == "object" && - tbOrException.constructor.name == "Array") - tb = tbOrException; - else - tb = fromException(tbOrException); - - var lines = ["Traceback (most recent call last):"]; - - tb.forEach( - function(frame) { - if (!(frame.filename || frame.lineNo || frame.funcName)) - return; - - lines.push(' File "' + frame.filename + '", line ' + - frame.lineNo + ', in ' + frame.funcName); - var sourceLine = safeGetFileLine(frame.filename, frame.lineNo); - if (sourceLine) - lines.push(' ' + sourceLine.trim()); - }); - - return lines.join("\n"); -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/content/content-proxy.js b/tools/addon-sdk-1.12/lib/sdk/content/content-proxy.js deleted file mode 100644 index 76df7f6..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/content/content-proxy.js +++ /dev/null @@ -1,870 +0,0 @@ -/* 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"; - -/* Trick the linker in order to avoid error on `Components.interfaces` usage. - We are tricking the linker with `require('./content-proxy.js')` from - worjer.js in order to ensure shipping this file! But then the linker think - that this file is going to be used as a CommonJS module where we forbid usage - of `Components`. -*/ -let Ci = Components['interfaces']; - -/** - * Access key that allows privileged code to unwrap proxy wrappers through - * valueOf: - * let xpcWrapper = proxyWrapper.valueOf(UNWRAP_ACCESS_KEY); - * This key should only be used by proxy unit test. - */ - const UNWRAP_ACCESS_KEY = {}; - - - /** - * Returns a closure that wraps arguments before calling the given function, - * which can be given to native functions that accept a function, such that when - * the closure is called, the given function is called with wrapped arguments. - * - * @param fun {Function} - * the function for which to create a closure wrapping its arguments - * @param obj {Object} - * target object from which `fun` comes from - * (optional, for debugging purpose) - * @param name {String} - * name of the attribute from which `fun` is binded on `obj` - * (optional, for debugging purpose) - * - * Example: - * function contentScriptListener(event) {} - * let wrapper = ContentScriptFunctionWrapper(contentScriptListener); - * xray.addEventListener("...", wrapper, false); - * -> Allow to `event` to be wrapped - */ -function ContentScriptFunctionWrapper(fun, obj, name) { - if ("___proxy" in fun && typeof fun.___proxy == "function") - return fun.___proxy; - - let wrappedFun = function () { - let args = []; - for (let i = 0, l = arguments.length; i < l; i++) - args.push(wrap(arguments[i])); - - //console.log("Called from native :"+obj+"."+name); - //console.log(">args "+arguments.length); - //console.log(fun); - - // Native code can execute this callback with `this` being the wrapped - // function. For example, window.mozRequestAnimationFrame. - if (this == wrappedFun) - return fun.apply(fun, args); - - return fun.apply(wrap(this), args); - }; - - Object.defineProperty(fun, "___proxy", {value : wrappedFun, - writable : false, - enumerable : false, - configurable : false}); - - return wrappedFun; -} - -/** - * Returns a closure that unwraps arguments before calling the `fun` function, - * which can be used to build a wrapper for a native function that accepts - * wrapped arguments, since native function only accept unwrapped arguments. - * - * @param fun {Function} - * the function to wrap - * @param originalObject {Object} - * target object from which `fun` comes from - * (optional, for debugging purpose) - * @param name {String} - * name of the attribute from which `fun` is binded on `originalObject` - * (optional, for debugging purpose) - * - * Example: - * wrapper.appendChild = NativeFunctionWrapper(xray.appendChild, xray); - * wrapper.appendChild(anotherWrapper); - * -> Allow to call xray.appendChild with unwrapped version of anotherWrapper - */ -function NativeFunctionWrapper(fun, originalObject, name) { - return function () { - let args = []; - let obj = this && typeof this.valueOf == "function" ? - this.valueOf(UNWRAP_ACCESS_KEY) : this; - - for (let i = 0, l = arguments.length; i < l; i++) - args.push( unwrap(arguments[i], obj, name) ); - - //if (name != "toString") - //console.log(">>calling native ["+(name?name:'#closure#')+"]: \n"+fun.apply+"\n"+obj+"\n("+args.join(', ')+")\nthis :"+obj+"from:"+originalObject+"\n"); - - // Need to use Function.prototype.apply.apply because XMLHttpRequest - // is a function (typeof return 'function') and fun.apply is null :/ - let unwrapResult = Function.prototype.apply.apply(fun, [obj, args]); - let result = wrap(unwrapResult, obj, name); - - //console.log("<< "+rr+" -> "+r); - - return result; - }; -} - -/* - * Unwrap a JS value that comes from the content script. - * Mainly converts proxy wrapper to XPCNativeWrapper. - */ -function unwrap(value, obj, name) { - //console.log("unwrap : "+value+" ("+name+")"); - if (!value) - return value; - let type = typeof value; - - // In case of proxy, unwrap them recursively - // (it should not be recursive, just in case of) - if (["object", "function"].indexOf(type) !== -1 && - "__isWrappedProxy" in value) { - while("__isWrappedProxy" in value) - value = value.valueOf(UNWRAP_ACCESS_KEY); - return value; - } - - // In case of functions we need to return a wrapper that converts native - // arguments applied to this function into proxies. - if (type == "function") - return ContentScriptFunctionWrapper(value, obj, name); - - // We must wrap objects coming from content script too, as they may have - // a function that will be called by a native method. - // For example: - // addEventListener(..., { handleEvent: function(event) {} }, ...); - if (type == "object") - return ContentScriptObjectWrapper(value); - - if (["string", "number", "boolean"].indexOf(type) !== -1) - return value; - //console.log("return non-wrapped to native : "+typeof value+" -- "+value); - return value; -} - -/** - * Returns an XrayWrapper proxy object that allow to wrap any of its function - * though `ContentScriptFunctionWrapper`. These proxies are given to - * XrayWrappers in order to automatically wrap values when they call a method - * of these proxies. So that they are only used internaly and content script, - * nor web page have ever access to them. As a conclusion, we can consider - * this code as being safe regarding web pages overload. - * - * - * @param obj {Object} - * object coming from content script context to wrap - * - * Example: - * let myListener = { handleEvent: function (event) {} }; - * node.addEventListener("click", myListener, false); - * `event` has to be wrapped, so handleEvent has to be wrapped using - * `ContentScriptFunctionWrapper` function. - * In order to do so, we build this new kind of proxies. - */ -function ContentScriptObjectWrapper(obj) { - if ("___proxy" in obj && typeof obj.___proxy == "object") - return obj.___proxy; - - function valueOf(key) { - if (key === UNWRAP_ACCESS_KEY) - return obj; - return this; - } - - let proxy = Proxy.create({ - // Fundamental traps - getPropertyDescriptor: function(name) { - return Object.getOwnPropertyDescriptor(obj, name); - }, - defineProperty: function(name, desc) { - return Object.defineProperty(obj, name, desc); - }, - getOwnPropertyNames: function () { - return Object.getOwnPropertyNames(obj); - }, - delete: function(name) { - return delete obj[name]; - }, - // derived traps - has: function(name) { - return name === "__isXrayWrapperProxy" || - name in obj; - }, - hasOwn: function(name) { - return Object.prototype.hasOwnProperty.call(obj, name); - }, - get: function(receiver, name) { - if (name == "valueOf") - return valueOf; - let value = obj[name]; - if (!value) - return value; - - return unwrap(value); - }, - set: function(receiver, name, val) { - obj[name] = val; - return true; - }, - enumerate: function() { - var result = []; - for each (let name in obj) { - result.push(name); - }; - return result; - }, - keys: function() { - return Object.keys(obj); - } - }); - - Object.defineProperty(obj, "___proxy", {value : proxy, - writable : false, - enumerable : false, - configurable : false}); - - return proxy; -} - -// List of all existing typed arrays. -// Can be found here: -// http://mxr.mozilla.org/mozilla-central/source/js/src/jsapi.cpp#1790 -const typedArraysCtor = [ - ArrayBuffer, - Int8Array, - Uint8Array, - Int16Array, - Uint16Array, - Int32Array, - Uint32Array, - Float32Array, - Float64Array, - Uint8ClampedArray -]; - -/* - * Wrap a JS value coming from the document by building a proxy wrapper. - */ -function wrap(value, obj, name, debug) { - if (!value) - return value; - let type = typeof value; - if (type == "object") { - // Bug 671016: Typed arrays don't need to be proxified. - // We avoid checking the whole constructor list on all objects - // by doing this check only on non-extensible objects: - if (!Object.isExtensible(value) && - typedArraysCtor.indexOf(value.constructor) !== -1) - return value; - - // Bug 715755: do not proxify COW wrappers - // These wrappers throw an exception when trying to access - // any attribute that is not in a white list - try { - ("nonExistantAttribute" in value); - } - catch(e) { - if (e.message.indexOf("Permission denied to access property") !== -1) - return value; - } - - // We may have a XrayWrapper proxy. - // For example: - // let myListener = { handleEvent: function () {} }; - // node.addEventListener("click", myListener, false); - // When native code want to call handleEvent, - // we go though ContentScriptFunctionWrapper that calls `wrap(this)` - // `this` is the XrayWrapper proxy of myListener. - // We return this object without building a CS proxy as it is already - // a value coming from the CS. - if ("__isXrayWrapperProxy" in value) - return value.valueOf(UNWRAP_ACCESS_KEY); - - // Unwrap object before wrapping it. - // It should not happen with CS proxy objects. - while("__isWrappedProxy" in value) { - value = value.valueOf(UNWRAP_ACCESS_KEY); - } - - if (XPCNativeWrapper.unwrap(value) !== value) - return getProxyForObject(value); - // In case of Event, HTMLCollection or NodeList or ??? - // XPCNativeWrapper.unwrap(value) === value - // but it's still a XrayWrapper so let's build a proxy - return getProxyForObject(value); - } - if (type == "function") { - if (XPCNativeWrapper.unwrap(value) !== value - || (typeof value.toString === "function" && - value.toString().match(/\[native code\]/))) { - return getProxyForFunction(value, NativeFunctionWrapper(value, obj, name)); - } - return value; - } - if (type == "string") - return value; - if (type == "number") - return value; - if (type == "boolean") - return value; - //console.log("return non-wrapped to wrapped : "+value); - return value; -} - -/* - * Wrap an object from the document to a proxy wrapper - */ -function getProxyForObject(obj) { - if (typeof obj != "object") { - let msg = "tried to proxify something other than an object: " + typeof obj; - console.warn(msg); - throw msg; - } - if ("__isWrappedProxy" in obj) { - return obj; - } - // Check if there is a proxy cached on this wrapper, - // but take care of prototype ___proxy attribute inheritance! - if (obj && obj.___proxy && obj.___proxy.valueOf(UNWRAP_ACCESS_KEY) === obj) { - return obj.___proxy; - } - - let proxy = Proxy.create(handlerMaker(obj)); - - Object.defineProperty(obj, "___proxy", {value : proxy, - writable : false, - enumerable : false, - configurable : false}); - return proxy; -} - -/* - * Wrap a function from the document to a proxy wrapper - */ -function getProxyForFunction(fun, callTrap) { - if (typeof fun != "function") { - let msg = "tried to proxify something other than a function: " + typeof fun; - console.warn(msg); - throw msg; - } - if ("__isWrappedProxy" in fun) - return obj; - if ("___proxy" in fun) - return fun.___proxy; - - let proxy = Proxy.createFunction(handlerMaker(fun), callTrap); - - Object.defineProperty(fun, "___proxy", {value : proxy, - writable : false, - enumerable : false, - configurable : false}); - - return proxy; -} - -/* - * Check if a DOM attribute name is an event name. - */ -function isEventName(id) { - if (id.indexOf("on") != 0 || id.length == 2) - return false; - // Taken from: - // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#7616 - switch (id[2]) { - case 'a' : - return (id == "onabort" || - id == "onafterscriptexecute" || - id == "onafterprint"); - case 'b' : - return (id == "onbeforeunload" || - id == "onbeforescriptexecute" || - id == "onblur" || - id == "onbeforeprint"); - case 'c' : - return (id == "onchange" || - id == "onclick" || - id == "oncontextmenu" || - id == "oncopy" || - id == "oncut" || - id == "oncanplay" || - id == "oncanplaythrough"); - case 'd' : - return (id == "ondblclick" || - id == "ondrag" || - id == "ondragend" || - id == "ondragenter" || - id == "ondragleave" || - id == "ondragover" || - id == "ondragstart" || - id == "ondrop" || - id == "ondurationchange"); - case 'e' : - return (id == "onerror" || - id == "onemptied" || - id == "onended"); - case 'f' : - return id == "onfocus"; - case 'h' : - return id == "onhashchange"; - case 'i' : - return (id == "oninput" || - id == "oninvalid"); - case 'k' : - return (id == "onkeydown" || - id == "onkeypress" || - id == "onkeyup"); - case 'l' : - return (id == "onload" || - id == "onloadeddata" || - id == "onloadedmetadata" || - id == "onloadstart"); - case 'm' : - return (id == "onmousemove" || - id == "onmouseout" || - id == "onmouseover" || - id == "onmouseup" || - id == "onmousedown" || - id == "onmessage"); - case 'p' : - return (id == "onpaint" || - id == "onpageshow" || - id == "onpagehide" || - id == "onpaste" || - id == "onpopstate" || - id == "onpause" || - id == "onplay" || - id == "onplaying" || - id == "onprogress"); - case 'r' : - return (id == "onreadystatechange" || - id == "onreset" || - id == "onresize" || - id == "onratechange"); - case 's' : - return (id == "onscroll" || - id == "onselect" || - id == "onsubmit" || - id == "onseeked" || - id == "onseeking" || - id == "onstalled" || - id == "onsuspend"); - case 't': - return id == "ontimeupdate" - /* - // TODO: Make it work for mobile version - || - (nsDOMTouchEvent::PrefEnabled() && - (id == "ontouchstart" || - id == "ontouchend" || - id == "ontouchmove" || - id == "ontouchenter" || - id == "ontouchleave" || - id == "ontouchcancel"))*/; - - case 'u' : - return id == "onunload"; - case 'v': - return id == "onvolumechange"; - case 'w': - return id == "onwaiting"; - } - - return false; -} - -// XrayWrappers miss some attributes. -// Here is a list of functions that return a value when it detects a miss: -const NODES_INDEXED_BY_NAME = ["IMG", "FORM", "APPLET", "EMBED", "OBJECT"]; -const xRayWrappersMissFixes = [ - - // Fix bug with XPCNativeWrapper on HTMLCollection - // We can only access array item once, then it's undefined :o - function (obj, name) { - let i = parseInt(name); - if (obj.toString().match(/HTMLCollection|NodeList/) && - i >= 0 && i < obj.length) { - return wrap(XPCNativeWrapper(obj.wrappedJSObject[name]), obj, name); - } - return null; - }, - - // Trap access to document["form name"] - // that may refer to an existing form node - // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#9285 - function (obj, name) { - if ("nodeType" in obj && obj.nodeType == 9) { - let node = obj.wrappedJSObject[name]; - // List of supported tag: - // http://mxr.mozilla.org/mozilla-central/source/content/html/content/src/nsGenericHTMLElement.cpp#1267 - if (node && NODES_INDEXED_BY_NAME.indexOf(node.tagName) != -1) - return wrap(XPCNativeWrapper(node)); - } - return null; - }, - - // Trap access to window["frame name"] and window.frames[i] - // that refer to an (i)frame internal window object - // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#6824 - function (obj, name) { - if (typeof obj == "object" && "document" in obj) { - // Ensure that we are on a window object - try { - obj.QueryInterface(Ci.nsIDOMWindow); - } - catch(e) { - return null; - } - - // Integer case: - let i = parseInt(name); - if (i >= 0 && i in obj) { - return wrap(XPCNativeWrapper(obj[i])); - } - - // String name case: - if (name in obj.wrappedJSObject) { - let win = obj.wrappedJSObject[name]; - let nodes = obj.document.getElementsByName(name); - for (let i = 0, l = nodes.length; i < l; i++) { - let node = nodes[i]; - if ("contentWindow" in node && node.contentWindow == win) - return wrap(node.contentWindow); - } - } - } - return null; - }, - - // Trap access to form["node name"] - // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#9477 - function (obj, name) { - if (typeof obj == "object" && "tagName" in obj && obj.tagName == "FORM") { - let match = obj.wrappedJSObject[name]; - let nodes = obj.ownerDocument.getElementsByName(name); - for (let i = 0, l = nodes.length; i < l; i++) { - let node = nodes[i]; - if (node == match) - return wrap(node); - } - } - return null; - } - -]; - -// XrayWrappers have some buggy methods. -// Here is the list of them with functions returning some replacement -// for a given object `obj`: -const xRayWrappersMethodsFixes = { - // postMessage method is checking the Javascript global - // and it expects it to be a window object. - // But in our case, the global object is our sandbox global object. - // See nsGlobalWindow::CallerInnerWindow(): - // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsGlobalWindow.cpp#5893 - // nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper); - // win is null - postMessage: function (obj) { - // Ensure that we are on a window object - try { - obj.QueryInterface(Ci.nsIDOMWindow); - } - catch(e) { - return null; - } - - // Create a wrapper that is going to call `postMessage` through `eval` - let f = function postMessage(message, targetOrigin) { - let jscode = "this.postMessage("; - if (typeof message != "string") - jscode += JSON.stringify(message); - else - jscode += "'" + message.toString().replace(/['\\]/g,"\\$&") + "'"; - - targetOrigin = targetOrigin.toString().replace(/['\\]/g,"\\$&"); - - jscode += ", '" + targetOrigin + "')"; - return this.wrappedJSObject.eval(jscode); - }; - return getProxyForFunction(f, NativeFunctionWrapper(f)); - }, - - // Fix mozMatchesSelector uses that is broken on XrayWrappers - // when we use document.documentElement.mozMatchesSelector.call(node, expr) - // It's only working if we call mozMatchesSelector on the node itself. - // SEE BUG 658909: mozMatchesSelector returns incorrect results with XrayWrappers - mozMatchesSelector: function (obj) { - // Ensure that we are on an object to expose this buggy method - try { - // Bug 707576 removed nsIDOMNSElement. - // Can be simplified as soon as Firefox 11 become the minversion - obj.QueryInterface("nsIDOMElement" in Ci ? Ci.nsIDOMElement : - Ci.nsIDOMNSElement); - } - catch(e) { - return null; - } - // We can't use `wrap` function as `f` is not a native function, - // so wrap it manually: - let f = function mozMatchesSelector(selectors) { - return this.mozMatchesSelector(selectors); - }; - - return getProxyForFunction(f, NativeFunctionWrapper(f)); - }, - - // Bug 679054: History API doesn't work with Proxy objects. We have to pass - // regular JS objects on `pushState` and `replaceState` methods. - // In addition, the first argument has to come from the same compartment. - pushState: function (obj) { - // Ensure that we are on an object that expose History API - try { - obj.QueryInterface(Ci.nsIDOMHistory); - } - catch(e) { - return null; - } - let f = function fix() { - // Call native method with JSON objects - // (need to convert `arguments` to an array via `slice`) - return this.pushState.apply(this, JSON.parse(JSON.stringify(Array.slice(arguments)))); - }; - - return getProxyForFunction(f, NativeFunctionWrapper(f)); - }, - replaceState: function (obj) { - // Ensure that we are on an object that expose History API - try { - obj.QueryInterface(Ci.nsIDOMHistory); - } - catch(e) { - return null; - } - let f = function fix() { - // Call native method with JSON objects - // (need to convert `arguments` to an array via `slice`) - return this.replaceState.apply(this, JSON.parse(JSON.stringify(Array.slice(arguments)))); - }; - - return getProxyForFunction(f, NativeFunctionWrapper(f)); - }, - - // Bug 769006: nsIDOMMutationObserver.observe fails with proxy as options - // attributes - observe: function observe(obj) { - // Ensure that we are on a DOMMutation object - try { - // nsIDOMMutationObserver starts with FF14 - if ("nsIDOMMutationObserver" in Ci) - obj.QueryInterface(Ci.nsIDOMMutationObserver); - else - return null; - } - catch(e) { - return null; - } - return function nsIDOMMutationObserverObserveFix(target, options) { - // Gets native/unwrapped this - let self = this && typeof this.valueOf == "function" ? - this.valueOf(UNWRAP_ACCESS_KEY) : this; - // Unwrap the xraywrapper target out of JS proxy - let targetXray = unwrap(target); - // But do not wrap `options` through ContentScriptObjectWrapper - let result = wrap(self.observe(targetXray, options)); - // Finally wrap result into JS proxies - return wrap(result); - }; - } -}; - -/* - * Generate handler for proxy wrapper - */ -function handlerMaker(obj) { - // Overloaded attributes dictionary - let overload = {}; - // Expando attributes dictionary (i.e. onclick, onfocus, on* ...) - let expando = {}; - // Cache of methods overloaded to fix XrayWrapper bug - let methodFixes = {}; - return { - // Fundamental traps - getPropertyDescriptor: function(name) { - return Object.getOwnPropertyDescriptor(obj, name); - }, - defineProperty: function(name, desc) { - return Object.defineProperty(obj, name, desc); - }, - getOwnPropertyNames: function () { - return Object.getOwnPropertyNames(obj); - }, - delete: function(name) { - delete expando[name]; - delete overload[name]; - return delete obj[name]; - }, - - // derived traps - has: function(name) { - if (name == "___proxy") return false; - if (isEventName(name)) { - // XrayWrappers throw exception when we try to access expando attributes - // even on "name in wrapper". So avoid doing it! - return name in expando; - } - return name in obj || name in overload || name == "__isWrappedProxy" || - undefined !== this.get(null, name); - }, - hasOwn: function(name) { - return Object.prototype.hasOwnProperty.call(obj, name); - }, - get: function(receiver, name) { - if (name == "___proxy") - return undefined; - - // Overload toString in order to avoid returning "[XrayWrapper [object HTMLElement]]" - // or "[object Function]" for function's Proxy - if (name == "toString") { - // Bug 714778: we should not pass obj.wrappedJSObject.toString - // in order to avoid sharing its proxy between two contents scripts. - // (not that `unwrappedObj` can be equal to `obj` when `obj` isn't - // an xraywrapper) - let unwrappedObj = XPCNativeWrapper.unwrap(obj); - return wrap(function () { - return unwrappedObj.toString.call( - this.valueOf(UNWRAP_ACCESS_KEY), arguments); - }, obj, name); - } - - // Offer a way to retrieve XrayWrapper from a proxified node through `valueOf` - if (name == "valueOf") - return function (key) { - if (key === UNWRAP_ACCESS_KEY) - return obj; - return this; - }; - - // Return overloaded value if there is one. - // It allows to overload native methods like addEventListener that - // are not saved, even on the wrapper itself. - // (And avoid some methods like toSource from being returned here! [__proto__ test]) - if (name in overload && - overload[name] != Object.getPrototypeOf(overload)[name] && - name != "__proto__") { - return overload[name]; - } - - // Catch exceptions thrown by XrayWrappers when we try to access on* - // attributes like onclick, onfocus, ... - if (isEventName(name)) { - //console.log("expando:"+obj+" - "+obj.nodeType); - return name in expando ? expando[name].original : undefined; - } - - // Overload some XrayWrappers method in order to fix its bugs - if (name in methodFixes && - methodFixes[name] != Object.getPrototypeOf(methodFixes)[name] && - name != "__proto__") - return methodFixes[name]; - if (Object.keys(xRayWrappersMethodsFixes).indexOf(name) !== -1) { - let fix = xRayWrappersMethodsFixes[name](obj); - if (fix) - return methodFixes[name] = fix; - } - - let o = obj[name]; - - // XrayWrapper miss some attributes, try to catch these and return a value - if (!o) { - for each(let atttributeFixer in xRayWrappersMissFixes) { - let fix = atttributeFixer(obj, name); - if (fix) - return fix; - } - } - - // Generic case - return wrap(o, obj, name); - - }, - - set: function(receiver, name, val) { - - if (isEventName(name)) { - //console.log("SET on* attribute : " + name + " / " + val + "/" + obj); - let shortName = name.replace(/^on/,""); - - // Unregister previously set listener - if (expando[name]) { - obj.removeEventListener(shortName, expando[name], true); - delete expando[name]; - } - - // Only accept functions - if (typeof val != "function") - return false; - - // Register a new listener - let original = val; - val = ContentScriptFunctionWrapper(val); - expando[name] = val; - val.original = original; - obj.addEventListener(name.replace(/^on/, ""), val, true); - return true; - } - - obj[name] = val; - - // Handle native method not overloaded on XrayWrappers: - // obj.addEventListener = val; -> obj.addEventlistener = native method - // And, XPCNativeWrapper bug where nested values appear to be wrapped: - // obj.customNestedAttribute = val -> obj.customNestedAttribute !== val - // obj.customNestedAttribute = "waive wrapper something" - // SEE BUG 658560: Fix identity problem with CrossOriginWrappers - // TODO: check that DOM can't be updated by the document itself and so overloaded value becomes wrong - // but I think such behavior is limited to primitive type - if ((typeof val == "function" || typeof val == "object") && name) { - overload[name] = val; - } - - return true; - }, - - enumerate: function() { - var result = []; - for each (let name in Object.keys(obj)) { - result.push(name); - }; - return result; - }, - - keys: function() { - return Object.keys(obj); - } - }; -}; - - -/* - * Wrap an object from the document to a proxy wrapper. - */ -function create(object) { - if ("wrappedJSObject" in object) - object = object.wrappedJSObject; - let xpcWrapper = XPCNativeWrapper(object); - // If we can't build an XPCNativeWrapper, it doesn't make sense to build - // a proxy. All proxy code is based on having such wrapper that store - // different JS attributes set. - // (we can't build XPCNativeWrapper when object is from the same - // principal/domain) - if (object === xpcWrapper) { - return object; - } - return getProxyForObject(xpcWrapper); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/content/content-worker.js b/tools/addon-sdk-1.12/lib/sdk/content/content-worker.js deleted file mode 100644 index 8cf9c5c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/content/content-worker.js +++ /dev/null @@ -1,308 +0,0 @@ -/* 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 ContentWorker = Object.freeze({ - // TODO: Bug 727854 Use same implementation than common JS modules, - // i.e. EventEmitter module - - /** - * Create an EventEmitter instance. - */ - createEventEmitter: function createEventEmitter(emit) { - let listeners = Object.create(null); - let eventEmitter = Object.freeze({ - emit: emit, - on: function on(name, callback) { - if (typeof callback !== "function") - return this; - if (!(name in listeners)) - listeners[name] = []; - listeners[name].push(callback); - return this; - }, - once: function once(name, callback) { - eventEmitter.on(name, function onceCallback() { - eventEmitter.removeListener(name, onceCallback); - callback.apply(callback, arguments); - }); - }, - removeListener: function removeListener(name, callback) { - if (!(name in listeners)) - return; - let index = listeners[name].indexOf(callback); - if (index == -1) - return; - listeners[name].splice(index, 1); - } - }); - function onEvent(name) { - if (!(name in listeners)) - return []; - let args = Array.slice(arguments, 1); - let results = []; - for each (let callback in listeners[name]) { - results.push(callback.apply(null, args)); - } - return results; - } - function hasListenerFor(name) { - if (!(name in listeners)) - return false; - return listeners[name].length > 0; - } - return { - eventEmitter: eventEmitter, - emit: onEvent, - hasListenerFor: hasListenerFor - }; - }, - - /** - * Create an EventEmitter instance to communicate with chrome module - * by passing only strings between compartments. - * This function expects `emitToChrome` function, that allows to send - * events to the chrome module. It returns the EventEmitter as `pipe` - * attribute, and, `onChromeEvent` a function that allows chrome module - * to send event into the EventEmitter. - * - * pipe.emit --> emitToChrome - * onChromeEvent --> callback registered through pipe.on - */ - createPipe: function createPipe(emitToChrome) { - function onEvent() { - // Convert to real array - let args = Array.slice(arguments); - // JSON.stringify is buggy with cross-sandbox values, - // it may return "{}" on functions. Use a replacer to match them correctly. - function replacer(k, v) { - return typeof v === "function" ? undefined : v; - } - let str = JSON.stringify(args, replacer); - emitToChrome(str); - } - - let { eventEmitter, emit, hasListenerFor } = - ContentWorker.createEventEmitter(onEvent); - - return { - pipe: eventEmitter, - onChromeEvent: function onChromeEvent(array) { - // We either receive a stringified array, or a real array. - // We still allow to pass an array of objects, in WorkerSandbox.emitSync - // in order to allow sending DOM node reference between content script - // and modules (only used for context-menu API) - let args = typeof array == "string" ? JSON.parse(array) : array; - return emit.apply(null, args); - }, - hasListenerFor: hasListenerFor - }; - }, - - injectConsole: function injectConsole(exports, pipe) { - exports.console = Object.freeze({ - log: pipe.emit.bind(null, "console", "log"), - info: pipe.emit.bind(null, "console", "info"), - warn: pipe.emit.bind(null, "console", "warn"), - error: pipe.emit.bind(null, "console", "error"), - debug: pipe.emit.bind(null, "console", "debug"), - exception: pipe.emit.bind(null, "console", "exception"), - trace: pipe.emit.bind(null, "console", "trace") - }); - }, - - injectTimers: function injectTimers(exports, chromeAPI, pipe, console) { - // wrapped functions from `'timer'` module. - // Wrapper adds `try catch` blocks to the callbacks in order to - // emit `error` event on a symbiont if exception is thrown in - // the Worker global scope. - // @see http://www.w3.org/TR/workers/#workerutils - - // List of all living timeouts/intervals - let _timers = Object.create(null); - - // Keep a reference to original timeout functions - let { - setTimeout: chromeSetTimeout, - setInterval: chromeSetInterval, - clearTimeout: chromeClearTimeout, - clearInterval: chromeClearInterval - } = chromeAPI.timers; - - function registerTimer(timer) { - let registerMethod = null; - if (timer.kind == "timeout") - registerMethod = chromeSetTimeout; - else if (timer.kind == "interval") - registerMethod = chromeSetInterval; - else - throw new Error("Unknown timer kind: " + timer.kind); - let id = registerMethod(onFire, timer.delay); - function onFire() { - try { - if (timer.kind == "timeout") - delete _timers[id]; - timer.fun.apply(null, timer.args); - } catch(e) { - console.exception(e); - } - } - _timers[id] = timer; - return id; - } - - function unregisterTimer(id) { - if (!(id in _timers)) - return; - let { kind } = _timers[id]; - delete _timers[id]; - if (kind == "timeout") - chromeClearTimeout(id); - else if (kind == "interval") - chromeClearInterval(id); - else - throw new Error("Unknown timer kind: " + kind); - } - - function disableAllTimers() { - Object.keys(_timers).forEach(unregisterTimer); - } - - exports.setTimeout = function ContentScriptSetTimeout(callback, delay) { - return registerTimer({ - kind: "timeout", - fun: callback, - delay: delay, - args: Array.slice(arguments, 2) - }); - }; - exports.clearTimeout = function ContentScriptClearTimeout(id) { - unregisterTimer(id); - }; - - exports.setInterval = function ContentScriptSetInterval(callback, delay) { - return registerTimer({ - kind: "interval", - fun: callback, - delay: delay, - args: Array.slice(arguments, 2) - }); - }; - exports.clearInterval = function ContentScriptClearInterval(id) { - unregisterTimer(id); - }; - - // On page-hide, save a list of all existing timers before disabling them, - // in order to be able to restore them on page-show. - // These events are fired when the page goes in/out of bfcache. - // https://developer.mozilla.org/En/Working_with_BFCache - let frozenTimers = []; - pipe.on("pageshow", function onPageShow() { - frozenTimers.forEach(registerTimer); - }); - pipe.on("pagehide", function onPageHide() { - frozenTimers = []; - for (let id in _timers) - frozenTimers.push(_timers[id]); - disableAllTimers(); - // Some other pagehide listeners may register some timers that won't be - // frozen as this particular pagehide listener is called first. - // So freeze these timers on next cycle. - chromeSetTimeout(function () { - for (let id in _timers) - frozenTimers.push(_timers[id]); - disableAllTimers(); - }, 0); - }); - - // Unregister all timers when the page is destroyed - // (i.e. when it is removed from bfcache) - pipe.on("detach", function clearTimeouts() { - disableAllTimers(); - _timers = {}; - frozenTimers = []; - }); - }, - - injectMessageAPI: function injectMessageAPI(exports, pipe) { - - let { eventEmitter: port, emit : portEmit } = - ContentWorker.createEventEmitter(pipe.emit.bind(null, "event")); - pipe.on("event", portEmit); - - let self = { - port: port, - postMessage: pipe.emit.bind(null, "message"), - on: pipe.on.bind(null), - once: pipe.once.bind(null), - removeListener: pipe.removeListener.bind(null), - }; - Object.defineProperty(exports, "self", { - value: self - }); - - // Deprecated use of on/postMessage from globals - exports.postMessage = function deprecatedPostMessage() { - console.error("DEPRECATED: The global `postMessage()` function in " + - "content scripts is deprecated in favor of the " + - "`self.postMessage()` function, which works the same. " + - "Replace calls to `postMessage()` with calls to " + - "`self.postMessage()`." + - "For more info on `self.on`, see " + - "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>."); - return self.postMessage.apply(null, arguments); - }; - exports.on = function deprecatedOn() { - console.error("DEPRECATED: The global `on()` function in content " + - "scripts is deprecated in favor of the `self.on()` " + - "function, which works the same. Replace calls to `on()` " + - "with calls to `self.on()`" + - "For more info on `self.on`, see " + - "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>."); - return self.on.apply(null, arguments); - }; - - // Deprecated use of `onMessage` from globals - let onMessage = null; - Object.defineProperty(exports, "onMessage", { - get: function () onMessage, - set: function (v) { - if (onMessage) - self.removeListener("message", onMessage); - console.error("DEPRECATED: The global `onMessage` function in content" + - "scripts is deprecated in favor of the `self.on()` " + - "function. Replace `onMessage = function (data){}` " + - "definitions with calls to `self.on('message', " + - "function (data){})`. " + - "For more info on `self.on`, see " + - "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>."); - onMessage = v; - if (typeof onMessage == "function") - self.on("message", onMessage); - } - }); - }, - - injectOptions: function (exports, options) { - Object.defineProperty( exports.self, "options", { value: JSON.parse( options ) }); - }, - - inject: function (exports, chromeAPI, emitToChrome, options) { - let { pipe, onChromeEvent, hasListenerFor } = - ContentWorker.createPipe(emitToChrome); - - ContentWorker.injectConsole(exports, pipe); - ContentWorker.injectTimers(exports, chromeAPI, pipe, exports.console); - ContentWorker.injectMessageAPI(exports, pipe); - if ( options !== undefined ) { - ContentWorker.injectOptions(exports, options); - } - - Object.freeze( exports.self ); - - return { - emitToContent: onChromeEvent, - hasListenerFor: hasListenerFor - }; - } -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/content/content.js b/tools/addon-sdk-1.12/lib/sdk/content/content.js deleted file mode 100644 index 4bffd45..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/content/content.js +++ /dev/null @@ -1,15 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -exports.Loader = require('./loader').Loader; -exports.Symbiont = require('./symbiont').Symbiont; -exports.Worker = require('./worker').Worker; - diff --git a/tools/addon-sdk-1.12/lib/sdk/content/loader.js b/tools/addon-sdk-1.12/lib/sdk/content/loader.js deleted file mode 100644 index b01675c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/content/loader.js +++ /dev/null @@ -1,204 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { EventEmitter } = require('../deprecated/events'); -const { validateOptions } = require('../deprecated/api-utils'); -const { URL } = require('../url'); -const file = require('../io/file'); - -const LOCAL_URI_SCHEMES = ['resource', 'data']; - -// Returns `null` if `value` is `null` or `undefined`, otherwise `value`. -function ensureNull(value) { - return value == null ? null : value; -} - -// map of property validations -const valid = { - contentURL: { - ok: function (value) { - try { - URL(value); - } - catch(e) { - return false; - } - return true; - }, - msg: 'The `contentURL` option must be a valid URL.' - }, - contentScriptFile: { - is: ['undefined', 'null', 'string', 'array'], - map: ensureNull, - ok: function(value) { - if (value === null) - return true; - - value = [].concat(value); - - // Make sure every item is a local file URL. - return value.every(function (item) { - try { - return ~LOCAL_URI_SCHEMES.indexOf(URL(item).scheme); - } - catch(e) { - return false; - } - }); - - }, - msg: 'The `contentScriptFile` option must be a local URL or an array of URLs.' - }, - contentScript: { - is: ['undefined', 'null', 'string', 'array'], - map: ensureNull, - ok: function(value) { - return !Array.isArray(value) || value.every( - function(item) { return typeof item === 'string' } - ); - }, - msg: 'The `contentScript` option must be a string or an array of strings.' - }, - contentScriptWhen: { - is: ['string'], - ok: function(value) { return ~['start', 'ready', 'end'].indexOf(value) }, - map: function(value) { - return value || 'end'; - }, - msg: 'The `contentScriptWhen` option must be either "start", "ready" or "end".' - }, - contentScriptOptions: { - ok: function(value) { - if ( value === undefined ) { return true; } - try { JSON.parse( JSON.stringify( value ) ); } catch(e) { return false; } - return true; - }, - map: function(value) 'undefined' === getTypeOf(value) ? null : value, - msg: 'The contentScriptOptions should be a jsonable value.' - } -}; -exports.validationAttributes = valid; - -/** - * Shortcut function to validate property with validation. - * @param {Object|Number|String} suspect - * value to validate - * @param {Object} validation - * validation rule passed to `api-utils` - */ -function validate(suspect, validation) validateOptions( - { $: suspect }, - { $: validation } -).$ - -function Allow(script) ({ - get script() script, - set script(value) script = !!value -}) - -/** - * Trait is intended to be used in some composition. It provides set of core - * properties and bounded validations to them. Trait is useful for all the - * compositions providing high level APIs for interaction with content. - * Property changes emit `"propertyChange"` events on instances. - */ -const Loader = EventEmitter.compose({ - /** - * Permissions for the content, with the following keys: - * @property {Object} [allow = { script: true }] - * @property {Boolean} [allow.script = true] - * Whether or not to execute script in the content. Defaults to true. - */ - get allow() this._allow || (this._allow = Allow(true)), - set allow(value) this.allow.script = value && value.script, - _allow: null, - /** - * The content to load. Either a string of HTML or a URL. - * @type {String} - */ - get contentURL() this._contentURL, - set contentURL(value) { - value = validate(value, valid.contentURL); - if (this._contentURL != value) { - this._emit('propertyChange', { - contentURL: this._contentURL = value - }); - } - }, - _contentURL: null, - /** - * When to load the content scripts. - * Possible values are "end" (default), which loads them once all page - * contents have been loaded, "ready", which loads them once DOM nodes are - * ready (ie like DOMContentLoaded event), and "start", which loads them once - * the `window` object for the page has been created, but before any scripts - * specified by the page have been loaded. - * Property change emits `propertyChange` event on instance with this key - * and new value. - * @type {'start'|'ready'|'end'} - */ - get contentScriptWhen() this._contentScriptWhen, - set contentScriptWhen(value) { - value = validate(value, valid.contentScriptWhen); - if (value !== this._contentScriptWhen) { - this._emit('propertyChange', { - contentScriptWhen: this._contentScriptWhen = value - }); - } - }, - _contentScriptWhen: 'end', - /** - * Options avalaible from the content script as `self.options`. - * The value of options can be of any type (object, array, string, etc.) - * but only jsonable values will be available as frozen objects from the - * content script. - * Property change emits `propertyChange` event on instance with this key - * and new value. - * @type {Object} - */ - get contentScriptOptions() this._contentScriptOptions, - set contentScriptOptions(value) this._contentScriptOptions = value, - _contentScriptOptions: null, - /** - * The URLs of content scripts. - * Property change emits `propertyChange` event on instance with this key - * and new value. - * @type {String[]} - */ - get contentScriptFile() this._contentScriptFile, - set contentScriptFile(value) { - value = validate(value, valid.contentScriptFile); - if (value != this._contentScriptFile) { - this._emit('propertyChange', { - contentScriptFile: this._contentScriptFile = value - }); - } - }, - _contentScriptFile: null, - /** - * The texts of content script. - * Property change emits `propertyChange` event on instance with this key - * and new value. - * @type {String|undefined} - */ - get contentScript() this._contentScript, - set contentScript(value) { - value = validate(value, valid.contentScript); - if (value != this._contentScript) { - this._emit('propertyChange', { - contentScript: this._contentScript = value - }); - } - }, - _contentScript: null -}); -exports.Loader = Loader; diff --git a/tools/addon-sdk-1.12/lib/sdk/content/symbiont.js b/tools/addon-sdk-1.12/lib/sdk/content/symbiont.js deleted file mode 100644 index 186e9d3..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/content/symbiont.js +++ /dev/null @@ -1,197 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Worker } = require('./worker'); -const { Loader } = require('./loader'); -const hiddenFrames = require('../frame/hidden-frame'); -const observers = require('../deprecated/observer-service'); -const unload = require('../system/unload'); - -const assetsURI = require('../self').data.url(); - -/** - * This trait is layered on top of `Worker` and in contrast to symbiont - * Worker constructor requires `content` option that represents content - * that will be loaded in the provided frame, if frame is not provided - * Worker will create hidden one. - */ -const Symbiont = Worker.resolve({ - constructor: '_initWorker', - destroy: '_workerDestroy' - }).compose(Loader, { - - /** - * The constructor requires all the options that are required by - * `require('content').Worker` with the difference that the `frame` option - * is optional. If `frame` is not provided, `contentURL` is expected. - * @param {Object} options - * @param {String} options.contentURL - * URL of a content to load into `this._frame` and create worker for. - * @param {Element} [options.frame] - * iframe element that is used to load `options.contentURL` into. - * if frame is not provided hidden iframe will be created. - */ - constructor: function Symbiont(options) { - options = options || {}; - - if ('contentURL' in options) - this.contentURL = options.contentURL; - if ('contentScriptWhen' in options) - this.contentScriptWhen = options.contentScriptWhen; - if ('contentScriptOptions' in options) - this.contentScriptOptions = options.contentScriptOptions; - if ('contentScriptFile' in options) - this.contentScriptFile = options.contentScriptFile; - if ('contentScript' in options) - this.contentScript = options.contentScript; - if ('allow' in options) - this.allow = options.allow; - if ('onError' in options) - this.on('error', options.onError); - if ('onMessage' in options) - this.on('message', options.onMessage); - if ('frame' in options) { - this._initFrame(options.frame); - } - else { - let self = this; - this._hiddenFrame = hiddenFrames.HiddenFrame({ - onReady: function onFrame() { - self._initFrame(this.element); - }, - onUnload: function onUnload() { - // Bug 751211: Remove reference to _frame when hidden frame is - // automatically removed on unload, otherwise we are going to face - // "dead object" exception - self.destroy(); - } - }); - hiddenFrames.add(this._hiddenFrame); - } - - unload.ensure(this._public, "destroy"); - }, - - destroy: function destroy() { - this._workerDestroy(); - this._unregisterListener(); - this._frame = null; - if (this._hiddenFrame) { - hiddenFrames.remove(this._hiddenFrame); - this._hiddenFrame = null; - } - }, - - /** - * XUL iframe or browser elements with attribute `type` being `content`. - * Used to create `ContentSymbiont` from. - * @type {nsIFrame|nsIBrowser} - */ - _frame: null, - - /** - * Listener to the `'frameReady"` event (emitted when `iframe` is ready). - * Removes listener, sets right permissions to the frame and loads content. - */ - _initFrame: function _initFrame(frame) { - if (this._loadListener) - this._unregisterListener(); - - this._frame = frame; - frame.docShell.allowJavascript = this.allow.script; - frame.setAttribute("src", this._contentURL); - - // Inject `addon` object in document if we load a document from - // one of our addon folder and if no content script are defined. bug 612726 - let isDataResource = - typeof this._contentURL == "string" && - this._contentURL.indexOf(assetsURI) == 0; - let hasContentScript = - (Array.isArray(this.contentScript) ? this.contentScript.length > 0 - : !!this.contentScript) || - (Array.isArray(this.contentScriptFile) ? this.contentScriptFile.length > 0 - : !!this.contentScriptFile); - // If we have to inject `addon` we have to do it before document - // script execution, so during `start`: - this._injectInDocument = isDataResource && !hasContentScript; - if (this._injectInDocument) - this.contentScriptWhen = "start"; - - if ((frame.contentDocument.readyState == "complete" || - (frame.contentDocument.readyState == "interactive" && - this.contentScriptWhen != 'end' )) && - frame.contentDocument.location == this._contentURL) { - // In some cases src doesn't change and document is already ready - // (for ex: when the user moves a widget while customizing toolbars.) - this._onInit(); - return; - } - - let self = this; - - if ('start' == this.contentScriptWhen) { - this._loadEvent = 'start'; - observers.add('document-element-inserted', - this._loadListener = function onStart(doc) { - - let window = doc.defaultView; - if (window && window == frame.contentWindow) { - self._unregisterListener(); - self._onInit(); - } - - }); - return; - } - - let eventName = 'end' == this.contentScriptWhen ? 'load' : 'DOMContentLoaded'; - let self = this; - this._loadEvent = eventName; - frame.addEventListener(eventName, - this._loadListener = function _onReady(event) { - - if (event.target != frame.contentDocument) - return; - self._unregisterListener(); - - self._onInit(); - - }, true); - - }, - - /** - * Unregister listener that watchs for document being ready to be injected. - * This listener is registered in `Symbiont._initFrame`. - */ - _unregisterListener: function _unregisterListener() { - if (!this._loadListener) - return; - if (this._loadEvent == "start") { - observers.remove('document-element-inserted', this._loadListener); - } - else { - this._frame.removeEventListener(this._loadEvent, this._loadListener, - true); - } - this._loadListener = null; - }, - - /** - * Called by Symbiont itself when the frame is ready to load - * content scripts according to contentScriptWhen. Overloaded by Panel. - */ - _onInit: function () { - this._initWorker({ window: this._frame.contentWindow }); - } - -}); -exports.Symbiont = Symbiont; diff --git a/tools/addon-sdk-1.12/lib/sdk/content/thumbnail.js b/tools/addon-sdk-1.12/lib/sdk/content/thumbnail.js deleted file mode 100644 index 9e57274..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/content/thumbnail.js +++ /dev/null @@ -1,46 +0,0 @@ -/* 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'; - -module.metadata = { - 'stability': 'unstable' -}; - -const { Cc, Ci, Cu } = require('chrome'); -const AppShellService = Cc['@mozilla.org/appshell/appShellService;1']. - getService(Ci.nsIAppShellService); - -const NS = 'http://www.w3.org/1999/xhtml'; -const COLOR = 'rgb(255,255,255)'; - -/** - * Creates canvas element with a thumbnail of the passed window. - * @param {Window} window - * @returns {Element} - */ -function getThumbnailCanvasForWindow(window) { - let aspectRatio = 0.5625; // 16:9 - let thumbnail = AppShellService.hiddenDOMWindow.document - .createElementNS(NS, 'canvas'); - thumbnail.mozOpaque = true; - thumbnail.width = Math.ceil(window.screen.availWidth / 5.75); - thumbnail.height = Math.round(thumbnail.width * aspectRatio); - let ctx = thumbnail.getContext('2d'); - let snippetWidth = window.innerWidth * .6; - let scale = thumbnail.width / snippetWidth; - ctx.scale(scale, scale); - ctx.drawWindow(window, window.scrollX, window.scrollY, snippetWidth, - snippetWidth * aspectRatio, COLOR); - return thumbnail; -} -exports.getThumbnailCanvasForWindow = getThumbnailCanvasForWindow; - -/** - * Creates Base64 encoded data URI of the thumbnail for the passed window. - * @param {Window} window - * @returns {String} - */ -exports.getThumbnailURIForWindow = function getThumbnailURIForWindow(window) { - return getThumbnailCanvasForWindow(window).toDataURL() -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/content/worker.js b/tools/addon-sdk-1.12/lib/sdk/content/worker.js deleted file mode 100644 index b2f204c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/content/worker.js +++ /dev/null @@ -1,582 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Trait } = require('../deprecated/traits'); -const { EventEmitter, EventEmitterTrait } = require('../deprecated/events'); -const { Ci, Cu, Cc } = require('chrome'); -const timer = require('../timers'); -const { URL } = require('../url'); -const unload = require('../system/unload'); -const observers = require('../deprecated/observer-service'); -const { Cortex } = require('../deprecated/cortex'); -const { sandbox, evaluate, load } = require("../loader/sandbox"); -const { merge } = require('../util/object'); -const xulApp = require("../system/xul-app"); -const USE_JS_PROXIES = !xulApp.versionInRange(xulApp.platformVersion, - "17.0a2", "*"); -const { getTabForWindow } = require('../tabs/helpers'); - -/* Trick the linker in order to ensure shipping these files in the XPI. - require('./content-proxy.js'); - require('./content-worker.js'); - Then, retrieve URL of these files in the XPI: -*/ -let prefix = module.uri.split('worker.js')[0]; -const CONTENT_PROXY_URL = prefix + 'content-proxy.js'; -const CONTENT_WORKER_URL = prefix + 'content-worker.js'; - -const JS_VERSION = '1.8'; - -const ERR_DESTROYED = - "Couldn't find the worker to receive this message. " + - "The script may not be initialized yet, or may already have been unloaded."; - -const ERR_FROZEN = "The page is currently hidden and can no longer be used " + - "until it is visible again."; - -/** - * This key is not exported and should only be used for proxy tests. - * The following `PRIVATE_KEY` is used in addon module scope in order to tell - * Worker API to expose `UNWRAP_ACCESS_KEY` in content script. - * This key allows test-content-proxy.js to unwrap proxy with valueOf: - * let xpcWrapper = proxyWrapper.valueOf(UNWRAP_ACCESS_KEY); - */ -const PRIVATE_KEY = {}; - - -const WorkerSandbox = EventEmitter.compose({ - - /** - * Emit a message to the worker content sandbox - */ - emit: function emit() { - // First ensure having a regular array - // (otherwise, `arguments` would be mapped to an object by `stringify`) - let array = Array.slice(arguments); - // JSON.stringify is buggy with cross-sandbox values, - // it may return "{}" on functions. Use a replacer to match them correctly. - function replacer(k, v) { - return typeof v === "function" ? undefined : v; - } - // Ensure having an asynchronous behavior - let self = this; - timer.setTimeout(function () { - self._emitToContent(JSON.stringify(array, replacer)); - }, 0); - }, - - /** - * Synchronous version of `emit`. - * /!\ Should only be used when it is strictly mandatory /!\ - * Doesn't ensure passing only JSON values. - * Mainly used by context-menu in order to avoid breaking it. - */ - emitSync: function emitSync() { - let args = Array.slice(arguments); - // Bug 732716: Ensure wrapping xrays sent to the content script - // otherwise it will have access to raw xraywrappers and content script - // will assume it is an user object coming from the content script sandbox - if ("_wrap" in this) - args = args.map(this._wrap); - return this._emitToContent(args); - }, - - /** - * Tells if content script has at least one listener registered for one event, - * through `self.on('xxx', ...)`. - * /!\ Shouldn't be used. Implemented to avoid breaking context-menu API. - */ - hasListenerFor: function hasListenerFor(name) { - return this._hasListenerFor(name); - }, - - /** - * Method called by the worker sandbox when it needs to send a message - */ - _onContentEvent: function onContentEvent(args) { - // As `emit`, we ensure having an asynchronous behavior - let self = this; - timer.setTimeout(function () { - // We emit event to chrome/addon listeners - self._emit.apply(self, JSON.parse(args)); - }, 0); - }, - - /** - * Configures sandbox and loads content scripts into it. - * @param {Worker} worker - * content worker - */ - constructor: function WorkerSandbox(worker) { - this._addonWorker = worker; - - // Ensure that `emit` has always the right `this` - this.emit = this.emit.bind(this); - this.emitSync = this.emitSync.bind(this); - - // We receive a wrapped window, that may be an xraywrapper if it's content - let window = worker._window; - let proto = window; - - // Instantiate trusted code in another Sandbox in order to prevent content - // script from messing with standard classes used by proxy and API code. - let apiSandbox = sandbox(window, { wantXrays: true }); - - // Build content proxies only if the document has a non-system principal - // And only on old firefox versions that doesn't ship bug 738244 - if (USE_JS_PROXIES && XPCNativeWrapper.unwrap(window) !== window) { - apiSandbox.console = console; - // Execute the proxy code - load(apiSandbox, CONTENT_PROXY_URL); - // Get a reference of the window's proxy - proto = apiSandbox.create(window); - // Keep a reference to `wrap` function for `emitSync` usage - this._wrap = apiSandbox.wrap; - } - - // Create the sandbox and bind it to window in order for content scripts to - // have access to all standard globals (window, document, ...) - let content = this._sandbox = sandbox(window, { - sandboxPrototype: proto, - wantXrays: true - }); - // We have to ensure that window.top and window.parent are the exact same - // object than window object, i.e. the sandbox global object. But not - // always, in case of iframes, top and parent are another window object. - let top = window.top === window ? content : content.top; - let parent = window.parent === window ? content : content.parent; - merge(content, { - // We need "this === window === top" to be true in toplevel scope: - get window() content, - get top() top, - get parent() parent, - // Use the Greasemonkey naming convention to provide access to the - // unwrapped window object so the content script can access document - // JavaScript values. - // NOTE: this functionality is experimental and may change or go away - // at any time! - get unsafeWindow() window.wrappedJSObject - }); - - // Load trusted code that will inject content script API. - // We need to expose JS objects defined in same principal in order to - // avoid having any kind of wrapper. - load(apiSandbox, CONTENT_WORKER_URL); - - // prepare a clean `self.options` - let options = 'contentScriptOptions' in worker ? - JSON.stringify( worker.contentScriptOptions ) : - undefined; - - // Then call `inject` method and communicate with this script - // by trading two methods that allow to send events to the other side: - // - `onEvent` called by content script - // - `result.emitToContent` called by addon script - // Bug 758203: We have to explicitely define `__exposedProps__` in order - // to allow access to these chrome object attributes from this sandbox with - // content priviledges - // https://developer.mozilla.org/en/XPConnect_wrappers#Other_security_wrappers - let chromeAPI = { - timers: { - setTimeout: timer.setTimeout, - setInterval: timer.setInterval, - clearTimeout: timer.clearTimeout, - clearInterval: timer.clearInterval, - __exposedProps__: { - setTimeout: 'r', - setInterval: 'r', - clearTimeout: 'r', - clearInterval: 'r' - } - }, - __exposedProps__: { - timers: 'r' - } - }; - let onEvent = this._onContentEvent.bind(this); - // `ContentWorker` is defined in CONTENT_WORKER_URL file - let result = apiSandbox.ContentWorker.inject(content, chromeAPI, onEvent, options); - this._emitToContent = result.emitToContent; - this._hasListenerFor = result.hasListenerFor; - - // Handle messages send by this script: - let self = this; - // console.xxx calls - this.on("console", function consoleListener(kind) { - console[kind].apply(console, Array.slice(arguments, 1)); - }); - - // self.postMessage calls - this.on("message", function postMessage(data) { - // destroyed? - if (self._addonWorker) - self._addonWorker._emit('message', data); - }); - - // self.port.emit calls - this.on("event", function portEmit(name, args) { - // destroyed? - if (self._addonWorker) - self._addonWorker._onContentScriptEvent.apply(self._addonWorker, arguments); - }); - - // Internal feature that is only used by SDK tests: - // Expose unlock key to content script context. - // See `PRIVATE_KEY` definition for more information. - if (apiSandbox && worker._expose_key) - content.UNWRAP_ACCESS_KEY = apiSandbox.UNWRAP_ACCESS_KEY; - - // Inject `addon` global into target document if document is trusted, - // `addon` in document is equivalent to `self` in content script. - if (worker._injectInDocument) { - let win = window.wrappedJSObject ? window.wrappedJSObject : window; - Object.defineProperty(win, "addon", { - value: content.self - } - ); - } - - // The order of `contentScriptFile` and `contentScript` evaluation is - // intentional, so programs can load libraries like jQuery from script URLs - // and use them in scripts. - let contentScriptFile = ('contentScriptFile' in worker) ? worker.contentScriptFile - : null, - contentScript = ('contentScript' in worker) ? worker.contentScript : null; - - if (contentScriptFile) { - if (Array.isArray(contentScriptFile)) - this._importScripts.apply(this, contentScriptFile); - else - this._importScripts(contentScriptFile); - } - if (contentScript) { - this._evaluate( - Array.isArray(contentScript) ? contentScript.join(';\n') : contentScript - ); - } - }, - destroy: function destroy() { - this.emitSync("detach"); - this._sandbox = null; - this._addonWorker = null; - this._wrap = null; - }, - - /** - * JavaScript sandbox where all the content scripts are evaluated. - * {Sandbox} - */ - _sandbox: null, - - /** - * Reference to the addon side of the worker. - * @type {Worker} - */ - _addonWorker: null, - - /** - * Evaluates code in the sandbox. - * @param {String} code - * JavaScript source to evaluate. - * @param {String} [filename='javascript:' + code] - * Name of the file - */ - _evaluate: function(code, filename) { - try { - evaluate(this._sandbox, code, filename || 'javascript:' + code); - } - catch(e) { - this._addonWorker._emit('error', e); - } - }, - /** - * Imports scripts to the sandbox by reading files under urls and - * evaluating its source. If exception occurs during evaluation - * `"error"` event is emitted on the worker. - * This is actually an analog to the `importScript` method in web - * workers but in our case it's not exposed even though content - * scripts may be able to do it synchronously since IO operation - * takes place in the UI process. - */ - _importScripts: function _importScripts(url) { - let urls = Array.slice(arguments, 0); - for each (let contentScriptFile in urls) { - try { - let uri = URL(contentScriptFile); - if (uri.scheme === 'resource') - load(this._sandbox, String(uri)); - else - throw Error("Unsupported `contentScriptFile` url: " + String(uri)); - } - catch(e) { - this._addonWorker._emit('error', e); - } - } - } -}); - -/** - * Message-passing facility for communication between code running - * in the content and add-on process. - * @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/content/worker - */ -const Worker = EventEmitter.compose({ - on: Trait.required, - _removeAllListeners: Trait.required, - - /** - * Sends a message to the worker's global scope. Method takes single - * argument, which represents data to be sent to the worker. The data may - * be any primitive type value or `JSON`. Call of this method asynchronously - * emits `message` event with data value in the global scope of this - * symbiont. - * - * `message` event listeners can be set either by calling - * `self.on` with a first argument string `"message"` or by - * implementing `onMessage` function in the global scope of this worker. - * @param {Number|String|JSON} data - */ - postMessage: function postMessage(data) { - if (!this._contentWorker) - throw new Error(ERR_DESTROYED); - if (this._frozen) - throw new Error(ERR_FROZEN); - - this._contentWorker.emit("message", data); - }, - - /** - * EventEmitter, that behaves (calls listeners) asynchronously. - * A way to send customized messages to / from the worker. - * Events from in the worker can be observed / emitted via - * worker.on / worker.emit. - */ - get port() { - // We generate dynamically this attribute as it needs to be accessible - // before Worker.constructor gets called. (For ex: Panel) - - // create an event emitter that receive and send events from/to the worker - let self = this; - this._port = EventEmitterTrait.create({ - emit: function () self._emitEventToContent(Array.slice(arguments)) - }); - - // expose wrapped port, that exposes only public properties: - // We need to destroy this getter in order to be able to set the - // final value. We need to update only public port attribute as we never - // try to access port attribute from private API. - delete this._public.port; - this._public.port = Cortex(this._port); - // Replicate public port to the private object - delete this.port; - this.port = this._public.port; - - return this._port; - }, - - /** - * Same object than this.port but private API. - * Allow access to _emit, in order to send event to port. - */ - _port: null, - - /** - * Emit a custom event to the content script, - * i.e. emit this event on `self.port` - */ - _emitEventToContent: function _emitEventToContent(args) { - // We need to save events that are emitted before the worker is - // initialized - if (!this._inited) { - this._earlyEvents.push(args); - return; - } - - if (this._frozen) - throw new Error(ERR_FROZEN); - - // We throw exception when the worker has been destroyed - if (!this._contentWorker) { - throw new Error(ERR_DESTROYED); - } - - // Forward the event to the WorkerSandbox object - this._contentWorker.emit.apply(null, ["event"].concat(args)); - }, - - // Is worker connected to the content worker sandbox ? - _inited: false, - - // Is worker being frozen? i.e related document is frozen in bfcache. - // Content script should not be reachable if frozen. - _frozen: true, - - // List of custom events fired before worker is initialized - get _earlyEvents() { - delete this._earlyEvents; - this._earlyEvents = []; - return this._earlyEvents; - }, - - constructor: function Worker(options) { - options = options || {}; - - if ('window' in options) - this._window = options.window; - if ('contentScriptFile' in options) - this.contentScriptFile = options.contentScriptFile; - if ('contentScriptOptions' in options) - this.contentScriptOptions = options.contentScriptOptions; - if ('contentScript' in options) - this.contentScript = options.contentScript; - if ('onError' in options) - this.on('error', options.onError); - if ('onMessage' in options) - this.on('message', options.onMessage); - if ('onDetach' in options) - this.on('detach', options.onDetach); - - // Internal feature that is only used by SDK unit tests. - // See `PRIVATE_KEY` definition for more information. - if ('exposeUnlockKey' in options && options.exposeUnlockKey === PRIVATE_KEY) - this._expose_key = true; - - // Track document unload to destroy this worker. - // We can't watch for unload event on page's window object as it - // prevents bfcache from working: - // https://developer.mozilla.org/En/Working_with_BFCache - this._windowID = this._window. - QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIDOMWindowUtils). - currentInnerWindowID; - observers.add("inner-window-destroyed", - this._documentUnload = this._documentUnload.bind(this)); - - // Listen to pagehide event in order to freeze the content script - // while the document is frozen in bfcache: - this._window.addEventListener("pageshow", - this._pageShow = this._pageShow.bind(this), - true); - this._window.addEventListener("pagehide", - this._pageHide = this._pageHide.bind(this), - true); - - unload.ensure(this._public, "destroy"); - - // Ensure that worker._port is initialized for contentWorker to be able - // to send use event during WorkerSandbox(this) - this.port; - - // will set this._contentWorker pointing to the private API: - this._contentWorker = WorkerSandbox(this); - - // Mainly enable worker.port.emit to send event to the content worker - this._inited = true; - this._frozen = false; - - // Flush all events that have been fired before the worker is initialized. - this._earlyEvents.forEach((function (args) this._emitEventToContent(args)). - bind(this)); - }, - - _documentUnload: function _documentUnload(subject, topic, data) { - let innerWinID = subject.QueryInterface(Ci.nsISupportsPRUint64).data; - if (innerWinID != this._windowID) return false; - this._workerCleanup(); - return true; - }, - - _pageShow: function _pageShow() { - this._contentWorker.emitSync("pageshow"); - this._emit("pageshow"); - this._frozen = false; - }, - - _pageHide: function _pageHide() { - this._contentWorker.emitSync("pagehide"); - this._emit("pagehide"); - this._frozen = true; - }, - - get url() { - // this._window will be null after detach - return this._window ? this._window.document.location.href : null; - }, - - get tab() { - // this._window will be null after detach - if (this._window) - return getTabForWindow(this._window); - return null; - }, - - /** - * Tells content worker to unload itself and - * removes all the references from itself. - */ - destroy: function destroy() { - this._workerCleanup(); - this._removeAllListeners(); - }, - - /** - * Remove all internal references to the attached document - * Tells _port to unload itself and removes all the references from itself. - */ - _workerCleanup: function _workerCleanup() { - // maybe unloaded before content side is created - // As Symbiont call worker.constructor on document load - if (this._contentWorker) - this._contentWorker.destroy(); - this._contentWorker = null; - if (this._window) { - this._window.removeEventListener("pageshow", this._pageShow, true); - this._window.removeEventListener("pagehide", this._pageHide, true); - } - this._window = null; - // This method may be called multiple times, - // avoid dispatching `detach` event more than once - if (this._windowID) { - this._windowID = null; - observers.remove("inner-window-destroyed", this._documentUnload); - this._earlyEvents.slice(0, this._earlyEvents.length); - this._emit("detach"); - } - }, - - /** - * Receive an event from the content script that need to be sent to - * worker.port. Provide a way for composed object to catch all events. - */ - _onContentScriptEvent: function _onContentScriptEvent() { - this._port._emit.apply(this._port, arguments); - }, - - /** - * Reference to the content side of the worker. - * @type {WorkerGlobalScope} - */ - _contentWorker: null, - - /** - * Reference to the window that is accessible from - * the content scripts. - * @type {Object} - */ - _window: null, - - /** - * Flag to enable `addon` object injection in document. (bug 612726) - * @type {Boolean} - */ - _injectInDocument: false -}); -exports.Worker = Worker; diff --git a/tools/addon-sdk-1.12/lib/sdk/context-menu.js b/tools/addon-sdk-1.12/lib/sdk/context-menu.js deleted file mode 100644 index 9d20f0b..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/context-menu.js +++ /dev/null @@ -1,1494 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "stable" -}; - -const {Ci} = require("chrome"); - -if (!require("./system/xul-app").is("Firefox")) { - throw new Error([ - "The context-menu module currently supports only Firefox. In the future ", - "we would like it to support other applications, however. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." - ].join("")); -} - -const apiUtils = require("./deprecated/api-utils"); -const collection = require("./util/collection"); -const { Worker } = require("./content/worker"); -const { URL } = require("./url"); -const { MatchPattern } = require("./page-mod/match-pattern"); -const { EventEmitterTrait: EventEmitter } = require("./deprecated/events"); -const observerServ = require("./deprecated/observer-service"); -const jpSelf = require("./self"); -const { WindowTracker } = require("./deprecated/window-utils"); -const { getInnerId, isBrowser } = require("./window/utils"); -const { Trait } = require("./deprecated/light-traits"); -const { Cortex } = require("./deprecated/cortex"); -const timer = require("./timers"); - -// All user items we add have this class name. -const ITEM_CLASS = "jetpack-context-menu-item"; - -// Items in the top-level context menu also have this class. -const TOPLEVEL_ITEM_CLASS = "jetpack-context-menu-item-toplevel"; - -// Items in the overflow submenu also have this class. -const OVERFLOW_ITEM_CLASS = "jetpack-context-menu-item-overflow"; - -// The ID of the menu separator that separates standard context menu items from -// our user items. -const SEPARATOR_ID = "jetpack-context-menu-separator"; - -// If more than this number of items are added to the context menu, all items -// overflow into a "Jetpack" submenu. -const OVERFLOW_THRESH_DEFAULT = 10; -const OVERFLOW_THRESH_PREF = - "extensions.addon-sdk.context-menu.overflowThreshold"; - -// The label of the overflow sub-xul:menu. -// -// TODO: Localize this. -const OVERFLOW_MENU_LABEL = "Add-ons"; - -// The ID of the overflow sub-xul:menu. -const OVERFLOW_MENU_ID = "jetpack-content-menu-overflow-menu"; - -// The ID of the overflow submenu's xul:menupopup. -const OVERFLOW_POPUP_ID = "jetpack-content-menu-overflow-popup"; - -// These are used by PageContext.isCurrent below. If the popupNode or any of -// its ancestors is one of these, Firefox uses a tailored context menu, and so -// the page context doesn't apply. -const NON_PAGE_CONTEXT_ELTS = [ - Ci.nsIDOMHTMLAnchorElement, - Ci.nsIDOMHTMLAppletElement, - Ci.nsIDOMHTMLAreaElement, - Ci.nsIDOMHTMLButtonElement, - Ci.nsIDOMHTMLCanvasElement, - Ci.nsIDOMHTMLEmbedElement, - Ci.nsIDOMHTMLImageElement, - Ci.nsIDOMHTMLInputElement, - Ci.nsIDOMHTMLMapElement, - Ci.nsIDOMHTMLMediaElement, - Ci.nsIDOMHTMLMenuElement, - Ci.nsIDOMHTMLObjectElement, - Ci.nsIDOMHTMLOptionElement, - Ci.nsIDOMHTMLSelectElement, - Ci.nsIDOMHTMLTextAreaElement, -]; - -// This is used to access private properties of Item and Menu instances. -const PRIVATE_PROPS_KEY = { - valueOf: function valueOf() "private properties key" -}; - -// Used as an internal ID for items and as part of a public ID for item DOM -// elements. Careful: This number is not necessarily unique to any one instance -// of the module. For each module instance, when the first item is created this -// number will be 0, when the second is created it will be 1, and so on. -let nextItemID = 0; - -// The number of items that haven't finished initializing yet. See -// AIT__finishActiveItemInit(). -let numItemsWithUnfinishedInit = 0; - -exports.Item = Item; -exports.Menu = Menu; -exports.Separator = Separator; - - -// A word about traits and privates. `this` inside of traits methods is an -// object private to the implementation. It should never be publicly leaked. -// We use Cortex in the exported menu item constructors to create public -// reflections of the private objects that hide private properties -- those -// prefixed with an underscore. Public reflections are attached to the private -// objects via the `_public` property. -// -// All item objects passed into the implementation by the client will be public -// reflections, not private objects. Likewise, all item objects passed out of -// the implementation to the client must be public, not private. Mixing up -// public and private is bad and easy to do, so not only are private objects -// restricted to the implementation, but as much as possible we try to restrict -// them to the Item, Menu, and Separator traits and constructors. Everybody -// else in the implementation should expect to be passed public reflections, and -// they must specifically request private objects via privateItem(). - -// Item, Menu, and Separator are composed of this trait. -const ItemBaseTrait = Trait({ - - _initBase: function IBT__initBase(opts, optRules, optsToNotSet) { - this._optRules = optRules; - for (let optName in optRules) - if (optsToNotSet.indexOf(optName) < 0) - this[optName] = opts[optName]; - optsToNotSet.forEach(function (opt) validateOpt(opts[opt], optRules[opt])); - this._isInited = true; - - this._id = nextItemID++; - this._parentMenu = null; - - // This makes the private properties accessible to anyone with access to - // PRIVATE_PROPS_KEY. Barring loader tricks, only this file has has access - // to it, so only this file has access to the private properties. - const self = this; - this.valueOf = function IBT_valueOf(key) { - return key === PRIVATE_PROPS_KEY ? self : self._public; - }; - }, - - destroy: function IBT_destroy() { - if (this._wasDestroyed) - return; - if (this.parentMenu) - this.parentMenu.removeItem(this._public); - else if (!(this instanceof Separator) && this._hasFinishedInit) - browserManager.removeTopLevelItem(this._public); - browserManager.unregisterItem(this._public); - this._wasDestroyed = true; - }, - - get parentMenu() { - return this._parentMenu; - }, - - set parentMenu(val) { - throw new Error("The 'parentMenu' property is not intended to be set. " + - "Use menu.addItem(item) instead."); - }, - - set _isTopLevel(val) { - if (val) - this._workerReg = new WorkerRegistry(this._public); - else { - this._workerReg.destroy(); - delete this._workerReg; - } - }, - - get _topLevelItem() { - let topLevelItem = this._public; - let parentMenu = this.parentMenu; - while (parentMenu) { - topLevelItem = parentMenu; - parentMenu = parentMenu.parentMenu; - } - return topLevelItem; - } -}); - -// Item and Menu are composed of this trait. -const ActiveItemTrait = Trait.compose(ItemBaseTrait, EventEmitter, Trait({ - - _initActiveItem: function AIT__initActiveItem(opts, optRules, optsToNotSet) { - this._initBase(opts, optRules, - optsToNotSet.concat(["onMessage", "context"])); - - if ("onMessage" in opts) - this.on("message", opts.onMessage); - - // When a URL context is removed (by calling context.remove(urlContext)), we - // may need to create workers for windows containing pages that the item now - // matches. Likewise, when a URL context is added, we need to destroy - // workers for windows containing pages that the item now does not match. - // - // collection doesn't provide a way to listen for removals. utils/registry - // does, but it doesn't allow its elements to be enumerated. So as a hack, - // use a collection for item.context and replace its add and remove methods. - collection.addCollectionProperty(this, "context"); - if (opts.context) - this.context.add(opts.context); - - const self = this; - - let add = this.context.add; - this.context.add = function itemContextAdd() { - let args = Array.slice(arguments); - add.apply(self.context, args); - if (self._workerReg && args.some(function (a) a instanceof URLContext)) - self._workerReg.destroyUnneededWorkers(); - }; - - let remove = this.context.remove; - this.context.remove = function itemContextRemove() { - let args = Array.slice(arguments); - remove.apply(self.context, args); - if (self._workerReg && args.some(function (a) a instanceof URLContext)) - self._workerReg.createNeededWorkers(); - }; - }, - - // Workers are only created for top-level menu items. When a top-level item - // is later added to a Menu, its workers are destroyed. Well, all items start - // out as top-level because there is, unfortunately, no contextMenu.add(). So - // when an item is created and immediately added to a Menu, workers for it are - // needlessly created and destroyed. The point of this timeout is to avoid - // that. Items that are created and added to Menus in the same turn of the - // event loop won't have workers created for them. - _finishActiveItemInit: function AIT__finishActiveItemInit() { - numItemsWithUnfinishedInit++; - const self = this; - timer.setTimeout(function AIT__finishActiveItemInitTimeout() { - if (!self.parentMenu && !self._wasDestroyed) - browserManager.addTopLevelItem(self._public); - self._hasFinishedInit = true; - numItemsWithUnfinishedInit--; - }, 0); - }, - - get label() { - return this._label; - }, - - set label(val) { - this._label = validateOpt(val, this._optRules.label); - if (this._isInited) - browserManager.setItemLabel(this, this._label); - return this._label; - }, - - get image() { - return this._image; - }, - - set image(val) { - this._image = validateOpt(val, this._optRules.image); - if (this._isInited) - browserManager.setItemImage(this, this._image); - return this._image; - }, - - get contentScript() { - return this._contentScript; - }, - - set contentScript(val) { - this._contentScript = validateOpt(val, this._optRules.contentScript); - return this._contentScript; - }, - - get contentScriptFile() { - return this._contentScriptFile; - }, - - set contentScriptFile(val) { - this._contentScriptFile = - validateOpt(val, this._optRules.contentScriptFile); - return this._contentScriptFile; - } -})); - -// Item is composed of this trait. -const ItemTrait = Trait.compose(ActiveItemTrait, Trait({ - - _initItem: function IT__initItem(opts, optRules) { - this._initActiveItem(opts, optRules, []); - }, - - get data() { - return this._data; - }, - - set data(val) { - this._data = validateOpt(val, this._optRules.data); - if (this._isInited) - browserManager.setItemData(this, this._data); - return this._data; - }, - - toString: function IT_toString() { - return '[object Item "' + this.label + '"]'; - } -})); - -// The exported Item constructor. -function Item(options) { - let optRules = optionsRules(); - optRules.data = { - map: function (v) v.toString(), - is: ["string", "undefined"] - }; - - let item = ItemTrait.create(Item.prototype); - item._initItem(options, optRules); - - item._public = Cortex(item); - browserManager.registerItem(item._public); - item._finishActiveItemInit(); - - return item._public; -} - -// Menu is composed of this trait. -const MenuTrait = Trait.compose( - ActiveItemTrait.resolve({ destroy: "_destroyThisItem" }), - Trait({ - - _initMenu: function MT__initMenu(opts, optRules, optsToNotSet) { - this._items = []; - this._initActiveItem(opts, optRules, optsToNotSet); - }, - - destroy: function MT_destroy() { - while (this.items.length) - this.items[0].destroy(); - this._destroyThisItem(); - }, - - get items() { - return this._items; - }, - - set items(val) { - let newItems = validateOpt(val, this._optRules.items); - while (this.items.length) - this.items[0].destroy(); - newItems.forEach(function (i) this.addItem(i), this); - return newItems; - }, - - addItem: function MT_addItem(item) { - // First, remove the item from its current parent. - let privates = privateItem(item); - if (item.parentMenu) - item.parentMenu.removeItem(item); - else if (!(item instanceof Separator) && privates._hasFinishedInit) - browserManager.removeTopLevelItem(item); - - // Now add the item to this menu. - this._items.push(item); - privates._parentMenu = this._public; - browserManager.addItemToMenu(item, this._public); - }, - - removeItem: function MT_removeItem(item) { - let idx = this._items.indexOf(item); - if (idx < 0) - return; - this._items.splice(idx, 1); - privateItem(item)._parentMenu = null; - browserManager.removeItemFromMenu(item, this._public); - }, - - toString: function MT_toString() { - return '[object Menu "' + this.label + '"]'; - } -})); - -// The exported Menu constructor. -function Menu(options) { - let optRules = optionsRules(); - optRules.items = { - is: ["array"], - ok: function (v) { - return v.every(function (item) { - return (item instanceof Item) || - (item instanceof Menu) || - (item instanceof Separator); - }); - }, - msg: "items must be an array, and each element in the array must be an " + - "Item, Menu, or Separator." - }; - - let menu = MenuTrait.create(Menu.prototype); - - // We can't rely on _initBase to set the `items` property, because the menu - // needs to be registered with and added to the browserManager before any - // child items are added to it. - menu._initMenu(options, optRules, ["items"]); - - menu._public = Cortex(menu); - browserManager.registerItem(menu._public); - menu.items = options.items; - menu._finishActiveItemInit(); - - return menu._public; -} - -// The exported Separator constructor. -function Separator() { - let sep = ItemBaseTrait.create(Separator.prototype); - sep._initBase({}, {}, []); - - sep._public = Cortex(sep); - browserManager.registerItem(sep._public); - sep._hasFinishedInit = true; - return sep._public; -} - - -function Context() {} - -function PageContext() { - this.isCurrent = function PageContext_isCurrent(popupNode) { - let win = popupNode.ownerDocument.defaultView; - if (win && !win.getSelection().isCollapsed) - return false; - - let cursor = popupNode; - while (cursor && !(cursor instanceof Ci.nsIDOMHTMLHtmlElement)) { - if (NON_PAGE_CONTEXT_ELTS.some(function (iface) cursor instanceof iface)) - return false; - cursor = cursor.parentNode; - } - return true; - }; -} - -PageContext.prototype = new Context(); - -function SelectorContext(selector) { - let opts = apiUtils.validateOptions({ selector: selector }, { - selector: { - is: ["string"], - msg: "selector must be a string." - } - }); - - this.adjustPopupNode = function SelectorContext_adjustPopupNode(node) { - return closestMatchingAncestor(node); - }; - - this.isCurrent = function SelectorContext_isCurrent(popupNode) { - return !!closestMatchingAncestor(popupNode); - }; - - // Returns node if it matches selector, or the closest ancestor of node that - // matches, or null if node and none of its ancestors matches. - function closestMatchingAncestor(node) { - let cursor = node; - while (cursor) { - if (cursor.mozMatchesSelector(selector)) - return cursor; - if (cursor instanceof Ci.nsIDOMHTMLHtmlElement) - break; - cursor = cursor.parentNode; - } - return null; - } -} - -SelectorContext.prototype = new Context(); - -function SelectionContext() { - this.isCurrent = function SelectionContext_isCurrent(popupNode) { - let win = popupNode.ownerDocument.defaultView; - if (!win) - return false; - - let hasSelection = !win.getSelection().isCollapsed; - if (!hasSelection) { - // window.getSelection doesn't return a selection for text selected in a - // form field (see bug 85686), so before returning false we want to check - // if the popupNode is a text field. - let { selectionStart, selectionEnd } = popupNode; - hasSelection = !isNaN(selectionStart) && - !isNaN(selectionEnd) && - selectionStart !== selectionEnd; - } - return hasSelection; - }; -} - -SelectionContext.prototype = new Context(); - -function URLContext(patterns) { - let opts = apiUtils.validateOptions({ patterns: patterns }, { - patterns: { - map: function (v) apiUtils.getTypeOf(v) === "array" ? v : [v], - ok: function (v) v.every(function (p) typeof(p) === "string"), - msg: "patterns must be a string or an array of strings." - } - }); - try { - patterns = opts.patterns.map(function (p) new MatchPattern(p)); - } - catch (err) { - console.error("Error creating URLContext match pattern:"); - throw err; - } - - const self = this; - - this.isCurrent = function URLContext_isCurrent(popupNode) { - return self.isCurrentForURL(popupNode.ownerDocument.URL); - }; - - this.isCurrentForURL = function URLContext_isCurrentForURL(url) { - return patterns.some(function (p) p.test(url)); - }; -} - -URLContext.prototype = new Context(); - -exports.PageContext = apiUtils.publicConstructor(PageContext); -exports.SelectorContext = apiUtils.publicConstructor(SelectorContext); -exports.SelectionContext = apiUtils.publicConstructor(SelectionContext); -exports.URLContext = apiUtils.publicConstructor(URLContext); - - -// Returns a version of opt validated against the given rule. -function validateOpt(opt, rule) { - return apiUtils.validateOptions({ opt: opt }, { opt: rule }).opt; -} - -// Returns rules for apiUtils.validateOptions() common to Item and Menu. -function optionsRules() { - return { - context: { - is: ["undefined", "object", "array"], - ok: function (v) { - if (!v) - return true; - let arr = apiUtils.getTypeOf(v) === "array" ? v : [v]; - return arr.every(function (o) o instanceof Context); - }, - msg: "The 'context' option must be a Context object or an array of " + - "Context objects." - }, - label: { - map: function (v) v.toString(), - is: ["string"], - ok: function (v) !!v, - msg: "The item must have a non-empty string label." - }, - image: { - map: function (v) v.toString(), - is: ["string", "undefined", "null"] - }, - contentScript: { - is: ["string", "array", "undefined"], - ok: function (v) { - return apiUtils.getTypeOf(v) !== "array" || - v.every(function (s) typeof(s) === "string"); - } - }, - contentScriptFile: { - is: ["string", "array", "undefined"], - ok: function (v) { - if (!v) - return true; - let arr = apiUtils.getTypeOf(v) === "array" ? v : [v]; - try { - return arr.every(function (s) { - return apiUtils.getTypeOf(s) === "string" && - URL(s).scheme === 'resource'; - }); - } - catch (err) {} - return false; - }, - msg: "The 'contentScriptFile' option must be a local file URL or " + - "an array of local file URLs." - }, - onMessage: { - is: ["function", "undefined"] - } - }; -} - -// Does a binary search on elts, a NodeList, and returns the DOM element -// before which an item with targetLabel should be inserted. null is returned -// if the new item should be inserted at the end. -function insertionPoint(targetLabel, elts) { - let from = 0; - let to = elts.length - 1; - - while (from <= to) { - let i = Math.floor((from + to) / 2); - let comp = targetLabel.localeCompare(elts[i].getAttribute("label")); - if (comp < 0) - to = i - 1; - else if (comp > 0) - from = i + 1; - else - return elts[i]; - } - return elts[from] || null; -} - -// Builds an ID suitable for a DOM element from the given item ID. -// isInOverflowSubtree should be true if the returned element will be inserted -// into the DOM subtree rooted at the overflow menu. -function domEltIDFromItemID(itemID, isInOverflowSubtree) { - let suffix = isInOverflowSubtree ? "-overflow" : ""; - return jpSelf.id + "-context-menu-item-" + itemID + suffix; -} - -// Parses the item ID out of the given DOM element ID and returns it. If the -// element's ID is malformed or it indicates that the element was not created by -// the instance of the module calling this function, returns -1. -function itemIDFromDOMEltID(domEltID) { - let match = /^(.+?)-context-menu-item-([0-9]+)[-a-z]*$/.exec(domEltID); - return !match || match[1] !== jpSelf.id ? -1 : match[2]; -} - -// Returns the private version of the given public reflection. -function privateItem(publicItem) { - return publicItem.valueOf(PRIVATE_PROPS_KEY); -} - - -// A type of Worker tailored to our uses. -const ContextMenuWorker = Worker.compose({ - destroy: Worker.required, - - // Returns true if any context listeners are defined in the worker's port. - anyContextListeners: function CMW_anyContextListeners() { - return this._contentWorker.hasListenerFor("context"); - }, - - // Returns the first string or truthy value returned by a context listener in - // the worker's port. If none return a string or truthy value or if there are - // no context listeners, returns false. popupNode is the node that was - // context-clicked. - isAnyContextCurrent: function CMW_isAnyContextCurrent(popupNode) { - let results = this._contentWorker.emitSync("context", popupNode); - for (let i = 0; i < results.length; i++) { - let val = results[i]; - if (typeof val === "string" || val) - return val; - } - return false; - }, - - // Emits a click event in the worker's port. popupNode is the node that was - // context-clicked, and clickedItemData is the data of the item that was - // clicked. - fireClick: function CMW_fireClick(popupNode, clickedItemData) { - this._contentWorker.emitSync("click", popupNode, clickedItemData); - } -}); - - -// This class creates and stores content workers for pairs of menu items and -// content windows. Use one instance for each item. Not all pairs need a -// worker: if an item has a URL context that does not match a window's page, -// then no worker is created for the pair. -function WorkerRegistry(item) { - this.item = item; - - // inner window ID => { win, worker } - this.winWorkers = {}; - - // inner window ID => content window - this.winsWithoutWorkers = {}; -} - -WorkerRegistry.prototype = { - - // Registers a content window, creating a worker for it if it needs one. - registerContentWin: function WR_registerContentWin(win) { - let innerWinID = getInnerId(win); - if ((innerWinID in this.winWorkers) || - (innerWinID in this.winsWithoutWorkers)) - return; - if (this._doesURLNeedWorker(win.document.URL)) - this.winWorkers[innerWinID] = { win: win, worker: this._makeWorker(win) }; - else - this.winsWithoutWorkers[innerWinID] = win; - }, - - // Unregisters a content window, destroying its related worker if it has one. - unregisterContentWin: function WR_unregisterContentWin(innerWinID) { - if (innerWinID in this.winWorkers) { - let winWorker = this.winWorkers[innerWinID]; - winWorker.worker.destroy(); - delete winWorker.worker; - delete winWorker.win; - delete this.winWorkers[innerWinID]; - } - else - delete this.winsWithoutWorkers[innerWinID]; - }, - - // Creates a worker for each window that needs a worker but doesn't have one. - createNeededWorkers: function WR_createNeededWorkers() { - for (let innerWinID in this.winsWithoutWorkers) { - let win = this.winsWithoutWorkers[innerWinID] - delete this.winsWithoutWorkers[innerWinID]; - this.registerContentWin(win); - } - }, - - // Destroys the worker for each window that has a worker but doesn't need it. - destroyUnneededWorkers: function WR_destroyUnneededWorkers() { - for (let innerWinID in this.winWorkers) { - let winWorker = this.winWorkers[innerWinID]; - if (!this._doesURLNeedWorker(winWorker.win.document.URL)) { - this.unregisterContentWin(innerWinID); - this.winsWithoutWorkers[innerWinID] = winWorker.win; - } - } - }, - - // Returns the worker for the item-window pair or null if none exists. - find: function WR_find(contentWin) { - let innerWinID = getInnerId(contentWin); - return (innerWinID in this.winWorkers) ? - this.winWorkers[innerWinID].worker : - null; - }, - - // Unregisters all content windows from the registry, which destroys all - // workers. - destroy: function WR_destroy() { - for (let innerWinID in this.winWorkers) - this.unregisterContentWin(innerWinID); - for (let innerWinID in this.winsWithoutWorkers) - this.unregisterContentWin(innerWinID); - }, - - // Returns false if the item has a URL context that does not match the given - // URL. - _doesURLNeedWorker: function WR__doesURLNeedWorker(url) { - for (let ctxt in this.item.context) - if ((ctxt instanceof URLContext) && !ctxt.isCurrentForURL(url)) - return false; - return true; - }, - - _makeWorker: function WR__makeWorker(win) { - let worker = ContextMenuWorker({ - window: win, - contentScript: this.item.contentScript, - contentScriptFile: this.item.contentScriptFile, - onError: function (err) console.exception(err) - }); - let item = this.item; - worker.on("message", function workerOnMessage(msg) { - try { - privateItem(item)._emitOnObject(item, "message", msg); - } - catch (err) { - console.exception(err); - } - }); - return worker; - } -}; - - -// Mirrors state across all browser windows. Also responsible for detecting -// all content window loads and unloads. -let browserManager = { - topLevelItems: [], - browserWins: [], - - // inner window ID => content window - contentWins: {}, - - // Call this when a new item is created, top-level or not. - registerItem: function BM_registerItem(item) { - this.browserWins.forEach(function (w) w.registerItem(item)); - }, - - // Call this when an item is destroyed and won't be used again, top-level or - // not. - unregisterItem: function BM_unregisterItem(item) { - this.browserWins.forEach(function (w) w.unregisterItem(item)); - }, - - addTopLevelItem: function BM_addTopLevelItem(item) { - this.topLevelItems.push(item); - this.browserWins.forEach(function (w) w.addTopLevelItem(item)); - - // Create the item's worker registry and register all currently loaded - // content windows with it. - let privates = privateItem(item); - privates._isTopLevel = true; - for each (let win in this.contentWins) - privates._workerReg.registerContentWin(win); - }, - - removeTopLevelItem: function BM_removeTopLevelItem(item) { - let idx = this.topLevelItems.indexOf(item); - if (idx < 0) - throw new Error("Internal error: item not in top-level menu: " + item); - this.topLevelItems.splice(idx, 1); - this.browserWins.forEach(function (w) w.removeTopLevelItem(item)); - privateItem(item)._isTopLevel = false; - }, - - addItemToMenu: function BM_addItemToMenu(item, parentMenu) { - this.browserWins.forEach(function (w) w.addItemToMenu(item, parentMenu)); - }, - - removeItemFromMenu: function BM_removeItemFromMenu(item, parentMenu) { - this.browserWins.forEach(function (w) w.removeItemFromMenu(item, - parentMenu)); - }, - - setItemLabel: function BM_setItemLabel(item, label) { - this.browserWins.forEach(function (w) w.setItemLabel(item, label)); - }, - - setItemImage: function BM_setItemImage(item, imageURL) { - this.browserWins.forEach(function (w) w.setItemImage(item, imageURL)); - }, - - setItemData: function BM_setItemData(item, data) { - this.browserWins.forEach(function (w) w.setItemData(item, data)); - }, - - // Note that calling this method will cause onTrack to be called immediately - // for each currently open browser window. - init: function BM_init() { - require("./system/unload").ensure(this); - let windowTracker = WindowTracker(this); - - // Register content windows on content-document-global-created and - // unregister them on inner-window-destroyed. For rationale, see bug 667957 - // for the former and bug 642004 for the latter. - observerServ.add("content-document-global-created", - this._onDocGlobalCreated, this); - observerServ.add("inner-window-destroyed", - this._onInnerWinDestroyed, this); - }, - - _onDocGlobalCreated: function BM__onDocGlobalCreated(contentWin) { - let doc = contentWin.document; - if (doc.readyState == "loading") { - const self = this; - doc.addEventListener("readystatechange", function onReadyStateChange(e) { - if (e.target != doc || doc.readyState != "complete") - return; - doc.removeEventListener("readystatechange", onReadyStateChange, false); - self._registerContentWin(contentWin); - }, false); - } - else if (doc.readyState == "complete") - this._registerContentWin(contentWin); - }, - - _onInnerWinDestroyed: function BM__onInnerWinDestroyed(subj) { - this._unregisterContentWin( - subj.QueryInterface(Ci.nsISupportsPRUint64).data); - }, - - // Stores the given content window with the manager and registers it with each - // top-level item's worker registry. - _registerContentWin: function BM__registerContentWin(win) { - let innerID = getInnerId(win); - - // It's an error to call this method for the same window more than once, but - // we allow it in one case: when onTrack races _onDocGlobalCreated. (See - // the comment in onTrack.) Make sure the window is registered only once. - if (innerID in this.contentWins) - return; - - this.contentWins[innerID] = win; - this.topLevelItems.forEach(function (item) { - privateItem(item)._workerReg.registerContentWin(win); - }); - }, - - // Removes the given content window from the manager and unregisters it from - // each top-level item's worker registry. - _unregisterContentWin: function BM__unregisterContentWin(innerID) { - delete this.contentWins[innerID]; - this.topLevelItems.forEach(function (item) { - privateItem(item)._workerReg.unregisterContentWin(innerID); - }); - }, - - unload: function BM_unload() { - // The window tracker is unloaded at the same time this method is called, - // which causes onUntrack to be called for each open browser window, so - // there's no need to clean up browser windows here. - - while (this.topLevelItems.length) { - let item = this.topLevelItems[0]; - this.removeTopLevelItem(item); - this.unregisterItem(item); - } - delete this.contentWins; - }, - - // Registers a browser window with the manager. This is a WindowTracker - // callback. Note that this is called in two cases: for each newly opened - // chrome window, and for each chrome window that is open when the loader - // loads this module. - onTrack: function BM_onTrack(window) { - if (!isBrowser(window)) - return; - - let browserWin = new BrowserWindow(window); - this.browserWins.push(browserWin); - - // Register all loaded content windows in the browser window. Be sure to - // include frames and iframes. If onTrack is called as a result of a new - // browser window being opened, as opposed to the module being loaded, then - // this will race the content-document-global-created notification. That's - // OK, since _registerContentWin will not register the same content window - // more than once. - window.gBrowser.browsers.forEach(function (browser) { - let topContentWin = browser.contentWindow; - let allContentWins = Array.slice(topContentWin.frames); - allContentWins.push(topContentWin); - allContentWins.forEach(function (contentWin) { - if (contentWin.document.readyState == "complete") - this._registerContentWin(contentWin); - }, this); - }, this); - - // Add all top-level items and, recursively, their child items to the new - // browser window. - function addItemTree(item, parentMenu) { - browserWin.registerItem(item); - if (parentMenu) - browserWin.addItemToMenu(item, parentMenu); - else - browserWin.addTopLevelItem(item); - if (item instanceof Menu) - item.items.forEach(function (subitem) addItemTree(subitem, item)); - } - this.topLevelItems.forEach(function (item) addItemTree(item, null)); - }, - - // Unregisters a browser window from the manager. This is a WindowTracker - // callback. Note that this is called in two cases: for each newly closed - // chrome window, and for each chrome window that is open when this module is - // unloaded. - onUntrack: function BM_onUntrack(window) { - if (!isBrowser(window)) - return; - - // Remove the window from the window list. - let idx = 0; - for (; idx < this.browserWins.length; idx++) - if (this.browserWins[idx].window == window) - break; - if (idx == this.browserWins.length) - throw new Error("Internal error: browser window not found"); - let browserWin = this.browserWins.splice(idx, 1)[0]; - - // Remove all top-level items from the window. - this.topLevelItems.forEach(function (i) browserWin.removeTopLevelItem(i)); - browserWin.destroy(); - } -}; - - -// Responsible for creating and managing context menu item DOM elements for a -// browser window. Also responsible for providing a description of the window's -// current context and determining whether an item matches the current context. -// -// TODO: If other apps besides Firefox want to support the context menu in -// whatever way is appropriate for them, plugging in a substitute for or an -// adapter to this class should be the way to do it. Make it easy for them. -// See bug 560716. -function BrowserWindow(window) { - this.window = window; - this.doc = window.document; - - let popupDOMElt = this.doc.getElementById("contentAreaContextMenu"); - if (!popupDOMElt) - throw new Error("Internal error: Context menu popup not found."); - this.contextMenuPopup = new ContextMenuPopup(popupDOMElt, this); - - // item ID => { item, domElt, overflowDOMElt, popup, overflowPopup } - // item may or may not be top-level. domElt is the item's DOM element - // contained in the subtree rooted in the top-level context menu. - // overflowDOMElt is the item's DOM element contained in the subtree rooted in - // the overflow submenu. popup and overflowPopup are only defined if the item - // is a Menu; they're the Popup instances containing the Menu's child items, - // with the aforementioned distinction between top-level and overflow - // subtrees. - this.items = {}; -} - -BrowserWindow.prototype = { - - // Creates and stores DOM elements for the given item, top-level or not. - registerItem: function BW_registerItem(item) { - // this.items[id] is referenced by _makeMenu, so it needs to be defined - // before _makeDOMElt is called. - let props = { item: item }; - this.items[privateItem(item)._id] = props; - props.domElt = this._makeDOMElt(item, false); - props.overflowDOMElt = this._makeDOMElt(item, true); - }, - - // Removes the given item's DOM elements from the store. - unregisterItem: function BW_unregisterItem(item) { - delete this.items[privateItem(item)._id]; - }, - - addTopLevelItem: function BW_addTopLevelItem(item) { - this.contextMenuPopup.addItem(item); - }, - - removeTopLevelItem: function BW_removeTopLevelItem(item) { - this.contextMenuPopup.removeItem(item); - }, - - addItemToMenu: function BW_addItemToMenu(item, parentMenu) { - let { popup, overflowPopup } = this.items[privateItem(parentMenu)._id]; - popup.addItem(item); - overflowPopup.addItem(item); - }, - - removeItemFromMenu: function BW_removeItemFromMenu(item, parentMenu) { - let { popup, overflowPopup } = this.items[privateItem(parentMenu)._id]; - popup.removeItem(item); - overflowPopup.removeItem(item); - }, - - setItemLabel: function BW_setItemLabel(item, label) { - let privates = privateItem(item); - let { domElt, overflowDOMElt } = this.items[privates._id]; - this._setDOMEltLabel(domElt, label); - this._setDOMEltLabel(overflowDOMElt, label); - if (!item.parentMenu && privates._hasFinishedInit) - this.contextMenuPopup.itemLabelDidChange(item); - }, - - _setDOMEltLabel: function BW__setDOMEltLabel(domElt, label) { - domElt.setAttribute("label", label); - }, - - setItemImage: function BW_setItemImage(item, imageURL) { - let { domElt, overflowDOMElt } = this.items[privateItem(item)._id]; - let isMenu = item instanceof Menu; - this._setDOMEltImage(domElt, imageURL, isMenu); - this._setDOMEltImage(overflowDOMElt, imageURL, isMenu); - }, - - _setDOMEltImage: function BW__setDOMEltImage(domElt, imageURL, isMenu) { - if (!imageURL) { - domElt.removeAttribute("image"); - domElt.classList.remove("menu-iconic"); - domElt.classList.remove("menuitem-iconic"); - } - else { - domElt.setAttribute("image", imageURL); - domElt.classList.add(isMenu ? "menu-iconic" : "menuitem-iconic"); - } - }, - - setItemData: function BW_setItemData(item, data) { - let { domElt, overflowDOMElt } = this.items[privateItem(item)._id]; - this._setDOMEltData(domElt, data); - this._setDOMEltData(overflowDOMElt, data); - }, - - _setDOMEltData: function BW__setDOMEltData(domElt, data) { - domElt.setAttribute("value", data); - }, - - // The context specified for a top-level item may not match exactly the real - // context that triggers it. For example, if the user context-clicks a span - // inside an anchor, we want items that specify an anchor context to be - // triggered, but the real context will indicate that the span was clicked, - // not the anchor. Where the real context and an item's context conflict, - // clients should be given the item's context, and this method can be used to - // make such adjustments. Returns an adjusted popupNode. - adjustPopupNode: function BW_adjustPopupNode(popupNode, topLevelItem) { - for (let ctxt in topLevelItem.context) { - if (typeof(ctxt.adjustPopupNode) === "function") { - let ctxtNode = ctxt.adjustPopupNode(popupNode); - if (ctxtNode) { - popupNode = ctxtNode; - break; - } - } - } - return popupNode; - }, - - // Returns true if all of item's contexts are current in the window. - areAllContextsCurrent: function BW_areAllContextsCurrent(item, popupNode) { - let win = popupNode.ownerDocument.defaultView; - let worker = privateItem(item)._workerReg.find(win); - - // If the worker for the item-window pair doesn't exist (e.g., because the - // page hasn't loaded yet), we can't really make a good decision since the - // content script may have a context listener. So just don't show the item - // at all. - if (!worker) - return false; - - // If there are no contexts given at all, the page context applies. - let hasContentContext = worker.anyContextListeners(); - if (!hasContentContext && !item.context.length) - return new PageContext().isCurrent(popupNode); - - // Otherwise, determine if all given contexts are current. Evaluate the - // declarative contexts first and the worker's context listeners last. That - // way the listener might be able to avoid some work. - let curr = true; - for (let ctxt in item.context) { - curr = curr && ctxt.isCurrent(popupNode); - if (!curr) - return false; - } - return !hasContentContext || worker.isAnyContextCurrent(popupNode); - }, - - // Returns the node the user context-clicked to invoke the context menu. - // For Gecko 2.0 and later, triggerNode is this node; - // if it's falsey, document.popupNode is used. - capturePopupNode: function BW_capturePopupNode(triggerNode) { - var popupNode = triggerNode || this.doc.popupNode; - if (!popupNode) - console.warn("popupNode is null."); - return popupNode; - }, - - destroy: function BW_destroy() { - this.contextMenuPopup.destroy(); - delete this.window; - delete this.doc; - delete this.items; - }, - - // Emits a click event in the port of the content worker related to given top- - // level item and popupNode's content window. Listeners will be passed - // popupNode and clickedItemData. - fireClick: function BW_fireClick(topLevelItem, popupNode, clickedItemData) { - let win = popupNode.ownerDocument.defaultView; - let worker = privateItem(topLevelItem)._workerReg.find(win); - if (worker) - worker.fireClick(popupNode, clickedItemData); - }, - - _makeDOMElt: function BW__makeDOMElt(item, isInOverflowSubtree) { - let elt = item instanceof Item ? this._makeMenuitem(item) : - item instanceof Menu ? this._makeMenu(item, isInOverflowSubtree) : - item instanceof Separator ? this._makeSeparator() : - null; - if (!elt) - throw new Error("Internal error: can't make element, unknown item type"); - - elt.id = domEltIDFromItemID(privateItem(item)._id, isInOverflowSubtree); - elt.classList.add(ITEM_CLASS); - return elt; - }, - - // Returns a new xul:menu representing the menu. - _makeMenu: function BW__makeMenu(menu, isInOverflowSubtree) { - let menuElt = this.doc.createElement("menu"); - this._setDOMEltLabel(menuElt, menu.label); - if (menu.image) - this._setDOMEltImage(menuElt, menu.image, true); - let popupElt = this.doc.createElement("menupopup"); - menuElt.appendChild(popupElt); - - let popup = new Popup(popupElt, this, isInOverflowSubtree); - let props = this.items[privateItem(menu)._id]; - if (isInOverflowSubtree) - props.overflowPopup = popup; - else - props.popup = popup; - - return menuElt; - }, - - // Returns a new xul:menuitem representing the item. - _makeMenuitem: function BW__makeMenuitem(item) { - let elt = this.doc.createElement("menuitem"); - this._setDOMEltLabel(elt, item.label); - if (item.image) - this._setDOMEltImage(elt, item.image, false); - if (item.data) - this._setDOMEltData(elt, item.data); - return elt; - }, - - // Returns a new xul:menuseparator. - _makeSeparator: function BW__makeSeparator() { - return this.doc.createElement("menuseparator"); - } -}; - - -// Responsible for adding DOM elements to and removing them from poupDOMElt. -function Popup(popupDOMElt, browserWin, isInOverflowSubtree) { - this.popupDOMElt = popupDOMElt; - this.browserWin = browserWin; - this.isInOverflowSubtree = isInOverflowSubtree; -} - -Popup.prototype = { - - addItem: function Popup_addItem(item) { - let props = this.browserWin.items[privateItem(item)._id]; - let elt = this.isInOverflowSubtree ? props.overflowDOMElt : props.domElt; - this.popupDOMElt.appendChild(elt); - }, - - removeItem: function Popup_removeItem(item) { - let props = this.browserWin.items[privateItem(item)._id]; - let elt = this.isInOverflowSubtree ? props.overflowDOMElt : props.domElt; - this.popupDOMElt.removeChild(elt); - } -}; - - -// Represents a browser window's context menu popup. Responsible for hiding and -// showing items according to the browser window's current context and for -// handling item clicks. -function ContextMenuPopup(popupDOMElt, browserWin) { - this.popupDOMElt = popupDOMElt; - this.browserWin = browserWin; - this.doc = popupDOMElt.ownerDocument; - - // item ID => item - // Calling this variable "topLevelItems" is redundant, since Popup and - // ContextMenuPopup are only responsible for their child items, not all their - // descendant items. But calling it "items" might encourage one to believe - // otherwise, so topLevelItems it is. - this.topLevelItems = {}; - - popupDOMElt.addEventListener("popupshowing", this, false); - popupDOMElt.addEventListener("command", this, false); -} - -ContextMenuPopup.prototype = { - - addItem: function CMP_addItem(item) { - this._ensureStaticEltsExist(); - let itemID = privateItem(item)._id; - this.topLevelItems[itemID] = item; - let props = this.browserWin.items[itemID]; - props.domElt.classList.add(TOPLEVEL_ITEM_CLASS); - props.overflowDOMElt.classList.add(OVERFLOW_ITEM_CLASS); - this._insertItemInSortedOrder(item); - }, - - removeItem: function CMP_removeItem(item) { - let itemID = privateItem(item)._id; - delete this.topLevelItems[itemID]; - let { domElt, overflowDOMElt } = this.browserWin.items[itemID]; - domElt.classList.remove(TOPLEVEL_ITEM_CLASS); - overflowDOMElt.classList.remove(OVERFLOW_ITEM_CLASS); - this.popupDOMElt.removeChild(domElt); - this._overflowPopup.removeChild(overflowDOMElt); - }, - - // Call this after the item's label changes. This re-inserts the item into - // the popup so that it remains in sorted order. - itemLabelDidChange: function CMP_itemLabelDidChange(item) { - let itemID = privateItem(item)._id; - let { domElt, overflowDOMElt } = this.browserWin.items[itemID]; - this.popupDOMElt.removeChild(domElt); - this._overflowPopup.removeChild(overflowDOMElt); - this._insertItemInSortedOrder(item); - }, - - destroy: function CMP_destroy() { - // If there are no more items from any instance of the module, remove the - // separator and overflow submenu, if they exist. - let elts = this._topLevelElts; - if (!elts.length) { - let submenu = this._overflowMenu; - if (submenu) - this.popupDOMElt.removeChild(submenu); - - let sep = this._separator; - if (sep) - this.popupDOMElt.removeChild(sep); - } - - this.popupDOMElt.removeEventListener("popupshowing", this, false); - this.popupDOMElt.removeEventListener("command", this, false); - }, - - handleEvent: function CMP_handleEvent(event) { - try { - if (event.type === "command") - this._handleClick(event.target); - else if (event.type === "popupshowing" && - event.target === this.popupDOMElt) - this._handlePopupShowing(); - } - catch (err) { - console.exception(err); - } - }, - - // command events bubble to the context menu's top-level xul:menupopup and are - // caught here. - _handleClick: function CMP__handleClick(clickedDOMElt) { - if (!clickedDOMElt.classList.contains(ITEM_CLASS)) - return; - let itemID = itemIDFromDOMEltID(clickedDOMElt.id); - if (itemID < 0) - return; - let { item, domElt, overflowDOMElt } = this.browserWin.items[itemID]; - - // Bail if the DOM element was not created by this module instance. In - // real-world add-ons, the itemID < 0 check above is sufficient, but for the - // unit test the JID never changes, making this necessary. - if (clickedDOMElt != domElt && clickedDOMElt != overflowDOMElt) - return; - - let topLevelItem = privateItem(item)._topLevelItem; - let popupNode = this.browserWin.adjustPopupNode(this._getPopupNode(), - topLevelItem); - this.browserWin.fireClick(topLevelItem, popupNode, item.data); - }, - - _getPopupNode: function CMP__getPopupNode() { - // popupDOMElt.triggerNode was added in Gecko 2.0 by bug 383930. The || is - // to avoid a Spidermonkey strict warning on earlier versions. - let triggerNode = this.popupDOMElt.triggerNode || undefined; - return this.browserWin.capturePopupNode(triggerNode); - }, - - // popupshowing is used to show top-level items that match the browser - // window's current context and hide items that don't. Each module instance - // is responsible for showing and hiding the items it owns. - _handlePopupShowing: function CMP__handlePopupShowing() { - - // If there are items queued up to finish initializing, let them go first. - // Otherwise the overflow submenu and menu separator may end up in an - // inappropriate state when those items are later added to the menu. - if (numItemsWithUnfinishedInit) { - const self = this; - timer.setTimeout(function popupShowingTryAgain() { - self._handlePopupShowing(); - }, 0); - return; - } - - let popupNode = this._getPopupNode(); - // Show and hide items. Set a "jetpackContextCurrent" property on the - // DOM elements to signal which of our items match the current context. - for (let itemID in this.topLevelItems) { - let item = this.topLevelItems[itemID] - let areContextsCurr = - this.browserWin.areAllContextsCurrent(item, popupNode); - - // Change the item's label if the return value was a string. - if (typeof(areContextsCurr) === "string") { - item.label = areContextsCurr; - areContextsCurr = true; - } - - let { domElt, overflowDOMElt } = this.browserWin.items[itemID]; - domElt.jetpackContextCurrent = areContextsCurr; - domElt.hidden = !areContextsCurr; - overflowDOMElt.jetpackContextCurrent = areContextsCurr; - overflowDOMElt.hidden = !areContextsCurr; - } - - // Get the total number of items that match the current context. It's a - // little tricky: There may be other instances of this module loaded, - // each hiding and showing their items. So we can't base this number on - // only our items, or on the hidden state of items. That's why we set - // the jetpackContextCurrent property above. The last instance to run - // will leave the menupopup in the correct state. - let elts = this._topLevelElts; - let numShown = Array.reduce(elts, function (total, elt) { - return total + (elt.jetpackContextCurrent ? 1 : 0); - }, 0); - - // If too many items are shown, show the submenu and hide the top-level - // items. Otherwise, hide the submenu and show the top-level items. - let overflow = numShown > this._overflowThreshold; - if (overflow) - Array.forEach(elts, function (e) e.hidden = true); - - let submenu = this._overflowMenu; - if (submenu) - submenu.hidden = !overflow; - - // If no items are shown, hide the menu separator. - let sep = this._separator; - if (sep) - sep.hidden = numShown === 0; - }, - - // Adds the menu separator and overflow submenu if they don't exist. - _ensureStaticEltsExist: function CMP__ensureStaticEltsExist() { - let sep = this._separator; - if (!sep) { - sep = this._makeSeparator(); - this.popupDOMElt.appendChild(sep); - } - - let submenu = this._overflowMenu; - if (!submenu) { - submenu = this._makeOverflowMenu(); - submenu.hidden = true; - this.popupDOMElt.insertBefore(submenu, sep.nextSibling); - } - }, - - // Inserts the given item's DOM element into the popup in sorted order. - _insertItemInSortedOrder: function CMP__insertItemInSortedOrder(item) { - let props = this.browserWin.items[privateItem(item)._id]; - this.popupDOMElt.insertBefore( - props.domElt, insertionPoint(item.label, this._topLevelElts)); - this._overflowPopup.insertBefore( - props.overflowDOMElt, insertionPoint(item.label, this._overflowElts)); - }, - - // Creates and returns the xul:menu that's shown when too many items are added - // to the popup. - _makeOverflowMenu: function CMP__makeOverflowMenu() { - let submenu = this.doc.createElement("menu"); - submenu.id = OVERFLOW_MENU_ID; - submenu.setAttribute("label", OVERFLOW_MENU_LABEL); - let popup = this.doc.createElement("menupopup"); - popup.id = OVERFLOW_POPUP_ID; - submenu.appendChild(popup); - return submenu; - }, - - // Creates and returns the xul:menuseparator that separates the standard - // context menu items from our items. - _makeSeparator: function CMP__makeSeparator() { - let elt = this.doc.createElement("menuseparator"); - elt.id = SEPARATOR_ID; - return elt; - }, - - // Returns the item elements contained in the overflow menu, a NodeList. - get _overflowElts() { - return this._overflowPopup.getElementsByClassName(OVERFLOW_ITEM_CLASS); - }, - - // Returns the overflow xul:menu. - get _overflowMenu() { - return this.doc.getElementById(OVERFLOW_MENU_ID); - }, - - // Returns the overflow xul:menupopup. - get _overflowPopup() { - return this.doc.getElementById(OVERFLOW_POPUP_ID); - }, - - // Returns the OVERFLOW_THRESH_PREF pref value if it exists or - // OVERFLOW_THRESH_DEFAULT if it doesn't. - get _overflowThreshold() { - let prefs = require("./preferences/service"); - return prefs.get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); - }, - - // Returns the xul:menuseparator. - get _separator() { - return this.doc.getElementById(SEPARATOR_ID); - }, - - // Returns the item elements contained in the top-level menu, a NodeList. - get _topLevelElts() { - return this.popupDOMElt.getElementsByClassName(TOPLEVEL_ITEM_CLASS); - } -}; - - -// Init the browserManager only after setting prototypes and such above, because -// it will cause browserManager.onTrack to be called immediately if there are -// open windows. -browserManager.init(); diff --git a/tools/addon-sdk-1.12/lib/sdk/core/heritage.js b/tools/addon-sdk-1.12/lib/sdk/core/heritage.js deleted file mode 100644 index 1c62e6c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/core/heritage.js +++ /dev/null @@ -1,146 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 expandtab */ -/* 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'; - -module.metadata = { - "stability": "unstable" -}; - -var getPrototypeOf = Object.getPrototypeOf; -var getNames = Object.getOwnPropertyNames; -var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; -var create = Object.create; -var freeze = Object.freeze; -var unbind = Function.call.bind(Function.bind, Function.call); - -// This shortcut makes sure that we do perform desired operations, even if -// associated methods have being overridden on the used object. -var owns = unbind(Object.prototype.hasOwnProperty); -var apply = unbind(Function.prototype.apply); -var slice = Array.slice || unbind(Array.prototype.slice); -var reduce = Array.reduce || unbind(Array.prototype.reduce); -var map = Array.map || unbind(Array.prototype.map); -var concat = Array.concat || unbind(Array.prototype.concat); - -// Utility function to get own properties descriptor map. -function getOwnPropertyDescriptors(object) { - return reduce(getNames(object), function(descriptor, name) { - descriptor[name] = getOwnPropertyDescriptor(object, name); - return descriptor; - }, {}); -} - -/** - * Takes `source` object as an argument and returns identical object - * with the difference that all own properties will be non-enumerable - */ -function obscure(source) { - var descriptor = reduce(getNames(source), function(descriptor, name) { - var property = getOwnPropertyDescriptor(source, name); - property.enumerable = false; - descriptor[name] = property; - return descriptor; - }, {}); - return create(getPrototypeOf(source), descriptor); -} -exports.obscure = obscure; - -/** - * Takes arbitrary number of source objects and returns fresh one, that - * inherits from the same prototype as a first argument and implements all - * own properties of all argument objects. If two or more argument objects - * have own properties with the same name, the property is overridden, with - * precedence from right to left, implying, that properties of the object on - * the left are overridden by a same named property of the object on the right. - */ -var mix = function(source) { - var descriptor = reduce(slice(arguments), function(descriptor, source) { - return reduce(getNames(source), function(descriptor, name) { - descriptor[name] = getOwnPropertyDescriptor(source, name); - return descriptor; - }, descriptor); - }, {}); - - return create(getPrototypeOf(source), descriptor); -}; -exports.mix = mix; - -/** - * Returns a frozen object with that inherits from the given `prototype` and - * implements all own properties of the given `properties` object. - */ -function extend(prototype, properties) { - return freeze(create(prototype, getOwnPropertyDescriptors(properties))); -} -exports.extend = extend; - -/** - * Returns a constructor function with a proper `prototype` setup. Returned - * constructor's `prototype` inherits from a given `options.extends` or - * `Class.prototype` if omitted and implements all the properties of the - * given `option`. If `options.implemens` array is passed, it's elements - * will be mixed into prototype as well. Also, `options.extends` can be - * a function or a prototype. If function than it's prototype is used as - * an ancestor of the prototype, if it's an object that it's used directly. - * Also `options.implements` may contain functions or objects, in case of - * functions their prototypes are used for mixing. - */ -var Class = new function() { - function prototypeOf(input) { - return typeof(input) === 'function' ? input.prototype : input; - } - var none = freeze([]); - - return function Class(options) { - // Create descriptor with normalized `options.extends` and - // `options.implements`. - var descriptor = { - // Normalize extends property of `options.extends` to a prototype object - // in case it's constructor. If property is missing that fallback to - // `Type.prototype`. - extends: owns(options, 'extends') ? - prototypeOf(options.extends) : Class.prototype, - // Normalize `options.implements` to make sure that it's array of - // prototype objects instead of constructor functions. - implements: owns(options, 'implements') ? - freeze(map(options.implements, prototypeOf)) : none - }; - - // Create array of property descriptors who's properties will be defined - // on the resulting prototype. Note: Using reflection `concat` instead of - // method as it may be overridden. - var descriptors = concat(descriptor.implements, options, descriptor); - // Create `prototype` that inherits from given ancestor passed as - // `options.extends`, falling back to `Type.prototype`, implementing all - // properties of given `options.implements` and `options` itself. - var prototype = extend(descriptor.extends, mix.apply(mix, descriptors)); - - // Note: we use reflection `apply` in the constructor instead of method - // call since later may be overridden. - function constructor() { - return apply(prototype.constructor, create(prototype), arguments); - } - constructor.prototype = prototype; - return freeze(constructor); - }; -} -Class.prototype = extend(null, obscure({ - constructor: function constructor() { - this.initialize.apply(this, arguments); - return this; - }, - initialize: function initialize() { - // Do your initialization logic here - }, - // Copy useful properties from `Object.prototype`. - toString: Object.prototype.toString, - toLocaleString: Object.prototype.toLocaleString, - toSource: Object.prototype.toSource, - valueOf: Object.prototype.valueOf, - isPrototypeOf: Object.prototype.isPrototypeOf -})); -exports.Class = freeze(Class); diff --git a/tools/addon-sdk-1.12/lib/sdk/core/namespace.js b/tools/addon-sdk-1.12/lib/sdk/core/namespace.js deleted file mode 100644 index 3ceb73b..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/core/namespace.js +++ /dev/null @@ -1,43 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const create = Object.create; -const prototypeOf = Object.getPrototypeOf; - -/** - * Returns a new namespace, function that may can be used to access an - * namespaced object of the argument argument. Namespaced object are associated - * with owner objects via weak references. Namespaced objects inherit from the - * owners ancestor namespaced object. If owner's ancestor is `null` then - * namespaced object inherits from given `prototype`. Namespaces can be used - * to define internal APIs that can be shared via enclosing `namespace` - * function. - * @examples - * const internals = ns(); - * internals(object).secret = secret; - */ -function ns() { - const map = new WeakMap(); - return function namespace(target) { - if (!target) // If `target` is not an object return `target` itself. - return target; - // If target has no namespaced object yet, create one that inherits from - // the target prototype's namespaced object. - if (!map.has(target)) - map.set(target, create(namespace(prototypeOf(target) || null))); - - return map.get(target); - }; -}; - -// `Namespace` is a e4x function in the scope, so we export the function also as -// `ns` as alias to avoid clashing. -exports.ns = ns; -exports.Namespace = ns; diff --git a/tools/addon-sdk-1.12/lib/sdk/core/promise.js b/tools/addon-sdk-1.12/lib/sdk/core/promise.js deleted file mode 100644 index 2506c80..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/core/promise.js +++ /dev/null @@ -1,230 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 expandtab */ -/*jshint asi: true undef: true es5: true node: true browser: true devel: true - forin: true latedef: false */ -/*global define: true, Cu: true, __URI__: true */ -;(function(id, factory) { // Module boilerplate :( - if (typeof(define) === 'function') { // RequireJS - define(factory); - } else if (typeof(require) === 'function') { // CommonJS - factory.call(this, require, exports, module); - } else if (~String(this).indexOf('BackstagePass')) { // JSM - factory(function require(uri) { - var imports = {}; - this['Components'].utils.import(uri, imports); - return imports; - }, this, { uri: __URI__, id: id }); - this.EXPORTED_SYMBOLS = Object.keys(this); - } else { // Browser or alike - var globals = this - factory(function require(id) { - return globals[id]; - }, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id }); - } -}).call(this, 'loader', function(require, exports, module) { - -'use strict'; - -module.metadata = { - "stability": "unstable" -}; - -function resolution(value) { - /** - Returns non-standard compliant (`then` does not returns a promise) promise - that resolves to a given `value`. Used just internally only. - **/ - return { then: function then(resolve) { resolve(value) } } -} - -function rejection(reason) { - /** - Returns non-standard compliant promise (`then` does not returns a promise) - that rejects with a given `reason`. This is used internally only. - **/ - return { then: function then(resolve, reject) { reject(reason) } } -} - -function attempt(f) { - /** - Returns wrapper function that delegates to `f`. If `f` throws then captures - error and returns promise that rejects with a thrown error. Otherwise returns - return value. (Internal utility) - **/ - return function effort(options) { - try { return f(options) } - catch(error) { return rejection(error) } - } -} - -function isPromise(value) { - /** - Returns true if given `value` is promise. Value is assumed to be promise if - it implements `then` method. - **/ - return value && typeof(value.then) === 'function' -} - -function defer(prototype) { - /** - Returns object containing following properties: - - `promise` Eventual value representation implementing CommonJS [Promises/A] - (http://wiki.commonjs.org/wiki/Promises/A) API. - - `resolve` Single shot function that resolves returned `promise` with a given - `value` argument. - - `reject` Single shot function that rejects returned `promise` with a given - `reason` argument. - - Given `prototype` argument is used as a prototype of the returned `promise` - allowing one to implement additional API. If prototype is not passed then - it falls back to `Object.prototype`. - - ## Examples - - // Simple usage. - var deferred = defer() - deferred.promise.then(console.log, console.error) - deferred.resolve(value) - - // Advanced usage - var prototype = { - get: function get(name) { - return this.then(function(value) { - return value[name]; - }) - } - } - - var foo = defer(prototype) - deferred.promise.get('name').then(console.log) - deferred.resolve({ name: 'Foo' }) - //=> 'Foo' - */ - var pending = [], result - prototype = (prototype || prototype === null) ? prototype : Object.prototype - - // Create an object implementing promise API. - var promise = Object.create(prototype, { - then: { value: function then(resolve, reject) { - // create a new deferred using a same `prototype`. - var deferred = defer(prototype) - // If `resolve / reject` callbacks are not provided. - resolve = resolve ? attempt(resolve) : resolution - reject = reject ? attempt(reject) : rejection - - // Create a listeners for a enclosed promise resolution / rejection that - // delegate to an actual callbacks and resolve / reject returned promise. - function resolved(value) { deferred.resolve(resolve(value)) } - function rejected(reason) { deferred.resolve(reject(reason)) } - - // If promise is pending register listeners. Otherwise forward them to - // resulting resolution. - if (pending) pending.push([ resolved, rejected ]) - else result.then(resolved, rejected) - - return deferred.promise - }} - }) - - var deferred = { - promise: promise, - resolve: function resolve(value) { - /** - Resolves associated `promise` to a given `value`, unless it's already - resolved or rejected. - **/ - if (pending) { - // store resolution `value` as a promise (`value` itself may be a - // promise), so that all subsequent listeners can be forwarded to it, - // which either resolves immediately or forwards if `value` is - // a promise. - result = isPromise(value) ? value : resolution(value) - // forward all pending observers. - while (pending.length) result.then.apply(result, pending.shift()) - // mark promise as resolved. - pending = null - } - }, - reject: function reject(reason) { - /** - Rejects associated `promise` with a given `reason`, unless it's already - resolved / rejected. - **/ - deferred.resolve(rejection(reason)) - } - } - - return deferred -} -exports.defer = defer - -function resolve(value, prototype) { - /** - Returns a promise resolved to a given `value`. Optionally second `prototype` - arguments my be provided to be used as a prototype for a returned promise. - **/ - var deferred = defer(prototype) - deferred.resolve(value) - return deferred.promise -} -exports.resolve = resolve - -function reject(reason, prototype) { - /** - Returns a promise that is rejected with a given `reason`. Optionally second - `prototype` arguments my be provided to be used as a prototype for a returned - promise. - **/ - var deferred = defer(prototype) - deferred.reject(reason) - return deferred.promise -} -exports.reject = reject - -var promised = (function() { - // Note: Define shortcuts and utility functions here in order to avoid - // slower property accesses and unnecessary closure creations on each - // call of this popular function. - - var call = Function.call - var concat = Array.prototype.concat - - // Utility function that does following: - // execute([ f, self, args...]) => f.apply(self, args) - function execute(args) { return call.apply(call, args) } - - // Utility function that takes promise of `a` array and maybe promise `b` - // as arguments and returns promise for `a.concat(b)`. - function promisedConcat(promises, unknown) { - return promises.then(function(values) { - return resolve(unknown).then(function(value) { - return values.concat([ value ]) - }) - }) - } - - return function promised(f, prototype) { - /** - Returns a wrapped `f`, which when called returns a promise that resolves to - `f(...)` passing all the given arguments to it, which by the way may be - promises. Optionally second `prototype` argument may be provided to be used - a prototype for a returned promise. - - ## Example - - var promise = promised(Array)(1, promise(2), promise(3)) - promise.then(console.log) // => [ 1, 2, 3 ] - **/ - - return function promised() { - // create array of [ f, this, args... ] - return concat.apply([ f, this ], arguments). - // reduce it via `promisedConcat` to get promised array of fulfillments - reduce(promisedConcat, resolve([], prototype)). - // finally map that to promise of `f.apply(this, args...)` - then(execute) - } - } -})() -exports.promised = promised - -}) diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/api-utils.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/api-utils.js deleted file mode 100644 index 521ad6e..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/api-utils.js +++ /dev/null @@ -1,158 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "deprecated" -}; - -const memory = require("./memory"); -// The possible return values of getTypeOf. -const VALID_TYPES = [ - "array", - "boolean", - "function", - "null", - "number", - "object", - "string", - "undefined", -]; - -/** - * Returns a function C that creates instances of privateCtor. C may be called - * with or without the new keyword. The prototype of each instance returned - * from C is C.prototype, and C.prototype is an object whose prototype is - * privateCtor.prototype. Instances returned from C will therefore be instances - * of both C and privateCtor. Additionally, the constructor of each instance - * returned from C is C. - * - * @param privateCtor - * A constructor. - * @return A function that makes new instances of privateCtor. - */ -exports.publicConstructor = function publicConstructor(privateCtor) { - function PublicCtor() { - let obj = { constructor: PublicCtor, __proto__: PublicCtor.prototype }; - memory.track(obj, privateCtor.name); - privateCtor.apply(obj, arguments); - return obj; - } - PublicCtor.prototype = { __proto__: privateCtor.prototype }; - return PublicCtor; -}; - -/** - * Returns a validated options dictionary given some requirements. If any of - * the requirements are not met, an exception is thrown. - * - * @param options - * An object, the options dictionary to validate. It's not modified. - * If it's null or otherwise falsey, an empty object is assumed. - * @param requirements - * An object whose keys are the expected keys in options. Any key in - * options that is not present in requirements is ignored. Each value - * in requirements is itself an object describing the requirements of - * its key. There are four optional keys in this object: - * map: A function that's passed the value of the key in options. - * map's return value is taken as the key's value in the final - * validated options, is, and ok. If map throws an exception - * it's caught and discarded, and the key's value is its value in - * options. - * is: An array containing any number of the typeof type names. If - * the key's value is none of these types, it fails validation. - * Arrays and null are identified by the special type names - * "array" and "null"; "object" will not match either. No type - * coercion is done. - * ok: A function that's passed the key's value. If it returns - * false, the value fails validation. - * msg: If the key's value fails validation, an exception is thrown. - * This string will be used as its message. If undefined, a - * generic message is used, unless is is defined, in which case - * the message will state that the value needs to be one of the - * given types. - * @return An object whose keys are those keys in requirements that are also in - * options and whose values are the corresponding return values of map - * or the corresponding values in options. Note that any keys not - * shared by both requirements and options are not in the returned - * object. - */ -exports.validateOptions = function validateOptions(options, requirements) { - options = options || {}; - let validatedOptions = {}; - let mapThrew = false; - - for (let key in requirements) { - let req = requirements[key]; - let [optsVal, keyInOpts] = (key in options) ? - [options[key], true] : - [undefined, false]; - if (req.map) { - try { - optsVal = req.map(optsVal); - } - catch (err) { - mapThrew = true; - } - } - if (req.is) { - // Sanity check the caller's type names. - req.is.forEach(function (typ) { - if (VALID_TYPES.indexOf(typ) < 0) { - let msg = 'Internal error: invalid requirement type "' + typ + '".'; - throw new Error(msg); - } - }); - if (req.is.indexOf(getTypeOf(optsVal)) < 0) - throw requirementError(key, req); - } - if (req.ok && !req.ok(optsVal)) - throw requirementError(key, req); - - if (keyInOpts || (req.map && !mapThrew)) - validatedOptions[key] = optsVal; - } - - return validatedOptions; -}; - -exports.addIterator = function addIterator(obj, keysValsGenerator) { - obj.__iterator__ = function(keysOnly, keysVals) { - let keysValsIterator = keysValsGenerator.call(this); - - // "for (.. in ..)" gets only keys, "for each (.. in ..)" gets values, - // and "for (.. in Iterator(..))" gets [key, value] pairs. - let index = keysOnly ? 0 : 1; - while (true) - yield keysVals ? keysValsIterator.next() : keysValsIterator.next()[index]; - }; -}; - -// Similar to typeof, except arrays and null are identified by "array" and -// "null", not "object". -let getTypeOf = exports.getTypeOf = function getTypeOf(val) { - let typ = typeof(val); - if (typ === "object") { - if (!val) - return "null"; - if (Array.isArray(val)) - return "array"; - } - return typ; -} - -// Returns a new Error with a nice message. -function requirementError(key, requirement) { - let msg = requirement.msg; - if (!msg) { - msg = 'The option "' + key + '" '; - msg += requirement.is ? - "must be one of the following types: " + requirement.is.join(", ") : - "is invalid."; - } - return new Error(msg); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/app-strings.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/app-strings.js deleted file mode 100644 index e09f79e..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/app-strings.js +++ /dev/null @@ -1,67 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "deprecated" -}; - -const {Cc,Ci} = require("chrome"); -const apiUtils = require("./api-utils"); - -/** - * A bundle of strings. - * - * @param url {String} - * the URL of the string bundle - */ -exports.StringBundle = apiUtils.publicConstructor(function StringBundle(url) { - - let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(url); - - this.__defineGetter__("url", function () url); - - /** - * Get a string from the bundle. - * - * @param name {String} - * the name of the string to get - * @param args {array} [optional] - * an array of arguments that replace occurrences of %S in the string - * - * @returns {String} the value of the string - */ - this.get = function strings_get(name, args) { - try { - if (args) - return stringBundle.formatStringFromName(name, args, args.length); - else - return stringBundle.GetStringFromName(name); - } - catch(ex) { - // f.e. "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) - // [nsIStringBundle.GetStringFromName]" - throw new Error("String '" + name + "' could not be retrieved from the " + - "bundle due to an unknown error (it doesn't exist?)."); - } - }, - - /** - * Iterate the strings in the bundle. - * - */ - apiUtils.addIterator( - this, - function keysValsGen() { - let enumerator = stringBundle.getSimpleEnumeration(); - while (enumerator.hasMoreElements()) { - let elem = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement); - yield [elem.key, elem.value]; - } - } - ); -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/cortex.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/cortex.js deleted file mode 100644 index 8eef91c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/cortex.js +++ /dev/null @@ -1,113 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 - * 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"; - -module.metadata = { - "stability": "deprecated" -}; - -// `var` is being used in the module in order to make it reusable in -// environments in which `let` and `const` is not yet supported. - -// Returns `object`'s property value, where `name` is a name of the property. -function get(object, name) { - return object[name]; -} - -// Assigns `value` to the `object`'s property, where `name` is the name of the -// property. -function set(object, name, value) { - return object[name] = value; -} - -/** - * Given an `object` containing a property with the given `name`, create - * a property descriptor that can be used to define alias/proxy properties - * on other objects. A change in the value of an alias will propagate - * to the aliased property and vice versa. - */ -function createAliasProperty(object, name) { - // Getting own property descriptor of an `object` for the given `name` as - // we are going to create proxy analog. - var property = Object.getOwnPropertyDescriptor(object, name); - var descriptor = { - configurable: property.configurable, - enumerable: property.enumerable, - alias: true - }; - - // If the original property has a getter and/or setter, bind a - // corresponding getter/setter in the alias descriptor to the original - // object, so the `this` object in the getter/setter is the original object - // rather than the alias. - if ("get" in property && property.get) - descriptor.get = property.get.bind(object); - if ("set" in property && property.set) - descriptor.set = property.set.bind(object); - - // If original property was a value property. - if ("value" in property) { - // If original property is a method using it's `object` bounded copy. - if (typeof property.value === "function") { - descriptor.value = property.value.bind(object); - // Also preserving writability of the original property. - descriptor.writable = property.writable; - } - - // If the original property was just a data property, we create proxy - // accessors using our custom get/set functions to propagate changes to the - // original `object` and vice versa. - else { - descriptor.get = get.bind(null, object, name); - descriptor.set = set.bind(null, object, name); - } - } - return descriptor; -} - -// Defines property on `object` object with a name `alias` if given if not -// defaults to `name` that represents an alias of `source[name]`. If aliased -// property was an assessor or a method `this` pseudo-variable will be `source` -// when invoked. If aliased property was a data property changes on any of the -// aliases will propagate to the `source[name]` and also other way round. -function defineAlias(source, target, name, alias) { - return Object.defineProperty(target, alias || name, - createAliasProperty(source, name)); -} - -/** - * Function takes any `object` and returns a proxy for its own public - * properties. By default properties are considered to be public if they don't - * start with `"_"`, but default behavior can be overridden if needed, by - * passing array of public property `names` as a second argument. By default - * returned object will be direct decedent of the given `object`'s prototype, - * but this can be overridden by passing third optional argument, that will be - * used as `prototype` instead. - * @param {Object} object - * Object to create cortex for. - * @param {String[]} [names] - * Optional array of public property names. - * @param {Object} [prototype] - * Optional argument that will be used as `prototype` of the returned object, - * if not provided `Object.getPrototypeOf(object)` is used instead. - */ -exports.Cortex = function Cortex(object, names, prototype) { - // Creating a cortex object from the given `prototype`, if one was not - // provided then `prototype` of a given `object` is used. This allows - // consumer to define expected behavior `instanceof`. In common case - // `prototype` argument can be omitted to preserve same behavior of - // `instanceof` as on original `object`. - var cortex = Object.create(prototype || Object.getPrototypeOf(object)); - // Creating alias properties on the `cortex` object for all the own - // properties of the original `object` that are contained in `names` array. - // If `names` array is not provided then all the properties that don't - // start with `"_"` are aliased. - Object.getOwnPropertyNames(object).forEach(function (name) { - if ((!names && "_" !== name.charAt(0)) || (names && ~names.indexOf(name))) - defineAlias(object, cortex, name); - }); - return cortex; -} diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/errors.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/errors.js deleted file mode 100644 index b640d05..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/errors.js +++ /dev/null @@ -1,64 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "deprecated" -}; - -function logToConsole(e) { - console.exception(e); -} - -var catchAndLog = exports.catchAndLog = function(callback, - defaultResponse, - logException) { - if (!logException) - logException = logToConsole; - - return function() { - try { - return callback.apply(this, arguments); - } catch (e) { - logException(e); - return defaultResponse; - } - }; -}; - -exports.catchAndLogProps = function catchAndLogProps(object, - props, - defaultResponse, - logException) { - if (typeof(props) == "string") - props = [props]; - props.forEach( - function(property) { - object[property] = catchAndLog(object[property], - defaultResponse, - logException); - }); -}; - -/** - * Catch and return an exception while calling the callback. If the callback - * doesn't throw, return the return value of the callback in a way that makes it - * possible to distinguish between a return value and an exception. - * - * This function is useful when you need to pass the result of a call across - * a process boundary (across which exceptions don't propagate). It probably - * doesn't need to be factored out into this module, since it is only used by - * a single caller, but putting it here works around bug 625560. - */ -exports.catchAndReturn = function(callback) { - return function() { - try { - return { returnValue: callback.apply(this, arguments) }; - } - catch (exception) { - return { exception: exception }; - } - }; -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/events.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/events.js deleted file mode 100644 index 0c28a1a..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/events.js +++ /dev/null @@ -1,182 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "deprecated" -}; - -const ERROR_TYPE = 'error', - UNCAUGHT_ERROR = 'An error event was dispatched for which there was' - + ' no listener.', - BAD_LISTENER = 'The event listener must be a function.'; -/** - * This object is used to create an `EventEmitter` that, useful for composing - * objects that emit events. It implements an interface like `EventTarget` from - * DOM Level 2, which is implemented by Node objects in implementations that - * support the DOM Event Model. - * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget - * @see http://nodejs.org/api.html#EventEmitter - * @see http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/events/EventDispatcher.html - */ -const eventEmitter = { - /** - * Registers an event `listener` that is called every time events of - * specified `type` are emitted. - * @param {String} type - * The type of event. - * @param {Function} listener - * The listener function that processes the event. - * @example - * worker.on('message', function (data) { - * console.log('data received: ' + data) - * }) - */ - on: function on(type, listener) { - if ('function' !== typeof listener) - throw new Error(BAD_LISTENER); - let listeners = this._listeners(type); - if (0 > listeners.indexOf(listener)) - listeners.push(listener); - // Use of `_public` is required by the legacy traits code that will go away - // once bug-637633 is fixed. - return this._public || this; - }, - - /** - * Registers an event `listener` that is called once the next time an event - * of the specified `type` is emitted. - * @param {String} type - * The type of the event. - * @param {Function} listener - * The listener function that processes the event. - */ - once: function once(type, listener) { - this.on(type, function selfRemovableListener() { - this.removeListener(type, selfRemovableListener); - listener.apply(this, arguments); - }); - }, - - /** - * Unregister `listener` for the specified event type. - * @param {String} type - * The type of event. - * @param {Function} listener - * The listener function that processes the event. - */ - removeListener: function removeListener(type, listener) { - if ('function' !== typeof listener) - throw new Error(BAD_LISTENER); - let listeners = this._listeners(type), - index = listeners.indexOf(listener); - if (0 <= index) - listeners.splice(index, 1); - // Use of `_public` is required by the legacy traits code, that will go away - // once bug-637633 is fixed. - return this._public || this; - }, - - /** - * Hash of listeners on this EventEmitter. - */ - _events: null, - - /** - * Returns an array of listeners for the specified event `type`. This array - * can be manipulated, e.g. to remove listeners. - * @param {String} type - * The type of event. - */ - _listeners: function listeners(type) { - let events = this._events || (this._events = {}); - return events[type] || (events[type] = []); - }, - - /** - * Execute each of the listeners in order with the supplied arguments. - * Returns `true` if listener for this event was called, `false` if there are - * no listeners for this event `type`. - * - * All the exceptions that are thrown by listeners during the emit - * are caught and can be handled by listeners of 'error' event. Thrown - * exceptions are passed as an argument to an 'error' event listener. - * If no 'error' listener is registered exception will propagate to a - * caller of this method. - * - * **It's recommended to have a default 'error' listener in all the complete - * composition that in worst case may dump errors to the console.** - * - * @param {String} type - * The type of event. - * @params {Object|Number|String|Boolean} - * Arguments that will be passed to listeners. - * @returns {Boolean} - */ - _emit: function _emit(type, event) { - let args = Array.slice(arguments); - // Use of `_public` is required by the legacy traits code that will go away - // once bug-637633 is fixed. - args.unshift(this._public || this); - return this._emitOnObject.apply(this, args); - }, - - /** - * A version of _emit that lets you specify the object on which listeners are - * called. This is a hack that is sometimes necessary when such an object - * (exports, for example) cannot be an EventEmitter for some reason, but other - * object(s) managing events for the object are EventEmitters. Once bug - * 577782 is fixed, this method shouldn't be necessary. - * - * @param {object} targetObj - * The object on which listeners will be called. - * @param {string} type - * The event name. - * @param {value} event - * The first argument to pass to listeners. - * @param {value} ... - * More arguments to pass to listeners. - * @returns {boolean} - */ - _emitOnObject: function _emitOnObject(targetObj, type, event /* , ... */) { - let listeners = this._listeners(type).slice(0); - // If there is no 'error' event listener then throw. - if (type === ERROR_TYPE && !listeners.length) - console.exception(event); - if (!listeners.length) - return false; - let params = Array.slice(arguments, 2); - for each (let listener in listeners) { - try { - listener.apply(targetObj, params); - } catch(e) { - // Bug 726967: Ignore exceptions being throws while notifying the error - // in order to avoid infinite loops. - if (type !== ERROR_TYPE) - this._emit(ERROR_TYPE, e); - else - console.exception("Exception in error event listener " + e); - } - } - return true; - }, - - /** - * Removes all the event listeners for the specified event `type`. - * @param {String} type - * The type of event. - */ - _removeAllListeners: function _removeAllListeners(type) { - if (typeof type == "undefined") { - this._events = null; - return this; - } - - this._listeners(type).splice(0); - return this; - } -}; -exports.EventEmitter = require("./traits").Trait.compose(eventEmitter); -exports.EventEmitterTrait = require('./light-traits').Trait(eventEmitter); diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/events/assembler.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/events/assembler.js deleted file mode 100644 index fcde1e3..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/events/assembler.js +++ /dev/null @@ -1,53 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { Trait } = require("../light-traits"); -const { removeListener, on } = require("../../dom/events"); - -/** - * Trait may be used for building objects / composing traits that wish to handle - * multiple dom events from multiple event targets in one place. Event targets - * can be added / removed by calling `observe / ignore` methods. Composer should - * provide array of event types it wishes to handle as property - * `supportedEventsTypes` and function for handling all those events as - * `handleEvent` property. - */ -exports.DOMEventAssembler = Trait({ - /** - * Function that is supposed to handle all the supported events (that are - * present in the `supportedEventsTypes`) from all the observed - * `eventTargets`. - * @param {Event} event - * Event being dispatched. - */ - handleEvent: Trait.required, - /** - * Array of supported event names. - * @type {String[]} - */ - supportedEventsTypes: Trait.required, - /** - * Adds `eventTarget` to the list of observed `eventTarget`s. Listeners for - * supported events will be registered on the given `eventTarget`. - * @param {EventTarget} eventTarget - */ - observe: function observe(eventTarget) { - this.supportedEventsTypes.forEach(function(eventType) { - on(eventTarget, eventType, this); - }, this); - }, - /** - * Removes `eventTarget` from the list of observed `eventTarget`s. Listeners - * for all supported events will be unregistered from the given `eventTarget`. - * @param {EventTarget} eventTarget - */ - ignore: function ignore(eventTarget) { - this.supportedEventsTypes.forEach(function(eventType) { - removeListener(eventTarget, eventType, this); - }, this); - } -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/light-traits.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/light-traits.js deleted file mode 100644 index b18dccb..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/light-traits.js +++ /dev/null @@ -1,600 +0,0 @@ -/* vim:ts=2:sts=2:sw=2: - * 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"; - -module.metadata = { - "stability": "deprecated" -}; - -// `var` is being used in the module in order to make it reusable in -// environments in which `let` is not yet supported. - -// Shortcut to `Object.prototype.hasOwnProperty.call`. -// owns(object, name) would be the same as -// Object.prototype.hasOwnProperty.call(object, name); -var owns = Function.prototype.call.bind(Object.prototype.hasOwnProperty); - -/** - * Whether or not given property descriptors are equivalent. They are - * equivalent either if both are marked as 'conflict' or 'required' property - * or if all the properties of descriptors are equal. - * @param {Object} actual - * @param {Object} expected - */ -function equivalentDescriptors(actual, expected) { - return (actual.conflict && expected.conflict) || - (actual.required && expected.required) || - equalDescriptors(actual, expected); -} -/** - * Whether or not given property descriptors define equal properties. - */ -function equalDescriptors(actual, expected) { - return actual.get === expected.get && - actual.set === expected.set && - actual.value === expected.value && - !!actual.enumerable === !!expected.enumerable && - !!actual.configurable === !!expected.configurable && - !!actual.writable === !!expected.writable; -} - -// Utilities that throwing exceptions for a properties that are marked -// as "required" or "conflict" properties. -function throwConflictPropertyError(name) { - throw new Error("Remaining conflicting property: `" + name + "`"); -} -function throwRequiredPropertyError(name) { - throw new Error("Missing required property: `" + name + "`"); -} - -/** - * Generates custom **required** property descriptor. Descriptor contains - * non-standard property `required` that is equal to `true`. - * @param {String} name - * property name to generate descriptor for. - * @returns {Object} - * custom property descriptor - */ -function RequiredPropertyDescriptor(name) { - // Creating function by binding first argument to a property `name` on the - // `throwConflictPropertyError` function. Created function is used as a - // getter & setter of the created property descriptor. This way we ensure - // that we throw exception late (on property access) if object with - // `required` property was instantiated using built-in `Object.create`. - var accessor = throwRequiredPropertyError.bind(null, name); - return { get: accessor, set: accessor, required: true }; -} - -/** - * Generates custom **conflicting** property descriptor. Descriptor contains - * non-standard property `conflict` that is equal to `true`. - * @param {String} name - * property name to generate descriptor for. - * @returns {Object} - * custom property descriptor - */ -function ConflictPropertyDescriptor(name) { - // For details see `RequiredPropertyDescriptor` since idea is same. - var accessor = throwConflictPropertyError.bind(null, name); - return { get: accessor, set: accessor, conflict: true }; -} - -/** - * Tests if property is marked as `required` property. - */ -function isRequiredProperty(object, name) { - return !!object[name].required; -} - -/** - * Tests if property is marked as `conflict` property. - */ -function isConflictProperty(object, name) { - return !!object[name].conflict; -} - -/** - * Function tests whether or not method of the `source` object with a given - * `name` is inherited from `Object.prototype`. - */ -function isBuiltInMethod(name, source) { - var target = Object.prototype[name]; - - // If methods are equal then we know it's `true`. - return target == source || - // If `source` object comes form a different sandbox `==` will evaluate - // to `false`, in that case we check if functions names and sources match. - (String(target) === String(source) && target.name === source.name); -} - -/** - * Function overrides `toString` and `constructor` methods of a given `target` - * object with a same-named methods of a given `source` if methods of `target` - * object are inherited / copied from `Object.prototype`. - * @see create - */ -function overrideBuiltInMethods(target, source) { - if (isBuiltInMethod("toString", target.toString)) { - Object.defineProperty(target, "toString", { - value: source.toString, - configurable: true, - enumerable: false - }); - } - - if (isBuiltInMethod("constructor", target.constructor)) { - Object.defineProperty(target, "constructor", { - value: source.constructor, - configurable: true, - enumerable: false - }); - } -} - -/** - * Composes new trait with the same own properties as the original trait, - * except that all property names appearing in the first argument are replaced - * by "required" property descriptors. - * @param {String[]} keys - * Array of strings property names. - * @param {Object} trait - * A trait some properties of which should be excluded. - * @returns {Object} - * @example - * var newTrait = exclude(["name", ...], trait) - */ -function exclude(names, trait) { - var map = {}; - - Object.keys(trait).forEach(function(name) { - - // If property is not excluded (the array of names does not contain it), - // or it is a "required" property, copy it to the property descriptor `map` - // that will be used for creation of resulting trait. - if (!~names.indexOf(name) || isRequiredProperty(trait, name)) - map[name] = { value: trait[name], enumerable: true }; - - // For all the `names` in the exclude name array we create required - // property descriptors and copy them to the `map`. - else - map[name] = { value: RequiredPropertyDescriptor(name), enumerable: true }; - }); - - return Object.create(Trait.prototype, map); -} - -/** - * Composes new instance of `Trait` with a properties of a given `trait`, - * except that all properties whose name is an own property of `renames` will - * be renamed to `renames[name]` and a `"required"` property for name will be - * added instead. - * - * For each renamed property, a required property is generated. If - * the `renames` map two properties to the same name, a conflict is generated. - * If the `renames` map a property to an existing unrenamed property, a - * conflict is generated. - * - * @param {Object} renames - * An object whose own properties serve as a mapping from old names to new - * names. - * @param {Object} trait - * A new trait with renamed properties. - * @returns {Object} - * @example - * - * // Return trait with `bar` property equal to `trait.foo` and with - * // `foo` and `baz` "required" properties. - * var renamedTrait = rename({ foo: "bar", baz: null }), trait); - * - * // t1 and t2 are equivalent traits - * var t1 = rename({a: "b"}, t); - * var t2 = compose(exclude(["a"], t), { a: { required: true }, b: t[a] }); - */ -function rename(renames, trait) { - var map = {}; - - // Loop over all the properties of the given `trait` and copy them to a - // property descriptor `map` that will be used for the creation of the - // resulting trait. Also, rename properties in the `map` as specified by - // `renames`. - Object.keys(trait).forEach(function(name) { - var alias; - - // If the property is in the `renames` map, and it isn't a "required" - // property (which should never need to be aliased because "required" - // properties never conflict), then we must try to rename it. - if (owns(renames, name) && !isRequiredProperty(trait, name)) { - alias = renames[name]; - - // If the `map` already has the `alias`, and it isn't a "required" - // property, that means the `alias` conflicts with an existing name for a - // provided trait (that can happen if >=2 properties are aliased to the - // same name). In this case we mark it as a conflicting property. - // Otherwise, everything is fine, and we copy property with an `alias` - // name. - if (owns(map, alias) && !map[alias].value.required) { - map[alias] = { - value: ConflictPropertyDescriptor(alias), - enumerable: true - }; - } - else { - map[alias] = { - value: trait[name], - enumerable: true - }; - } - - // Regardless of whether or not the rename was successful, we check to - // see if the original `name` exists in the map (such a property - // could exist if previous another property was aliased to this `name`). - // If it isn't, we mark it as "required", to make sure the caller - // provides another value for the old name, which methods of the trait - // might continue to reference. - if (!owns(map, name)) { - map[name] = { - value: RequiredPropertyDescriptor(name), - enumerable: true - }; - } - } - - // Otherwise, either the property isn't in the `renames` map (thus the - // caller is not trying to rename it) or it is a "required" property. - // Either way, we don't have to alias the property, we just have to copy it - // to the map. - else { - // The property isn't in the map yet, so we copy it over. - if (!owns(map, name)) { - map[name] = { value: trait[name], enumerable: true }; - } - - // The property is already in the map (that means another property was - // aliased with this `name`, which creates a conflict if the property is - // not marked as "required"), so we have to mark it as a "conflict" - // property. - else if (!isRequiredProperty(trait, name)) { - map[name] = { - value: ConflictPropertyDescriptor(name), - enumerable: true - }; - } - } - }); - return Object.create(Trait.prototype, map); -} - -/** - * Composes new resolved trait, with all the same properties as the original - * `trait`, except that all properties whose name is an own property of - * `resolutions` will be renamed to `resolutions[name]`. - * - * If `resolutions[name]` is `null`, the value is mapped to a property - * descriptor that is marked as a "required" property. - */ -function resolve(resolutions, trait) { - var renames = {}; - var exclusions = []; - - // Go through each mapping in `resolutions` object and distribute it either - // to `renames` or `exclusions`. - Object.keys(resolutions).forEach(function(name) { - - // If `resolutions[name]` is a truthy value then it's a mapping old -> new - // so we copy it to `renames` map. - if (resolutions[name]) - renames[name] = resolutions[name]; - - // Otherwise it's not a mapping but an exclusion instead in which case we - // add it to the `exclusions` array. - else - exclusions.push(name); - }); - - // First `exclude` **then** `rename` and order is important since - // `exclude` and `rename` are not associative. - return rename(renames, exclude(exclusions, trait)); -} - -/** - * Create a Trait (a custom property descriptor map) that represents the given - * `object`'s own properties. Property descriptor map is a "custom", because it - * inherits from `Trait.prototype` and it's property descriptors may contain - * two attributes that is not part of the ES5 specification: - * - * - "required" (this property must be provided by another trait - * before an instance of this trait can be created) - * - "conflict" (when the trait is composed with another trait, - * a unique value for this property is provided by two or more traits) - * - * Data properties bound to the `Trait.required` singleton exported by - * this module will be marked as "required" properties. - * - * @param {Object} object - * Map of properties to compose trait from. - * @returns {Trait} - * Trait / Property descriptor map containing all the own properties of the - * given argument. - */ -function trait(object) { - var map; - var trait = object; - - if (!(object instanceof Trait)) { - // If the passed `object` is not already an instance of `Trait`, we create - // a property descriptor `map` containing descriptors for the own properties - // of the given `object`. `map` is then used to create a `Trait` instance - // after all properties are mapped. Note that we can't create a trait and - // then just copy properties into it since that will fail for inherited - // read-only properties. - map = {}; - - // Each own property of the given `object` is mapped to a data property - // whose value is a property descriptor. - Object.keys(object).forEach(function (name) { - - // If property of an `object` is equal to a `Trait.required`, it means - // that it was marked as "required" property, in which case we map it - // to "required" property. - if (Trait.required == - Object.getOwnPropertyDescriptor(object, name).value) { - map[name] = { - value: RequiredPropertyDescriptor(name), - enumerable: true - }; - } - // Otherwise property is mapped to it's property descriptor. - else { - map[name] = { - value: Object.getOwnPropertyDescriptor(object, name), - enumerable: true - }; - } - }); - - trait = Object.create(Trait.prototype, map); - } - return trait; -} - -/** - * Compose a property descriptor map that inherits from `Trait.prototype` and - * contains property descriptors for all the own properties of the passed - * traits. - * - * If two or more traits have own properties with the same name, the returned - * trait will contain a "conflict" property for that name. Composition is a - * commutative and associative operation, and the order of its arguments is - * irrelevant. - */ -function compose(trait1, trait2/*, ...*/) { - // Create a new property descriptor `map` to which all the own properties - // of the passed traits are copied. This map will be used to create a `Trait` - // instance that will be the result of this composition. - var map = {}; - - // Properties of each passed trait are copied to the composition. - Array.prototype.forEach.call(arguments, function(trait) { - // Copying each property of the given trait. - Object.keys(trait).forEach(function(name) { - - // If `map` already owns a property with the `name` and it is not - // marked "required". - if (owns(map, name) && !map[name].value.required) { - - // If the source trait's property with the `name` is marked as - // "required", we do nothing, as the requirement was already resolved - // by a property in the `map` (because it already contains a - // non-required property with that `name`). But if properties are just - // different, we have a name clash and we substitute it with a property - // that is marked "conflict". - if (!isRequiredProperty(trait, name) && - !equivalentDescriptors(map[name].value, trait[name]) - ) { - map[name] = { - value: ConflictPropertyDescriptor(name), - enumerable: true - }; - } - } - - // Otherwise, the `map` does not have an own property with the `name`, or - // it is marked "required". Either way, the trait's property is copied to - // the map (if the property of the `map` is marked "required", it is going - // to be resolved by the property that is being copied). - else { - map[name] = { value: trait[name], enumerable: true }; - } - }); - }); - - return Object.create(Trait.prototype, map); -} - -/** - * `defineProperties` is like `Object.defineProperties`, except that it - * ensures that: - * - An exception is thrown if any property in a given `properties` map - * is marked as "required" property and same named property is not - * found in a given `prototype`. - * - An exception is thrown if any property in a given `properties` map - * is marked as "conflict" property. - * @param {Object} object - * Object to define properties on. - * @param {Object} properties - * Properties descriptor map. - * @returns {Object} - * `object` that was passed as a first argument. - */ -function defineProperties(object, properties) { - - // Create a map into which we will copy each verified property from the given - // `properties` description map. We use it to verify that none of the - // provided properties is marked as a "conflict" property and that all - // "required" properties are resolved by a property of an `object`, so we - // can throw an exception before mutating object if that isn't the case. - var verifiedProperties = {}; - - // Coping each property from a given `properties` descriptor map to a - // verified map of property descriptors. - Object.keys(properties).forEach(function(name) { - - // If property is marked as "required" property and we don't have a same - // named property in a given `object` we throw an exception. If `object` - // has same named property just skip this property since required property - // is was inherited and there for requirement was satisfied. - if (isRequiredProperty(properties, name)) { - if (!(name in object)) - throwRequiredPropertyError(name); - } - - // If property is marked as "conflict" property we throw an exception. - else if (isConflictProperty(properties, name)) { - throwConflictPropertyError(name); - } - - // If property is not marked neither as "required" nor "conflict" property - // we copy it to verified properties map. - else { - verifiedProperties[name] = properties[name]; - } - }); - - // If no exceptions were thrown yet, we know that our verified property - // descriptor map has no properties marked as "conflict" or "required", - // so we just delegate to the built-in `Object.defineProperties`. - return Object.defineProperties(object, verifiedProperties); -} - -/** - * `create` is like `Object.create`, except that it ensures that: - * - An exception is thrown if any property in a given `properties` map - * is marked as "required" property and same named property is not - * found in a given `prototype`. - * - An exception is thrown if any property in a given `properties` map - * is marked as "conflict" property. - * @param {Object} prototype - * prototype of the composed object - * @param {Object} properties - * Properties descriptor map. - * @returns {Object} - * An object that inherits form a given `prototype` and implements all the - * properties defined by a given `properties` descriptor map. - */ -function create(prototype, properties) { - - // Creating an instance of the given `prototype`. - var object = Object.create(prototype); - - // Overriding `toString`, `constructor` methods if they are just inherited - // from `Object.prototype` with a same named methods of the `Trait.prototype` - // that will have more relevant behavior. - overrideBuiltInMethods(object, Trait.prototype); - - // Trying to define given `properties` on the `object`. We use our custom - // `defineProperties` function instead of build-in `Object.defineProperties` - // that behaves exactly the same, except that it will throw if any - // property in the given `properties` descriptor is marked as "required" or - // "conflict" property. - return defineProperties(object, properties); -} - -/** - * Composes new trait. If two or more traits have own properties with the - * same name, the new trait will contain a "conflict" property for that name. - * "compose" is a commutative and associative operation, and the order of its - * arguments is not significant. - * - * **Note:** Use `Trait.compose` instead of calling this function with more - * than one argument. The multiple-argument functionality is strictly for - * backward compatibility. - * - * @params {Object} trait - * Takes traits as an arguments - * @returns {Object} - * New trait containing the combined own properties of all the traits. - * @example - * var newTrait = compose(trait_1, trait_2, ..., trait_N) - */ -function Trait(trait1, trait2) { - - // If the function was called with one argument, the argument should be - // an object whose properties are mapped to property descriptors on a new - // instance of Trait, so we delegate to the trait function. - // If the function was called with more than one argument, those arguments - // should be instances of Trait or plain property descriptor maps - // whose properties should be mixed into a new instance of Trait, - // so we delegate to the compose function. - - return trait2 === undefined ? trait(trait1) : compose.apply(null, arguments); -} - -Object.freeze(Object.defineProperties(Trait.prototype, { - toString: { - value: function toString() { - return "[object " + this.constructor.name + "]"; - } - }, - - /** - * `create` is like `Object.create`, except that it ensures that: - * - An exception is thrown if this trait defines a property that is - * marked as required property and same named property is not - * found in a given `prototype`. - * - An exception is thrown if this trait contains property that is - * marked as "conflict" property. - * @param {Object} - * prototype of the compared object - * @returns {Object} - * An object with all of the properties described by the trait. - */ - create: { - value: function createTrait(prototype) { - return create(undefined === prototype ? Object.prototype : prototype, - this); - }, - enumerable: true - }, - - /** - * Composes a new resolved trait, with all the same properties as the original - * trait, except that all properties whose name is an own property of - * `resolutions` will be renamed to the value of `resolutions[name]`. If - * `resolutions[name]` is `null`, the property is marked as "required". - * @param {Object} resolutions - * An object whose own properties serve as a mapping from old names to new - * names, or to `null` if the property should be excluded. - * @returns {Object} - * New trait with the same own properties as the original trait but renamed. - */ - resolve: { - value: function resolveTrait(resolutions) { - return resolve(resolutions, this); - }, - enumerable: true - } -})); - -/** - * @see compose - */ -Trait.compose = Object.freeze(compose); -Object.freeze(compose.prototype); - -/** - * Constant singleton, representing placeholder for required properties. - * @type {Object} - */ -Trait.required = Object.freeze(Object.create(Object.prototype, { - toString: { - value: Object.freeze(function toString() { - return "<Trait.required>"; - }) - } -})); -Object.freeze(Trait.required.toString.prototype); - -exports.Trait = Object.freeze(Trait); diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/list.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/list.js deleted file mode 100644 index 5de69e7..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/list.js +++ /dev/null @@ -1,118 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "experimental" -}; - -const { Trait } = require('../deprecated/traits'); - -/** - * @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/list - */ -const Iterable = Trait.compose({ - /** - * Hash map of key-values to iterate over. - * Note: That this property can be a getter if you need dynamic behavior. - * @type {Object} - */ - _keyValueMap: Trait.required, - /** - * Custom iterator providing `Iterable`s enumeration behavior. - * @param {Boolean} onKeys - */ - __iterator__: function __iterator__(onKeys, onKeyValue) { - let map = this._keyValueMap; - for (let key in map) - yield onKeyValue ? [key, map[key]] : onKeys ? key : map[key]; - } -}); -exports.Iterable = Iterable; - -/** - * An ordered collection (also known as a sequence) disallowing duplicate - * elements. List is composed out of `Iterable` there for it provides custom - * enumeration behavior that is similar to array (enumerates only on the - * elements of the list). List is a base trait and is meant to be a part of - * composition, since all of it's API is private except length property. - */ -const List = Trait.resolve({ toString: null }).compose({ - _keyValueMap: null, - /** - * List constructor can take any number of element to populate itself. - * @params {Object|String|Number} element - * @example - * List(1,2,3).length == 3 // true - */ - constructor: function List() { - this._keyValueMap = []; - for (let i = 0, ii = arguments.length; i < ii; i++) - this._add(arguments[i]); - }, - /** - * Number of elements in this list. - * @type {Number} - */ - get length() this._keyValueMap.length, - /** - * Returns a string representing this list. - * @returns {String} - */ - toString: function toString() 'List(' + this._keyValueMap + ')', - /** - * Returns `true` if this list contains the specified `element`. - * @param {Object|Number|String} element - * @returns {Boolean} - */ - _has: function _has(element) 0 <= this._keyValueMap.indexOf(element), - /** - * Appends the specified `element` to the end of this list, if it doesn't - * contains it. Ignores the call if `element` is already contained. - * @param {Object|Number|String} element - */ - _add: function _add(element) { - let list = this._keyValueMap, - index = list.indexOf(element); - if (0 > index) - list.push(this._public[list.length] = element); - }, - /** - * Removes specified `element` from this list, if it contains it. - * Ignores the call if `element` is not contained. - * @param {Object|Number|String} element - */ - _remove: function _remove(element) { - let list = this._keyValueMap, - index = list.indexOf(element); - if (0 <= index) { - delete this._public[list.length - 1]; - list.splice(index, 1); - for (let length = list.length; index < length; index++) - this._public[index] = list[index]; - } - }, - /** - * Removes all of the elements from this list. - */ - _clear: function _clear() { - for (let i = 0, ii = this._keyValueMap.length; i < ii; i ++) - delete this._public[i]; - this._keyValueMap.splice(0); - }, - /** - * Custom iterator providing `List`s enumeration behavior. - * We cant reuse `_iterator` that is defined by `Iterable` since it provides - * iteration in an arbitrary order. - * @see https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in - * @param {Boolean} onKeys - */ - __iterator__: function __iterator__(onKeys, onKeyValue) { - let array = this._keyValueMap.slice(0), - i = -1; - for each(let element in array) - yield onKeyValue ? [++i, element] : onKeys ? ++i : element; - } -}); -exports.List = List; diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/memory.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/memory.js deleted file mode 100644 index 70bcffc..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/memory.js +++ /dev/null @@ -1,118 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "deprecated" -}; - -const {Cc,Ci,Cu,components} = require("chrome"); -var trackedObjects = {}; - -var Compacter = { - INTERVAL: 5000, - notify: function(timer) { - var newTrackedObjects = {}; - for (let name in trackedObjects) { - var oldBin = trackedObjects[name]; - var newBin = []; - var strongRefs = []; - for (var i = 0; i < oldBin.length; i++) { - var strongRef = oldBin[i].weakref.get(); - if (strongRef && strongRefs.indexOf(strongRef) == -1) { - strongRefs.push(strongRef); - newBin.push(oldBin[i]); - } - } - if (newBin.length) - newTrackedObjects[name] = newBin; - } - trackedObjects = newTrackedObjects; - } -}; - -var timer = Cc["@mozilla.org/timer;1"] - .createInstance(Ci.nsITimer); - -timer.initWithCallback(Compacter, - Compacter.INTERVAL, - Ci.nsITimer.TYPE_REPEATING_SLACK); - -var track = exports.track = function track(object, bin, stackFrameNumber) { - var frame = components.stack.caller; - var weakref = Cu.getWeakReference(object); - if (!bin && 'constructor' in object) - bin = object.constructor.name; - if (bin == "Object") - bin = frame.name; - if (!bin) - bin = "generic"; - if (!(bin in trackedObjects)) - trackedObjects[bin] = []; - - if (stackFrameNumber > 0) - for (var i = 0; i < stackFrameNumber; i++) - frame = frame.caller; - - trackedObjects[bin].push({weakref: weakref, - created: new Date(), - filename: frame.filename, - lineNo: frame.lineNumber, - bin: bin}); -}; - -var getBins = exports.getBins = function getBins() { - var names = []; - for (let name in trackedObjects) - names.push(name); - return names; -}; - -var getObjects = exports.getObjects = function getObjects(bin) { - function getLiveObjectsInBin(bin, array) { - for (var i = 0; i < bin.length; i++) { - var object = bin[i].weakref.get(); - if (object) - array.push(bin[i]); - } - } - - var results = []; - if (bin) { - if (bin in trackedObjects) - getLiveObjectsInBin(trackedObjects[bin], results); - } else - for (let name in trackedObjects) - getLiveObjectsInBin(trackedObjects[name], results); - return results; -}; - -var gc = exports.gc = function gc() { - // Components.utils.forceGC() doesn't currently perform - // cycle collection, which means that e.g. DOM elements - // won't be collected by it. Fortunately, there are - // other ways... - - var window = Cc["@mozilla.org/appshell/appShellService;1"] - .getService(Ci.nsIAppShellService) - .hiddenDOMWindow; - var test_utils = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - test_utils.garbageCollect(); - Compacter.notify(); - - // Not sure why, but sometimes it appears that we don't get - // them all with just one CC, so let's do it again. - test_utils.garbageCollect(); -}; - -require("../system/unload").when( - function() { - trackedObjects = {}; - if (timer) { - timer.cancel(); - timer = null; - } - }); diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/observer-service.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/observer-service.js deleted file mode 100644 index 6a25109..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/observer-service.js +++ /dev/null @@ -1,134 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "deprecated" -}; - -const { Cc, Ci } = require("chrome"); -const { when: unload } = require("../system/unload"); -const { ns } = require("../core/namespace"); -const { on, off, emit, once } = require("../system/events"); -const { id } = require("../self"); - -const subscribers = ns(); -const cache = []; - -/** - * Topics specifically available to Jetpack-generated extensions. - * - * Using these predefined consts instead of the platform strings is good: - * - allows us to scope topics specifically for Jetpacks - * - addons aren't dependent on strings nor behavior of core platform topics - * - the core platform topics are not clearly named - * - */ -exports.topics = { - /** - * A topic indicating that the application is in a state usable - * by add-ons. - */ - APPLICATION_READY: id + "_APPLICATION_READY" -}; - -function Listener(callback, target) { - return function listener({ subject, data }) { - callback.call(target || callback, subject, data); - } -} - -/** - * Register the given callback as an observer of the given topic. - * - * @param topic {String} - * the topic to observe - * - * @param callback {Object} - * the callback; an Object that implements nsIObserver or a Function - * that gets called when the notification occurs - * - * @param target {Object} [optional] - * the object to use as |this| when calling a Function callback - * - * @returns the observer - */ -function add(topic, callback, target) { - let listeners = subscribers(callback); - if (!(topic in listeners)) { - let listener = Listener(callback, target); - listeners[topic] = listener; - - // Cache callback unless it's already cached. - if (!~cache.indexOf(callback)) - cache.push(callback); - - on(topic, listener); - } -}; -exports.add = add; - -/** - * Unregister the given callback as an observer of the given topic. - * - * @param topic {String} - * the topic being observed - * - * @param callback {Object} - * the callback doing the observing - * - * @param target {Object} [optional] - * the object being used as |this| when calling a Function callback - */ -function remove(topic, callback, target) { - let listeners = subscribers(callback); - if (topic in listeners) { - let listener = listeners[topic]; - delete listeners[topic]; - - // If no more observers are registered and callback is still in cache - // then remove it. - let index = cache.indexOf(callback); - if (~index && !Object.keys(listeners).length) - cache.splice(index, 1) - - off(topic, listener); - } -}; -exports.remove = remove; - -/** - * Notify observers about something. - * - * @param topic {String} - * the topic to notify observers about - * - * @param subject {Object} [optional] - * some information about the topic; can be any JS object or primitive - * - * @param data {String} [optional] [deprecated] - * some more information about the topic; deprecated as the subject - * is sufficient to pass all needed information to the JS observers - * that this module targets; if you have multiple values to pass to - * the observer, wrap them in an object and pass them via the subject - * parameter (i.e.: { foo: 1, bar: "some string", baz: myObject }) - */ -function notify(topic, subject, data) { - emit(topic, { - subject: subject === undefined ? null : subject, - data: data === undefined ? null : data - }); -} -exports.notify = notify; - -unload(function() { - // Make a copy of cache first, since cache will be changing as we - // iterate through it. - cache.slice().forEach(function(callback) { - Object.keys(subscribers(callback)).forEach(function(topic) { - remove(topic, callback); - }); - }); -}) diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/tab-browser.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/tab-browser.js deleted file mode 100644 index 068471c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/tab-browser.js +++ /dev/null @@ -1,731 +0,0 @@ -/* 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'; - -module.metadata = { - 'stability': 'deprecated' -}; - -const {Cc,Ci,Cu} = require('chrome'); -var NetUtil = {}; -Cu.import('resource://gre/modules/NetUtil.jsm', NetUtil); -NetUtil = NetUtil.NetUtil; -const errors = require('./errors'); -const windowUtils = require('./window-utils'); -const apiUtils = require('./api-utils'); -const collection = require('../util/collection'); -const { getMostRecentBrowserWindow } = require('../window/utils'); -const { getSelectedTab } = require('../tabs/utils'); - -// TODO: The hard-coding of app-specific info here isn't very nice; -// ideally such app-specific info should be more decoupled, and the -// module should be extensible, allowing for support of new apps at -// runtime, perhaps by inspecting supported packages (e.g. via -// dynamically-named modules or package-defined extension points). - -if (!require("../system/xul-app").is("Firefox")) { - throw new Error([ - "The tab-browser module currently supports only Firefox. In the future ", - "it will support other applications. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." - ].join("")); -} - -function onBrowserLoad(callback, event) { - if (event.target && event.target.defaultView == this) { - this.removeEventListener("load", onBrowserLoad, true); - try { - require("../timers").setTimeout(function () { - callback(event); - }, 10); - } catch (e) { console.exception(e); } - } -} - -// Utility function to open a new browser window. -function openBrowserWindow(callback, url) { - let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]. - getService(Ci.nsIWindowWatcher); - let urlString = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - urlString.data = url; - let window = ww.openWindow(null, "chrome://browser/content/browser.xul", - "_blank", "chrome,all,dialog=no", urlString); - if (callback) - window.addEventListener("load", onBrowserLoad.bind(window, callback), true); - - return window; -} - -// Open a URL in a new tab -exports.addTab = function addTab(url, options) { - if (!options) - options = {}; - options.url = url; - - options = apiUtils.validateOptions(options, { - // TODO: take URL object instead of string (bug 564524) - url: { - is: ["string"], - ok: function (v) !!v, - msg: "The url parameter must have be a non-empty string." - }, - inNewWindow: { - is: ["undefined", "null", "boolean"] - }, - inBackground: { - is: ["undefined", "null", "boolean"] - }, - onLoad: { - is: ["undefined", "null", "function"] - }, - isPinned: { - is: ["undefined", "boolean"] - } - }); - - var win = getMostRecentBrowserWindow(); - if (!win || options.inNewWindow) { - openBrowserWindow(function(e) { - if(options.isPinned) { - //get the active tab in the recently created window - let mainWindow = e.target.defaultView; - mainWindow.gBrowser.pinTab(getSelectedTab(mainWindow)); - } - require("./errors").catchAndLog(function(e) options.onLoad(e))(e); - }, options.url); - } - else { - let tab = win.gBrowser.addTab(options.url); - if (!options.inBackground) - win.gBrowser.selectedTab = tab; - if (options.onLoad) { - let tabBrowser = win.gBrowser.getBrowserForTab(tab); - tabBrowser.addEventListener("load", function onLoad(e) { - if (e.target.defaultView.content.location == "about:blank") - return; - // remove event handler from addTab - don't want notified - // for subsequent loads in same tab. - tabBrowser.removeEventListener("load", onLoad, true); - require("./errors").catchAndLog(function(e) options.onLoad(e))(e); - }, true); - } - } -} - -// Iterate over a window's tabbrowsers -function tabBrowserIterator(window) { - var browsers = window.document.querySelectorAll("tabbrowser"); - for (var i = 0; i < browsers.length; i++) - yield browsers[i]; -} - -// Iterate over a tabbrowser's tabs -function tabIterator(tabbrowser) { - var tabs = tabbrowser.tabContainer; - for (var i = 0; i < tabs.children.length; i++) { - yield tabs.children[i]; - } -} - -// Tracker for all tabbrowsers across all windows, -// or a single tabbrowser if the window is given. -function Tracker(delegate, window) { - this._delegate = delegate; - this._browsers = []; - this._window = window; - this._windowTracker = windowUtils.WindowTracker(this); - - require("../system/unload").ensure(this); -} -Tracker.prototype = { - __iterator__: function __iterator__() { - for (var i = 0; i < this._browsers.length; i++) - yield this._browsers[i]; - }, - get: function get(index) { - return this._browsers[index]; - }, - onTrack: function onTrack(window) { - if (this._window && window != this._window) - return; - - for (let browser in tabBrowserIterator(window)) - this._browsers.push(browser); - if (this._delegate) - for (let browser in tabBrowserIterator(window)) - this._delegate.onTrack(browser); - }, - onUntrack: function onUntrack(window) { - if (this._window && window != this._window) - return; - - for (let browser in tabBrowserIterator(window)) { - let index = this._browsers.indexOf(browser); - if (index != -1) - this._browsers.splice(index, 1); - else - console.error("internal error: browser tab not found"); - } - if (this._delegate) - for (let browser in tabBrowserIterator(window)) - this._delegate.onUntrack(browser); - }, - get length() { - return this._browsers.length; - }, - unload: function unload() { - this._windowTracker.unload(); - } -}; -exports.Tracker = apiUtils.publicConstructor(Tracker); - -// Tracker for all tabs across all windows, -// or a single window if it's given. -function TabTracker(delegate, window) { - this._delegate = delegate; - this._tabs = []; - this._tracker = new Tracker(this, window); - require("../system/unload").ensure(this); -} -TabTracker.prototype = { - _TAB_EVENTS: ["TabOpen", "TabClose"], - _safeTrackTab: function safeTrackTab(tab) { - this._tabs.push(tab); - try { - this._delegate.onTrack(tab); - } catch (e) { - console.exception(e); - } - }, - _safeUntrackTab: function safeUntrackTab(tab) { - var index = this._tabs.indexOf(tab); - if (index == -1) - console.error("internal error: tab not found"); - this._tabs.splice(index, 1); - try { - this._delegate.onUntrack(tab); - } catch (e) { - console.exception(e); - } - }, - handleEvent: function handleEvent(event) { - switch (event.type) { - case "TabOpen": - this._safeTrackTab(event.target); - break; - case "TabClose": - this._safeUntrackTab(event.target); - break; - default: - throw new Error("internal error: unknown event type: " + - event.type); - } - }, - onTrack: function onTrack(tabbrowser) { - for (let tab in tabIterator(tabbrowser)) - this._safeTrackTab(tab); - var self = this; - this._TAB_EVENTS.forEach( - function(eventName) { - tabbrowser.tabContainer.addEventListener(eventName, self, true); - }); - }, - onUntrack: function onUntrack(tabbrowser) { - for (let tab in tabIterator(tabbrowser)) - this._safeUntrackTab(tab); - var self = this; - this._TAB_EVENTS.forEach( - function(eventName) { - tabbrowser.tabContainer.removeEventListener(eventName, self, true); - }); - }, - unload: function unload() { - this._tracker.unload(); - } -}; -exports.TabTracker = apiUtils.publicConstructor(TabTracker); - -exports.whenContentLoaded = function whenContentLoaded(callback) { - var cb = require("./errors").catchAndLog(function eventHandler(event) { - if (event.target && event.target.defaultView) - callback(event.target.defaultView); - }); - - var tracker = new Tracker({ - onTrack: function(tabBrowser) { - tabBrowser.addEventListener("DOMContentLoaded", cb, false); - }, - onUntrack: function(tabBrowser) { - tabBrowser.removeEventListener("DOMContentLoaded", cb, false); - } - }); - - return tracker; -}; - -Object.defineProperty(exports, 'activeTab', { - get: function() { - return getSelectedTab(getMostRecentBrowserWindow()); - } -}); - -/******************* TabModule *********************/ - -// Supported tab events -const events = [ - "onActivate", - "onDeactivate", - "onOpen", - "onClose", - "onReady", - "onLoad", - "onPaint" -]; -exports.tabEvents = events; - -/** - * TabModule - * - * Constructor for a module that implements the tabs API - */ -let TabModule = exports.TabModule = function TabModule(window) { - let self = this; - /** - * Tab - * - * Safe object representing a tab. - */ - let tabConstructor = apiUtils.publicConstructor(function(element) { - if (!element) - throw new Error("no tab element."); - let win = element.ownerDocument.defaultView; - if (!win) - throw new Error("element has no window."); - if (window && win != window) - throw new Error("module's window and element's window don't match."); - let browser = win.gBrowser.getBrowserForTab(element); - - this.__defineGetter__("title", function() browser.contentDocument.title); - this.__defineGetter__("location", function() browser.contentDocument.location); - this.__defineSetter__("location", function(val) browser.contentDocument.location = val); - this.__defineGetter__("contentWindow", function() browser.contentWindow); - this.__defineGetter__("contentDocument", function() browser.contentDocument); - this.__defineGetter__("favicon", function() { - let pageURI = NetUtil.newURI(browser.contentDocument.location); - let fs = Cc["@mozilla.org/browser/favicon-service;1"]. - getService(Ci.nsIFaviconService); - let faviconURL; - try { - let faviconURI = fs.getFaviconForPage(pageURI); - faviconURL = fs.getFaviconDataAsDataURL(faviconURI); - } catch(ex) { - let data = getChromeURLContents("chrome://mozapps/skin/places/defaultFavicon.png"); - let encoded = browser.contentWindow.btoa(data); - faviconURL = "data:image/png;base64," + encoded; - } - return faviconURL; - }); - this.__defineGetter__("style", function() null); // TODO - this.__defineGetter__("index", function() win.gBrowser.getBrowserIndexForDocument(browser.contentDocument)); - this.__defineGetter__("thumbnail", function() getThumbnailCanvasForTab(element, browser.contentWindow)); - - this.close = function() win.gBrowser.removeTab(element); - this.move = function(index) { - win.gBrowser.moveTabTo(element, index); - }; - - this.__defineGetter__("isPinned", function() element.pinned); - this.pin = function() win.gBrowser.pinTab(element); - this.unpin = function() win.gBrowser.unpinTab(element); - - // Set up the event handlers - let tab = this; - events.filter(function(e) e != "onOpen").forEach(function(e) { - // create a collection for each event - collection.addCollectionProperty(tab, e); - // make tabs setter for each event, for adding via property assignment - tab.__defineSetter__(e, function(val) tab[e].add(val)); - }); - - // listen for events, filtered on this tab - eventsTabDelegate.addTabDelegate(this); - }); - - /** - * tabs.activeTab - */ - this.__defineGetter__("activeTab", function() { - try { - return window ? tabConstructor(getSelectedTab(window)) - : tabConstructor(exports.activeTab); - } - catch (e) { } - return null; - }); - this.__defineSetter__("activeTab", function(tab) { - let [tabElement, win] = getElementAndWindowForTab(tab, window); - if (tabElement) { - // set as active tab - win.gBrowser.selectedTab = tabElement; - // focus the window - win.focus(); - } - }); - - this.open = function TM_open(options) { - open(options, tabConstructor, window); - } - - // Set up the event handlers - events.forEach(function(eventHandler) { - // create a collection for each event - collection.addCollectionProperty(self, eventHandler); - // make tabs setter for each event, for adding via property assignment - self.__defineSetter__(eventHandler, function(val) self[eventHandler].add(val)); - }); - - // Tracker that listens for tab events, and proxies - // them to registered event listeners. - let eventsTabDelegate = { - selectedTab: null, - tabs: [], - addTabDelegate: function TETT_addTabDelegate(tabObj) { - this.tabs.push(tabObj); - }, - pushTabEvent: function TETT_pushTabEvent(event, tab) { - for (let callback in self[event]) { - require("./errors").catchAndLog(function(tab) { - callback(new tabConstructor(tab)); - })(tab); - } - - if (event != "onOpen") { - this.tabs.forEach(function(tabObj) { - if (tabObj[event].length) { - let [tabEl,] = getElementAndWindowForTab(tabObj, window); - if (tabEl == tab) { - for (let callback in tabObj[event]) - require("./errors").catchAndLog(function() callback())(); - } - } - // if being closed, remove the tab object from the cache - // of tabs to notify about events. - if (event == "onClose") - this.tabs.splice(this.tabs.indexOf(tabObj), 1); - }, this); - } - }, - unload: function() { - this.selectedTab = null; - this.tabs.splice(0); - } - }; - require("../system/unload").ensure(eventsTabDelegate); - - let eventsTabTracker = new ModuleTabTracker({ - onTrack: function TETT_onTrack(tab) { - eventsTabDelegate.pushTabEvent("onOpen", tab); - }, - onUntrack: function TETT_onUntrack(tab) { - eventsTabDelegate.pushTabEvent("onClose", tab); - }, - onSelect: function TETT_onSelect(tab) { - if (eventsTabDelegate.selectedTab) - eventsTabDelegate.pushTabEvent("onDeactivate", tab); - - eventsTabDelegate.selectedTab = new tabConstructor(tab); - - eventsTabDelegate.pushTabEvent("onActivate", tab); - }, - onReady: function TETT_onReady(tab) { - eventsTabDelegate.pushTabEvent("onReady", tab); - }, - onLoad: function TETT_onLoad(tab) { - eventsTabDelegate.pushTabEvent("onLoad", tab); - }, - onPaint: function TETT_onPaint(tab) { - eventsTabDelegate.pushTabEvent("onPaint", tab); - } - }, window); - require("../system/unload").ensure(eventsTabTracker); - - // Iterator for all tabs - this.__iterator__ = function tabsIterator() { - for (let i = 0; i < eventsTabTracker._tabs.length; i++) - yield tabConstructor(eventsTabTracker._tabs[i]); - } - - this.__defineGetter__("length", function() eventsTabTracker._tabs.length); - - // Cleanup when unloaded - this.unload = function TM_unload() { - // Unregister tabs event listeners - events.forEach(function(e) self[e] = []); - } - require("../system/unload").ensure(this); - -} // End of TabModule constructor - -/** - * tabs.open - open a URL in a new tab - */ -function open(options, tabConstructor, window) { - if (typeof options === "string") - options = { url: options }; - - options = apiUtils.validateOptions(options, { - url: { - is: ["string"] - }, - inNewWindow: { - is: ["undefined", "boolean"] - }, - inBackground: { - is: ["undefined", "boolean"] - }, - isPinned: { - is: ["undefined", "boolean"] - }, - onOpen: { - is: ["undefined", "function"] - } - }); - - if (window) - options.inNewWindow = false; - - let win = window || require("./window-utils").activeBrowserWindow; - - if (!win || options.inNewWindow) - openURLInNewWindow(options, tabConstructor); - else - openURLInNewTab(options, win, tabConstructor); -} - -function openURLInNewWindow(options, tabConstructor) { - let addTabOptions = { - inNewWindow: true - }; - if (options.onOpen) { - addTabOptions.onLoad = function(e) { - let win = e.target.defaultView; - let tabEl = win.gBrowser.tabContainer.childNodes[0]; - let tabBrowser = win.gBrowser.getBrowserForTab(tabEl); - tabBrowser.addEventListener("load", function onLoad(e) { - tabBrowser.removeEventListener("load", onLoad, true); - let tab = tabConstructor(tabEl); - require("./errors").catchAndLog(function(e) options.onOpen(e))(tab); - }, true); - }; - } - if (options.isPinned) { - addTabOptions.isPinned = true; - } - exports.addTab(options.url.toString(), addTabOptions); -} - -function openURLInNewTab(options, window, tabConstructor) { - window.focus(); - let tabEl = window.gBrowser.addTab(options.url.toString()); - if (!options.inBackground) - window.gBrowser.selectedTab = tabEl; - if (options.isPinned) - window.gBrowser.pinTab(tabEl); - if (options.onOpen) { - let tabBrowser = window.gBrowser.getBrowserForTab(tabEl); - tabBrowser.addEventListener("load", function onLoad(e) { - // remove event handler from addTab - don't want to be notified - // for subsequent loads in same tab. - tabBrowser.removeEventListener("load", onLoad, true); - let tab = tabConstructor(tabEl); - require("../timers").setTimeout(function() { - require("./errors").catchAndLog(function(tab) options.onOpen(tab))(tab); - }, 10); - }, true); - } -} - -function getElementAndWindowForTab(tabObj, window) { - // iterate over open windows, or use single window if provided - let windowIterator = window ? function() { yield window; } - : require("./window-utils").windowIterator; - for (let win in windowIterator()) { - if (win.gBrowser) { - // find the tab element at tab.index - let index = win.gBrowser.getBrowserIndexForDocument(tabObj.contentDocument); - if (index > -1) - return [win.gBrowser.tabContainer.getItemAtIndex(index), win]; - } - } - return [null, null]; -} - -// Tracker for all tabs across all windows -// This is tab-browser.TabTracker, but with -// support for additional events added. -function ModuleTabTracker(delegate, window) { - this._delegate = delegate; - this._tabs = []; - this._tracker = new Tracker(this, window); - require("../system/unload").ensure(this); -} -ModuleTabTracker.prototype = { - _TAB_EVENTS: ["TabOpen", "TabClose", "TabSelect", "DOMContentLoaded", - "load", "MozAfterPaint"], - _safeTrackTab: function safeTrackTab(tab) { - tab.addEventListener("load", this, false); - tab.linkedBrowser.addEventListener("MozAfterPaint", this, false); - this._tabs.push(tab); - try { - this._delegate.onTrack(tab); - } catch (e) { - console.exception(e); - } - }, - _safeUntrackTab: function safeUntrackTab(tab) { - tab.removeEventListener("load", this, false); - tab.linkedBrowser.removeEventListener("MozAfterPaint", this, false); - var index = this._tabs.indexOf(tab); - if (index == -1) - throw new Error("internal error: tab not found"); - this._tabs.splice(index, 1); - try { - this._delegate.onUntrack(tab); - } catch (e) { - console.exception(e); - } - }, - _safeSelectTab: function safeSelectTab(tab) { - var index = this._tabs.indexOf(tab); - if (index == -1) - console.error("internal error: tab not found"); - try { - if (this._delegate.onSelect) - this._delegate.onSelect(tab); - } catch (e) { - console.exception(e); - } - }, - _safeDOMContentLoaded: function safeDOMContentLoaded(event) { - let tabBrowser = event.currentTarget; - let tabBrowserIndex = tabBrowser.getBrowserIndexForDocument(event.target); - // TODO: I'm seeing this when loading data url images - if (tabBrowserIndex == -1) - return; - let tab = tabBrowser.tabContainer.getItemAtIndex(tabBrowserIndex); - let index = this._tabs.indexOf(tab); - if (index == -1) - console.error("internal error: tab not found"); - try { - if (this._delegate.onReady) - this._delegate.onReady(tab); - } catch (e) { - console.exception(e); - } - }, - _safeLoad: function safeLoad(event) { - let tab = event.target; - let index = this._tabs.indexOf(tab); - if (index == -1) - console.error("internal error: tab not found"); - try { - if (this._delegate.onLoad) - this._delegate.onLoad(tab); - } catch (e) { - console.exception(e); - } - }, - _safeMozAfterPaint: function safeMozAfterPaint(event) { - let win = event.currentTarget.ownerDocument.defaultView; - let tabIndex = win.gBrowser.getBrowserIndexForDocument(event.target.document); - if (tabIndex == -1) - return; - let tab = win.gBrowser.tabContainer.getItemAtIndex(tabIndex); - let index = this._tabs.indexOf(tab); - if (index == -1) - console.error("internal error: tab not found"); - try { - if (this._delegate.onPaint) - this._delegate.onPaint(tab); - } catch (e) { - console.exception(e); - } - }, - handleEvent: function handleEvent(event) { - switch (event.type) { - case "TabOpen": - this._safeTrackTab(event.target); - break; - case "TabClose": - this._safeUntrackTab(event.target); - break; - case "TabSelect": - this._safeSelectTab(event.target); - break; - case "DOMContentLoaded": - this._safeDOMContentLoaded(event); - break; - case "load": - this._safeLoad(event); - break; - case "MozAfterPaint": - this._safeMozAfterPaint(event); - break; - default: - throw new Error("internal error: unknown event type: " + - event.type); - } - }, - onTrack: function onTrack(tabbrowser) { - for (let tab in tabIterator(tabbrowser)) - this._safeTrackTab(tab); - tabbrowser.tabContainer.addEventListener("TabOpen", this, false); - tabbrowser.tabContainer.addEventListener("TabClose", this, false); - tabbrowser.tabContainer.addEventListener("TabSelect", this, false); - tabbrowser.ownerDocument.defaultView.gBrowser.addEventListener("DOMContentLoaded", this, false); - }, - onUntrack: function onUntrack(tabbrowser) { - for (let tab in tabIterator(tabbrowser)) - this._safeUntrackTab(tab); - tabbrowser.tabContainer.removeEventListener("TabOpen", this, false); - tabbrowser.tabContainer.removeEventListener("TabClose", this, false); - tabbrowser.tabContainer.removeEventListener("TabSelect", this, false); - tabbrowser.ownerDocument.defaultView.gBrowser.removeEventListener("DOMContentLoaded", this, false); - }, - unload: function unload() { - this._tracker.unload(); - } -}; - -// Utility to get a thumbnail canvas from a tab object -function getThumbnailCanvasForTab(tabEl, window) { - var thumbnail = window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); - thumbnail.mozOpaque = true; - window = tabEl.linkedBrowser.contentWindow; - thumbnail.width = Math.ceil(window.screen.availWidth / 5.75); - var aspectRatio = 0.5625; // 16:9 - thumbnail.height = Math.round(thumbnail.width * aspectRatio); - var ctx = thumbnail.getContext("2d"); - var snippetWidth = window.innerWidth * .6; - var scale = thumbnail.width / snippetWidth; - ctx.scale(scale, scale); - ctx.drawWindow(window, window.scrollX, window.scrollY, snippetWidth, snippetWidth * aspectRatio, "rgb(255,255,255)"); - return thumbnail; -} - -// Utility to return the contents of the target of a chrome URL -function getChromeURLContents(chromeURL) { - let io = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - let channel = io.newChannel(chromeURL, null, null); - let input = channel.open(); - let stream = Cc["@mozilla.org/binaryinputstream;1"]. - createInstance(Ci.nsIBinaryInputStream); - stream.setInputStream(input); - let str = stream.readBytes(input.available()); - stream.close(); - input.close(); - return str; -} diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/traits.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/traits.js deleted file mode 100644 index 30ae342..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/traits.js +++ /dev/null @@ -1,187 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "deprecated" -}; - -const { - compose: _compose, - override: _override, - resolve: _resolve, - trait: _trait, - //create: _create, - required, -} = require('./traits/core'); - -const defineProperties = Object.defineProperties, - freeze = Object.freeze, - create = Object.create; - -/** - * Work around bug 608959 by defining the _create function here instead of - * importing it from traits/core. For docs on this function, see the create - * function in that module. - * - * FIXME: remove this workaround in favor of importing the function once that - * bug has been fixed. - */ -function _create(proto, trait) { - let properties = {}, - keys = Object.getOwnPropertyNames(trait); - for each(let key in keys) { - let descriptor = trait[key]; - if (descriptor.required && - !Object.prototype.hasOwnProperty.call(proto, key)) - throw new Error('Missing required property: ' + key); - else if (descriptor.conflict) - throw new Error('Remaining conflicting property: ' + key); - else - properties[key] = descriptor; - } - return Object.create(proto, properties); -} - -/** - * Placeholder for `Trait.prototype` - */ -let TraitProto = Object.prototype; - -function Get(key) this[key] -function Set(key, value) this[key] = value - -/** - * Creates anonymous trait descriptor from the passed argument, unless argument - * is a trait constructor. In later case trait's already existing properties - * descriptor is returned. - * This is module's internal function and is used as a gateway to a trait's - * internal properties descriptor. - * @param {Function} $ - * Composed trait's constructor. - * @returns {Object} - * Private trait property of the composition. - */ -function TraitDescriptor(object) - ( - 'function' == typeof object && - (object.prototype == TraitProto || object.prototype instanceof Trait) - ) ? object._trait(TraitDescriptor) : _trait(object) - -function Public(instance, trait) { - let result = {}, - keys = Object.getOwnPropertyNames(trait); - for each (let key in keys) { - if ('_' === key.charAt(0) && '__iterator__' !== key ) - continue; - let property = trait[key], - descriptor = { - configurable: property.configurable, - enumerable: property.enumerable - }; - if (property.get) - descriptor.get = property.get.bind(instance); - if (property.set) - descriptor.set = property.set.bind(instance); - if ('value' in property) { - let value = property.value; - if ('function' === typeof value) { - descriptor.value = property.value.bind(instance); - descriptor.writable = property.writable; - } else { - descriptor.get = Get.bind(instance, key); - descriptor.set = Set.bind(instance, key); - } - } - result[key] = descriptor; - } - return result; -} - -/** - * This is private function that composes new trait with privates. - */ -function Composition(trait) { - function Trait() { - let self = _create({}, trait); - self._public = create(Trait.prototype, Public(self, trait)); - delete self._public.constructor; - if (Object === self.constructor) - self.constructor = Trait; - else - return self.constructor.apply(self, arguments) || self._public; - return self._public; - } - defineProperties(Trait, { - prototype: { value: freeze(create(TraitProto, { - constructor: { value: constructor, writable: true } - }))}, // writable is `true` to avoid getters in custom ES5 - displayName: { value: (trait.constructor || constructor).name }, - compose: { value: compose, enumerable: true }, - override: { value: override, enumerable: true }, - resolve: { value: resolve, enumerable: true }, - required: { value: required, enumerable: true }, - _trait: { value: function _trait(caller) - caller === TraitDescriptor ? trait : undefined - } - }); - return freeze(Trait); -} - -/** - * Composes new trait out of itself and traits / property maps passed as an - * arguments. If two or more traits / property maps have properties with the - * same name, the new trait will contain a "conflict" property for that name. - * This is a commutative and associative operation, and the order of its - * arguments is not significant. - * @params {Object|Function} - * List of Traits or property maps to create traits from. - * @returns {Function} - * New trait containing the combined properties of all the traits. - */ -function compose() { - let traits = Array.slice(arguments, 0); - traits.push(this); - return Composition(_compose.apply(null, traits.map(TraitDescriptor))); -} - -/** - * Composes a new trait with all of the combined properties of `this` and the - * argument traits. In contrast to `compose`, `override` immediately resolves - * all conflicts resulting from this composition by overriding the properties of - * later traits. Trait priority is from left to right. I.e. the properties of - * the leftmost trait are never overridden. - * @params {Object} trait - * @returns {Object} - */ -function override() { - let traits = Array.slice(arguments, 0); - traits.push(this); - return Composition(_override.apply(null, traits.map(TraitDescriptor))); -} - -/** - * Composes new resolved trait, with all the same properties as this - * trait, except that all properties whose name is an own property of - * `resolutions` will be renamed to `resolutions[name]`. If it is - * `resolutions[name]` is `null` value is changed into a required property - * descriptor. - */ -function resolve(resolutions) - Composition(_resolve(resolutions, TraitDescriptor(this))) - -/** - * Base Trait, that all the traits are composed of. - */ -const Trait = Composition({ - /** - * Internal property holding public API of this instance. - */ - _public: { value: null, configurable: true, writable: true }, - toString: { value: function() '[object ' + this.constructor.name + ']' } -}); -TraitProto = Trait.prototype; -exports.Trait = Trait; - diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/traits/core.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/traits/core.js deleted file mode 100644 index c08c38f..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/traits/core.js +++ /dev/null @@ -1,322 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "deprecated" -}; - -// Design inspired by: http://www.traitsjs.org/ - -// shortcuts -const getOwnPropertyNames = Object.getOwnPropertyNames, - getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor, - hasOwn = Object.prototype.hasOwnProperty, - _create = Object.create; - -function doPropertiesMatch(object1, object2, name) { - // If `object1` has property with the given `name` - return name in object1 ? - // then `object2` should have it with the same value. - name in object2 && object1[name] === object2[name] : - // otherwise `object2` should not have property with the given `name`. - !(name in object2); -} - -/** - * Compares two trait custom property descriptors if they are the same. If - * both are `conflict` or all the properties of descriptor are equal returned - * value will be `true`, otherwise it will be `false`. - * @param {Object} desc1 - * @param {Object} desc2 - */ -function areSame(desc1, desc2) { - return ('conflict' in desc1 && desc1.conflict && - 'conflict' in desc2 && desc2.conflict) || - (doPropertiesMatch(desc1, desc2, 'get') && - doPropertiesMatch(desc1, desc2, 'set') && - doPropertiesMatch(desc1, desc2, 'value') && - doPropertiesMatch(desc1, desc2, 'enumerable') && - doPropertiesMatch(desc1, desc2, 'required') && - doPropertiesMatch(desc1, desc2, 'conflict')); -} - -/** - * Converts array to an object whose own property names represent - * values of array. - * @param {String[]} names - * @returns {Object} - * @example - * Map(['foo', ...]) => { foo: true, ...} - */ -function Map(names) { - let map = {}; - for each (let name in names) - map[name] = true; - return map; -} - - -const ERR_CONFLICT = 'Remaining conflicting property: ', - ERR_REQUIRED = 'Missing required property: '; -/** - * Constant singleton, representing placeholder for required properties. - * @type {Object} - */ -const required = { toString: function()'<Trait.required>' }; -exports.required = required; - -/** - * Generates custom **required** property descriptor. Descriptor contains - * non-standard property `required` that is equal to `true`. - * @param {String} name - * property name to generate descriptor for. - * @returns {Object} - * custom property descriptor - */ -function Required(name) { - function required() { throw new Error(ERR_REQUIRED + name) } - return { - get: required, - set: required, - required: true - }; -} - -/** - * Generates custom **conflicting** property descriptor. Descriptor contains - * non-standard property `conflict` that is equal to `true`. - * @param {String} name - * property name to generate descriptor for. - * @returns {Object} - * custom property descriptor - */ -function Conflict(name) { - function conflict() { throw new Error(ERR_CONFLICT + name) } - return { - get: conflict, - set: conflict, - conflict: true - }; -} - -/** - * Function generates custom properties descriptor of the `object`s own - * properties. All the inherited properties are going to be ignored. - * Properties with values matching `required` singleton will be marked as - * 'required' properties. - * @param {Object} object - * Set of properties to generate trait from. - * @returns {Object} - * Properties descriptor of all of the `object`'s own properties. - */ -function trait(properties) { - let result = {}, - keys = getOwnPropertyNames(properties); - for each (let key in keys) { - let descriptor = getOwnPropertyDescriptor(properties, key); - result[key] = (required === descriptor.value) ? Required(key) : descriptor; - } - return result; -} -exports.Trait = exports.trait = trait; - -/** - * Composes new trait. If two or more traits have own properties with the - * same name, the new trait will contain a 'conflict' property for that name. - * 'compose' is a commutative and associative operation, and the order of its - * arguments is not significant. - * - * @params {Object} trait - * Takes traits as an arguments - * @returns {Object} - * New trait containing the combined own properties of all the traits. - * @example - * var newTrait = compose(trait_1, trait_2, ..., trait_N); - */ -function compose(trait1, trait2) { - let traits = Array.slice(arguments, 0), - result = {}; - for each (let trait in traits) { - let keys = getOwnPropertyNames(trait); - for each (let key in keys) { - let descriptor = trait[key]; - // if property already exists and it's not a requirement - if (hasOwn.call(result, key) && !result[key].required) { - if (descriptor.required) - continue; - if (!areSame(descriptor, result[key])) - result[key] = Conflict(key); - } else { - result[key] = descriptor; - } - } - } - return result; -} -exports.compose = compose; - -/** - * Composes new trait with the same own properties as the original trait, - * except that all property names appearing in the first argument are replaced - * by 'required' property descriptors. - * @param {String[]} keys - * Array of strings property names. - * @param {Object} trait - * A trait some properties of which should be excluded. - * @returns {Object} - * @example - * var newTrait = exclude(['name', ...], trait) - */ -function exclude(keys, trait) { - let exclusions = Map(keys), - result = {}; - - keys = getOwnPropertyNames(trait); - - for each (let key in keys) { - if (!hasOwn.call(exclusions, key) || trait[key].required) - result[key] = trait[key]; - else - result[key] = Required(key); - } - return result; -} - -/** - * Composes a new trait with all of the combined properties of the argument - * traits. In contrast to `compose`, `override` immediately resolves all - * conflicts resulting from this composition by overriding the properties of - * later traits. Trait priority is from left to right. I.e. the properties of - * the leftmost trait are never overridden. - * @params {Object} trait - * @returns {Object} - * @examples - * // override is associative: - * override(t1,t2,t3) - * // is equivalent to - * override(t1, override(t2, t3)) - * // or - * to override(override(t1, t2), t3) - * - * // override is not commutative: - * override(t1,t2) - * // is not equivalent to - * override(t2,t1) - */ -function override() { - let traits = Array.slice(arguments, 0), - result = {}; - for each (let trait in traits) { - let keys = getOwnPropertyNames(trait); - for each(let key in keys) { - let descriptor = trait[key]; - if (!hasOwn.call(result, key) || result[key].required) - result[key] = descriptor; - } - } - return result; -} -exports.override = override; - -/** - * Composes a new trait with the same properties as the original trait, except - * that all properties whose name is an own property of map will be renamed to - * map[name], and a 'required' property for name will be added instead. - * @param {Object} map - * An object whose own properties serve as a mapping from old names to new - * names. - * @param {Object} trait - * A trait object - * @returns {Object} - * @example - * var newTrait = rename(map, trait); - */ -function rename(map, trait) { - let result = {}, - keys = getOwnPropertyNames(trait); - for each(let key in keys) { - // must be renamed & it's not requirement - if (hasOwn.call(map, key) && !trait[key].required) { - let alias = map[key]; - if (hasOwn.call(result, alias) && !result[alias].required) - result[alias] = Conflict(alias); - else - result[alias] = trait[key]; - if (!hasOwn.call(result, key)) - result[key] = Required(key); - } else { // must not be renamed or its a requirement - // property is not in result trait yet - if (!hasOwn.call(result, key)) - result[key] = trait[key]; - // property is already in resulted trait & it's not requirement - else if (!trait[key].required) - result[key] = Conflict(key); - } - } - return result; -} - -/** -* Composes new resolved trait, with all the same properties as the original -* trait, except that all properties whose name is an own property of -* resolutions will be renamed to `resolutions[name]`. If it is -* `resolutions[name]` is `null` value is changed into a required property -* descriptor. -* function can be implemented as `rename(map,exclude(exclusions, trait))` -* where map is the subset of mappings from oldName to newName and exclusions -* is an array of all the keys that map to `null`. -* Note: it's important to **first** `exclude`, **then** `rename`, since -* `exclude` and rename are not associative. -* @param {Object} resolutions -* An object whose own properties serve as a mapping from old names to new -* names, or to `null` if the property should be excluded. -* @param {Object} trait -* A trait object -* @returns {Object} -* Resolved trait with the same own properties as the original trait. -*/ -function resolve(resolutions, trait) { - let renames = {}, - exclusions = [], - keys = getOwnPropertyNames(resolutions); - for each (let key in keys) { // pre-process renamed and excluded properties - if (resolutions[key]) // old name -> new name - renames[key] = resolutions[key]; - else // name -> undefined - exclusions.push(key); - } - return rename(renames, exclude(exclusions, trait)); -} -exports.resolve = resolve; - -/** - * `create` is like `Object.create`, except that it ensures that: - * - an exception is thrown if 'trait' still contains required properties - * - an exception is thrown if 'trait' still contains conflicting - * properties - * @param {Object} - * prototype of the completed object - * @param {Object} trait - * trait object to be turned into a complete object - * @returns {Object} - * An object with all of the properties described by the trait. - */ -function create(proto, trait) { - let properties = {}, - keys = getOwnPropertyNames(trait); - for each(let key in keys) { - let descriptor = trait[key]; - if (descriptor.required && !hasOwn.call(proto, key)) - throw new Error(ERR_REQUIRED + key); - else if (descriptor.conflict) - throw new Error(ERR_CONFLICT + key); - else - properties[key] = descriptor; - } - return _create(proto, properties); -} -exports.create = create; - diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/unit-test-finder.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/unit-test-finder.js deleted file mode 100644 index 880f554..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/unit-test-finder.js +++ /dev/null @@ -1,71 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "deprecated" -}; - -const file = require("../io/file"); -const memory = require('./memory'); -const suites = require('@test/options').allTestModules; - - -const NOT_TESTS = ['setup', 'teardown']; - -var TestFinder = exports.TestFinder = function TestFinder(options) { - memory.track(this); - this.filter = options.filter; - this.testInProcess = options.testInProcess === false ? false : true; - this.testOutOfProcess = options.testOutOfProcess === true ? true : false; -}; - -TestFinder.prototype = { - findTests: function findTests(cb) { - var self = this; - var tests = []; - var filter; - // A filter string is {fileNameRegex}[:{testNameRegex}] - ie, a colon - // optionally separates a regex for the test fileName from a regex for the - // testName. - if (this.filter) { - var colonPos = this.filter.indexOf(':'); - var filterFileRegex, filterNameRegex; - if (colonPos === -1) { - filterFileRegex = new RegExp(self.filter); - } else { - filterFileRegex = new RegExp(self.filter.substr(0, colonPos)); - filterNameRegex = new RegExp(self.filter.substr(colonPos + 1)); - } - // This function will first be called with just the filename; if - // it returns true the module will be loaded then the function - // called again with both the filename and the testname. - filter = function(filename, testname) { - return filterFileRegex.test(filename) && - ((testname && filterNameRegex) ? filterNameRegex.test(testname) - : true); - }; - } else - filter = function() {return true}; - - suites.forEach( - function(suite) { - var module = require(suite); - if (self.testInProcess) - for each (let name in Object.keys(module).sort()) { - if(NOT_TESTS.indexOf(name) === -1 && filter(suite, name)) { - tests.push({ - setup: module.setup, - teardown: module.teardown, - testFunction: module[name], - name: suite + "." + name - }); - } - } - }); - - cb(tests); - } -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/unit-test.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/unit-test.js deleted file mode 100644 index 6a8ac1b..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/unit-test.js +++ /dev/null @@ -1,451 +0,0 @@ -/* vim:st=2:sts=2:sw=2: - * 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"; - -module.metadata = { - "stability": "deprecated" -}; - -const memory = require('./memory'); -var timer = require("../timers"); - -exports.findAndRunTests = function findAndRunTests(options) { - var TestFinder = require("./unit-test-finder").TestFinder; - var finder = new TestFinder({ - filter: options.filter, - testInProcess: options.testInProcess, - testOutOfProcess: options.testOutOfProcess - }); - var runner = new TestRunner({fs: options.fs}); - finder.findTests( - function (tests) { - runner.startMany({tests: tests, - stopOnError: options.stopOnError, - onDone: options.onDone}); - }); -}; - -var TestRunner = exports.TestRunner = function TestRunner(options) { - if (options) { - this.fs = options.fs; - } - this.console = (options && "console" in options) ? options.console : console; - memory.track(this); - this.passed = 0; - this.failed = 0; - this.testRunSummary = []; - this.expectFailNesting = 0; -}; - -TestRunner.prototype = { - toString: function toString() "[object TestRunner]", - - DEFAULT_PAUSE_TIMEOUT: 10000, - PAUSE_DELAY: 500, - - _logTestFailed: function _logTestFailed(why) { - if (!(why in this.test.errors)) - this.test.errors[why] = 0; - this.test.errors[why]++; - if (!this.testFailureLogged) { - this.console.error("TEST FAILED: " + this.test.name + " (" + why + ")"); - this.testFailureLogged = true; - } - }, - - pass: function pass(message) { - if(!this.expectFailure) { - this.console.info("pass:", message); - this.passed++; - this.test.passed++; - } - else { - this.expectFailure = false; - this.fail('Failure Expected: ' + message); - } - }, - - fail: function fail(message) { - if(!this.expectFailure) { - this._logTestFailed("failure"); - this.console.error("fail:", message); - this.console.trace(); - this.failed++; - this.test.failed++; - } - else { - this.expectFailure = false; - this.pass(message); - } - }, - - expectFail: function(callback) { - this.expectFailure = true; - callback(); - this.expectFailure = false; - }, - - exception: function exception(e) { - this._logTestFailed("exception"); - this.console.exception(e); - this.failed++; - this.test.failed++; - }, - - assertMatches: function assertMatches(string, regexp, message) { - if (regexp.test(string)) { - if (!message) - message = uneval(string) + " matches " + uneval(regexp); - this.pass(message); - } else { - var no = uneval(string) + " doesn't match " + uneval(regexp); - if (!message) - message = no; - else - message = message + " (" + no + ")"; - this.fail(message); - } - }, - - assertRaises: function assertRaises(func, predicate, message) { - try { - func(); - if (message) - this.fail(message + " (no exception thrown)"); - else - this.fail("function failed to throw exception"); - } catch (e) { - var errorMessage; - if (typeof(e) == "string") - errorMessage = e; - else - errorMessage = e.message; - if (typeof(predicate) == "string") - this.assertEqual(errorMessage, predicate, message); - else - this.assertMatches(errorMessage, predicate, message); - } - }, - - assert: function assert(a, message) { - if (!a) { - if (!message) - message = "assertion failed, value is " + a; - this.fail(message); - } else - this.pass(message || "assertion successful"); - }, - - assertNotEqual: function assertNotEqual(a, b, message) { - if (a != b) { - if (!message) - message = "a != b != " + uneval(a); - this.pass(message); - } else { - var equality = uneval(a) + " == " + uneval(b); - if (!message) - message = equality; - else - message += " (" + equality + ")"; - this.fail(message); - } - }, - - assertEqual: function assertEqual(a, b, message) { - if (a == b) { - if (!message) - message = "a == b == " + uneval(a); - this.pass(message); - } else { - var inequality = uneval(a) + " != " + uneval(b); - if (!message) - message = inequality; - else - message += " (" + inequality + ")"; - this.fail(message); - } - }, - - assertNotStrictEqual: function assertNotStrictEqual(a, b, message) { - if (a !== b) { - if (!message) - message = "a !== b !== " + uneval(a); - this.pass(message); - } else { - var equality = uneval(a) + " === " + uneval(b); - if (!message) - message = equality; - else - message += " (" + equality + ")"; - this.fail(message); - } - }, - - assertStrictEqual: function assertStrictEqual(a, b, message) { - if (a === b) { - if (!message) - message = "a === b === " + uneval(a); - this.pass(message); - } else { - var inequality = uneval(a) + " !== " + uneval(b); - if (!message) - message = inequality; - else - message += " (" + inequality + ")"; - this.fail(message); - } - }, - - assertFunction: function assertFunction(a, message) { - this.assertStrictEqual('function', typeof a, message); - }, - - assertUndefined: function(a, message) { - this.assertStrictEqual('undefined', typeof a, message); - }, - - assertNotUndefined: function(a, message) { - this.assertNotStrictEqual('undefined', typeof a, message); - }, - - assertNull: function(a, message) { - this.assertStrictEqual(null, a, message); - }, - - assertNotNull: function(a, message) { - this.assertNotStrictEqual(null, a, message); - }, - - assertObject: function(a, message) { - this.assertStrictEqual('[object Object]', Object.prototype.toString.apply(a), message); - }, - - assertString: function(a, message) { - this.assertStrictEqual('[object String]', Object.prototype.toString.apply(a), message); - }, - - assertArray: function(a, message) { - this.assertStrictEqual('[object Array]', Object.prototype.toString.apply(a), message); - }, - - assertNumber: function(a, message) { - this.assertStrictEqual('[object Number]', Object.prototype.toString.apply(a), message); - }, - - done: function done() { - if (!this.isDone) { - this.isDone = true; - if(this.test.teardown) { - this.test.teardown(this); - } - if (this.waitTimeout !== null) { - timer.clearTimeout(this.waitTimeout); - this.waitTimeout = null; - } - // Do not leave any callback set when calling to `waitUntil` - this.waitUntilCallback = null; - if (this.test.passed == 0 && this.test.failed == 0) { - this._logTestFailed("empty test"); - this.failed++; - this.test.failed++; - } - - this.testRunSummary.push({ - name: this.test.name, - passed: this.test.passed, - failed: this.test.failed, - errors: [error for (error in this.test.errors)].join(", ") - }); - - if (this.onDone !== null) { - var onDone = this.onDone; - var self = this; - this.onDone = null; - timer.setTimeout(function() { onDone(self); }, 0); - } - } - }, - - // Set of assertion functions to wait for an assertion to become true - // These functions take the same arguments as the TestRunner.assert* methods. - waitUntil: function waitUntil() { - return this._waitUntil(this.assert, arguments); - }, - - waitUntilNotEqual: function waitUntilNotEqual() { - return this._waitUntil(this.assertNotEqual, arguments); - }, - - waitUntilEqual: function waitUntilEqual() { - return this._waitUntil(this.assertEqual, arguments); - }, - - waitUntilMatches: function waitUntilMatches() { - return this._waitUntil(this.assertMatches, arguments); - }, - - /** - * Internal function that waits for an assertion to become true. - * @param {Function} assertionMethod - * Reference to a TestRunner assertion method like test.assert, - * test.assertEqual, ... - * @param {Array} args - * List of arguments to give to the previous assertion method. - * All functions in this list are going to be called to retrieve current - * assertion values. - */ - _waitUntil: function waitUntil(assertionMethod, args) { - let count = 0; - let maxCount = this.DEFAULT_PAUSE_TIMEOUT / this.PAUSE_DELAY; - - // We need to ensure that test is asynchronous - if (!this.waitTimeout) - this.waitUntilDone(this.DEFAULT_PAUSE_TIMEOUT); - - let callback = null; - let finished = false; - - let test = this; - - // capture a traceback before we go async. - let traceback = require("../console/traceback"); - let stack = traceback.get(); - stack.splice(-2, 2); - let currentWaitStack = traceback.format(stack); - let timeout = null; - - function loop(stopIt) { - timeout = null; - - // Build a mockup object to fake TestRunner API and intercept calls to - // pass and fail methods, in order to retrieve nice error messages - // and assertion result - let mock = { - pass: function (msg) { - test.pass(msg); - test.waitUntilCallback = null; - if (callback && !stopIt) - callback(); - finished = true; - }, - fail: function (msg) { - // If we are called on test timeout, we stop the loop - // and print which test keeps failing: - if (stopIt) { - test.console.error("test assertion never became true:\n", - msg + "\n", - currentWaitStack); - if (timeout) - timer.clearTimeout(timeout); - return; - } - timeout = timer.setTimeout(loop, test.PAUSE_DELAY); - } - }; - - // Automatically call args closures in order to build arguments for - // assertion function - let appliedArgs = []; - for (let i = 0, l = args.length; i < l; i++) { - let a = args[i]; - if (typeof a == "function") { - try { - a = a(); - } - catch(e) { - test.fail("Exception when calling asynchronous assertion: " + e + - "\n" + e.stack); - finished = true; - return; - } - } - appliedArgs.push(a); - } - - // Finally call assertion function with current assertion values - assertionMethod.apply(mock, appliedArgs); - } - loop(); - this.waitUntilCallback = loop; - - // Return an object with `then` method, to offer a way to execute - // some code when the assertion passed or failed - return { - then: function (c) { - callback = c; - - // In case of immediate positive result, we need to execute callback - // immediately here: - if (finished) - callback(); - } - }; - }, - - waitUntilDone: function waitUntilDone(ms) { - if (ms === undefined) - ms = this.DEFAULT_PAUSE_TIMEOUT; - - var self = this; - - function tiredOfWaiting() { - self._logTestFailed("timed out"); - if (self.waitUntilCallback) { - self.waitUntilCallback(true); - self.waitUntilCallback = null; - } - self.failed++; - self.test.failed++; - self.done(); - } - - // We may already have registered a timeout callback - if (this.waitTimeout) - timer.clearTimeout(this.waitTimeout); - - this.waitTimeout = timer.setTimeout(tiredOfWaiting, ms); - }, - - startMany: function startMany(options) { - function runNextTest(self) { - var test = options.tests.shift(); - if (options.stopOnError && self.test && self.test.failed) { - self.console.error("aborted: test failed and --stop-on-error was specified"); - options.onDone(self); - } else if (test) { - self.start({test: test, onDone: runNextTest}); - } else { - options.onDone(self); - } - } - runNextTest(this); - }, - - start: function start(options) { - this.test = options.test; - this.test.passed = 0; - this.test.failed = 0; - this.test.errors = {}; - - this.isDone = false; - this.onDone = options.onDone; - this.waitTimeout = null; - this.testFailureLogged = false; - - try { - this.console.info("executing '" + this.test.name + "'"); - - if(this.test.setup) { - this.test.setup(this); - } - this.test.testFunction(this); - } catch (e) { - this.exception(e); - } - if (this.waitTimeout === null) - this.done(); - } -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/deprecated/window-utils.js b/tools/addon-sdk-1.12/lib/sdk/deprecated/window-utils.js deleted file mode 100644 index 66fcfba..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/deprecated/window-utils.js +++ /dev/null @@ -1,201 +0,0 @@ -/* 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'; - -module.metadata = { - 'stability': 'deprecated' -}; - -const { Cc, Ci } = require('chrome'); -const { EventEmitter } = require('../deprecated/events'); -const { Trait } = require('../deprecated/traits'); -const { when } = require('../system/unload'); -const { getInnerId, getOuterId, windows, isDocumentLoaded, isBrowser, - getMostRecentBrowserWindow } = require('../window/utils'); -const errors = require('../deprecated/errors'); -const { deprecateFunction } = require('../util/deprecate'); - -const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1']. - getService(Ci.nsIWindowWatcher); -const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. - getService(Ci.nsIAppShellService); - -/** - * An iterator for XUL windows currently in the application. - * - * @return A generator that yields XUL windows exposing the - * nsIDOMWindow interface. - */ -function windowIterator() { - // Bug 752631: We only pass already loaded window in order to avoid - // breaking XUL windows DOM. DOM is broken when some JS code try - // to access DOM during "uninitialized" state of the related document. - let list = windows().filter(isDocumentLoaded); - for (let i = 0, l = list.length; i < l; i++) { - yield list[i]; - } -}; -exports.windowIterator = windowIterator; - -/** - * An iterator for browser windows currently open in the application. - * @returns {Function} - * A generator that yields browser windows exposing the `nsIDOMWindow` - * interface. - */ -function browserWindowIterator() { - for each (let window in windowIterator()) { - if (isBrowser(window)) - yield window; - } -} -exports.browserWindowIterator = browserWindowIterator; - -function WindowTracker(delegate) { - if (!(this instanceof WindowTracker)) { - return new WindowTracker(delegate); - } - - this._delegate = delegate; - this._loadingWindows = []; - - for each (let window in windows()) - this._regWindow(window); - windowWatcher.registerNotification(this); - - require('../system/unload').ensure(this); - - return this; -}; - -WindowTracker.prototype = { - _regLoadingWindow: function _regLoadingWindow(window) { - this._loadingWindows.push(window); - window.addEventListener('load', this, true); - }, - - _unregLoadingWindow: function _unregLoadingWindow(window) { - var index = this._loadingWindows.indexOf(window); - - if (index != -1) { - this._loadingWindows.splice(index, 1); - window.removeEventListener('load', this, true); - } - }, - - _regWindow: function _regWindow(window) { - if (window.document.readyState == 'complete') { - this._unregLoadingWindow(window); - this._delegate.onTrack(window); - } else - this._regLoadingWindow(window); - }, - - _unregWindow: function _unregWindow(window) { - if (window.document.readyState == 'complete') { - if (this._delegate.onUntrack) - this._delegate.onUntrack(window); - } else { - this._unregLoadingWindow(window); - } - }, - - unload: function unload() { - windowWatcher.unregisterNotification(this); - for each (let window in windows()) - this._unregWindow(window); - }, - - handleEvent: errors.catchAndLog(function handleEvent(event) { - if (event.type == 'load' && event.target) { - var window = event.target.defaultView; - if (window) - this._regWindow(window); - } - }), - - observe: errors.catchAndLog(function observe(subject, topic, data) { - var window = subject.QueryInterface(Ci.nsIDOMWindow); - if (topic == 'domwindowopened') - this._regWindow(window); - else - this._unregWindow(window); - }) -}; -exports.WindowTracker = WindowTracker; - -const WindowTrackerTrait = Trait.compose({ - _onTrack: Trait.required, - _onUntrack: Trait.required, - constructor: function WindowTrackerTrait() { - WindowTracker({ - onTrack: this._onTrack.bind(this), - onUntrack: this._onUntrack.bind(this) - }); - } -}); -exports.WindowTrackerTrait = WindowTrackerTrait; - -var gDocsToClose = []; - -function onDocUnload(event) { - var index = gDocsToClose.indexOf(event.target); - if (index == -1) - throw new Error('internal error: unloading document not found'); - var document = gDocsToClose.splice(index, 1)[0]; - // Just in case, let's remove the event listener too. - document.defaultView.removeEventListener('unload', onDocUnload, false); -} - -onDocUnload = require('./errors').catchAndLog(onDocUnload); - -exports.closeOnUnload = function closeOnUnload(window) { - window.addEventListener('unload', onDocUnload, false); - gDocsToClose.push(window.document); -}; - -Object.defineProperties(exports, { - activeWindow: { - enumerable: true, - get: function() { - return Cc['@mozilla.org/appshell/window-mediator;1'] - .getService(Ci.nsIWindowMediator) - .getMostRecentWindow(null); - }, - set: function(window) { - try { window.focus(); } catch (e) { } - } - }, - activeBrowserWindow: { - enumerable: true, - get: getMostRecentBrowserWindow - } -}); - - -/** - * Returns the ID of the window's current inner window. - */ -exports.getInnerId = deprecateFunction(getInnerId, - 'require("window-utils").getInnerId is deprecated, ' + - 'please use require("window/utils").getInnerId instead' -); - -exports.getOuterId = deprecateFunction(getOuterId, - 'require("window-utils").getOuterId is deprecated, ' + - 'please use require("window/utils").getOuterId instead' -); - -exports.isBrowser = deprecateFunction(isBrowser, - 'require("window-utils").isBrowser is deprecated, ' + - 'please use require("window/utils").isBrowser instead' -); - -exports.hiddenWindow = appShellService.hiddenDOMWindow; - -when( - function() { - gDocsToClose.slice().forEach( - function(doc) { doc.defaultView.close(); }); - }); diff --git a/tools/addon-sdk-1.12/lib/sdk/dom/events.js b/tools/addon-sdk-1.12/lib/sdk/dom/events.js deleted file mode 100644 index 3f2d21c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/dom/events.js +++ /dev/null @@ -1,140 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -// Utility function that returns copy of the given `text` with last character -// removed if it is `"s"`. -function singularify(text) { - return text[text.length - 1] === "s" ? text.substr(0, text.length - 1) : text; -} - -// Utility function that takes event type, argument is passed to -// `document.createEvent` and returns name of the initializer method of the -// given event. Please note that there are some event types whose initializer -// methods can't be guessed by this function. For more details see following -// link: https://developer.mozilla.org/En/DOM/Document.createEvent -function getInitializerName(category) { - return "init" + singularify(category); -} - -/** - * Registers an event `listener` on a given `element`, that will be called - * when events of specified `type` is dispatched on the `element`. - * @param {Element} element - * Dom element to register listener on. - * @param {String} type - * A string representing the - * [event type](https://developer.mozilla.org/en/DOM/event.type) to - * listen for. - * @param {Function} listener - * Function that is called whenever an event of the specified `type` - * occurs. - * @param {Boolean} capture - * If true, indicates that the user wishes to initiate capture. After - * initiating capture, all events of the specified type will be dispatched - * to the registered listener before being dispatched to any `EventTarget`s - * beneath it in the DOM tree. Events which are bubbling upward through - * the tree will not trigger a listener designated to use capture. - * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) - * for a detailed explanation. - */ -function on(element, type, listener, capture) { - // `capture` defaults to `false`. - capture = capture || false; - element.addEventListener(type, listener, capture); -} -exports.on = on; - -/** - * Registers an event `listener` on a given `element`, that will be called - * only once, next time event of specified `type` is dispatched on the - * `element`. - * @param {Element} element - * Dom element to register listener on. - * @param {String} type - * A string representing the - * [event type](https://developer.mozilla.org/en/DOM/event.type) to - * listen for. - * @param {Function} listener - * Function that is called whenever an event of the specified `type` - * occurs. - * @param {Boolean} capture - * If true, indicates that the user wishes to initiate capture. After - * initiating capture, all events of the specified type will be dispatched - * to the registered listener before being dispatched to any `EventTarget`s - * beneath it in the DOM tree. Events which are bubbling upward through - * the tree will not trigger a listener designated to use capture. - * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) - * for a detailed explanation. - */ -function once(element, type, listener, capture) { - on(element, type, function selfRemovableListener(event) { - removeListener(element, type, selfRemovableListener, capture); - listener.apply(this, arguments); - }, capture); -} -exports.once = once; - -/** - * Unregisters an event `listener` on a given `element` for the events of the - * specified `type`. - * - * @param {Element} element - * Dom element to unregister listener from. - * @param {String} type - * A string representing the - * [event type](https://developer.mozilla.org/en/DOM/event.type) to - * listen for. - * @param {Function} listener - * Function that is called whenever an event of the specified `type` - * occurs. - * @param {Boolean} capture - * If true, indicates that the user wishes to initiate capture. After - * initiating capture, all events of the specified type will be dispatched - * to the registered listener before being dispatched to any `EventTarget`s - * beneath it in the DOM tree. Events which are bubbling upward through - * the tree will not trigger a listener designated to use capture. - * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) - * for a detailed explanation. - */ -function removeListener(element, type, listener, capture) { - element.removeEventListener(type, listener, capture); -} -exports.removeListener = removeListener; - -/** - * Emits event of the specified `type` and `category` on the given `element`. - * Specified `settings` are used to initialize event before dispatching it. - * @param {Element} element - * Dom element to dispatch event on. - * @param {String} type - * A string representing the - * [event type](https://developer.mozilla.org/en/DOM/event.type). - * @param {Object} options - * Options object containing following properties: - * - `category`: String passed to the `document.createEvent`. Option is - * optional and defaults to "UIEvents". - * - `initializer`: If passed it will be used as name of the method used - * to initialize event. If omitted name will be generated from the - * `category` field by prefixing it with `"init"` and removing last - * character if it matches `"s"`. - * - `settings`: Array of settings that are forwarded to the event - * initializer after firs `type` argument. - * @see https://developer.mozilla.org/En/DOM/Document.createEvent - */ -function emit(element, type, { category, initializer, settings }) { - category = category || "UIEvents"; - initializer = initializer || getInitializerName(category); - let document = element.ownerDocument; - let event = document.createEvent(category); - event[initializer].apply(event, [type].concat(settings)); - element.dispatchEvent(event); -}; -exports.emit = emit; diff --git a/tools/addon-sdk-1.12/lib/sdk/dom/events/keys.js b/tools/addon-sdk-1.12/lib/sdk/dom/events/keys.js deleted file mode 100644 index 01e4a0d..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/dom/events/keys.js +++ /dev/null @@ -1,64 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { emit } = require("../events"); -const { getCodeForKey, toJSON } = require("../../keyboard/utils"); -const { has } = require("../../util/array"); -const { isString } = require("../../lang/type"); - -const INITIALIZER = "initKeyEvent"; -const CATEGORY = "KeyboardEvent"; - -function Options(options) { - if (!isString(options)) - return options; - - var { key, modifiers } = toJSON(options); - return { - key: key, - control: has(modifiers, "control"), - alt: has(modifiers, "alt"), - shift: has(modifiers, "shift"), - meta: has(modifiers, "meta") - }; -} - -var keyEvent = exports.keyEvent = function keyEvent(element, type, options) { - - emit(element, type, { - initializer: INITIALIZER, - category: CATEGORY, - settings: [ - !("bubbles" in options) || options.bubbles !== false, - !("cancelable" in options) || options.cancelable !== false, - "window" in options && options.window ? options.window : null, - "control" in options && !!options.control, - "alt" in options && !!options.alt, - "shift" in options && !!options.shift, - "meta" in options && !!options.meta, - getCodeForKey(options.key) || 0, - options.key.length === 1 ? options.key.charCodeAt(0) : 0 - ] - }); -} - -exports.keyDown = function keyDown(element, options) { - keyEvent(element, "keydown", Options(options)); -}; - -exports.keyUp = function keyUp(element, options) { - keyEvent(element, "keyup", Options(options)); -}; - -exports.keyPress = function keyPress(element, options) { - keyEvent(element, "keypress", Options(options)); -}; - diff --git a/tools/addon-sdk-1.12/lib/sdk/event/core.js b/tools/addon-sdk-1.12/lib/sdk/event/core.js deleted file mode 100644 index e63e3b9..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/event/core.js +++ /dev/null @@ -1,152 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const UNCAUGHT_ERROR = 'An error event was emitted for which there was no listener.'; -const BAD_LISTENER = 'The event listener must be a function.'; - -const { ns } = require('../core/namespace'); - -const event = ns(); - -// Utility function to access given event `target` object's event listeners for -// the specific event `type`. If listeners for this type does not exists they -// will be created. -const observers = function observers(target, type) { - let listeners = event(target); - return type in listeners ? listeners[type] : listeners[type] = []; -}; - -/** - * Registers an event `listener` that is called every time events of - * specified `type` is emitted on the given event `target`. - * @param {Object} target - * Event target object. - * @param {String} type - * The type of event. - * @param {Function} listener - * The listener function that processes the event. - */ -function on(target, type, listener) { - if (typeof(listener) !== 'function') - throw new Error(BAD_LISTENER); - - let listeners = observers(target, type); - if (!~listeners.indexOf(listener)) - listeners.push(listener); -} -exports.on = on; - -/** - * Registers an event `listener` that is called only the next time an event - * of the specified `type` is emitted on the given event `target`. - * @param {Object} target - * Event target object. - * @param {String} type - * The type of the event. - * @param {Function} listener - * The listener function that processes the event. - */ -function once(target, type, listener) { - on(target, type, function observer() { - off(target, type, observer); - listener.apply(target, arguments); - }); -} -exports.once = once; - -/** - * Execute each of the listeners in order with the supplied arguments. - * All the exceptions that are thrown by listeners during the emit - * are caught and can be handled by listeners of 'error' event. Thrown - * exceptions are passed as an argument to an 'error' event listener. - * If no 'error' listener is registered exception will be logged into an - * error console. - * @param {Object} target - * Event target object. - * @param {String} type - * The type of event. - * @params {Object|Number|String|Boolean} message - * First argument that will be passed to listeners. - * @params {Object|Number|String|Boolean} ... - * More arguments that will be passed to listeners. - */ -function emit(target, type, message /*, ...*/) { - for each (let item in emit.lazy.apply(emit.lazy, arguments)) { - // We just iterate, iterator take care of emitting events. - } -} - -/** - * This is very experimental feature that you should not use unless absolutely - * need it. Also it may be removed at any point without any further notice. - * - * Creates lazy iterator of return values of listeners. You can think of it - * as lazy array of return values of listeners for the `emit` with the given - * arguments. - */ -emit.lazy = function lazy(target, type, message /*, ...*/) { - let args = Array.slice(arguments, 2) - let listeners = observers(target, type).slice() - while (listeners.length) { - try { - yield listeners.shift().apply(target, args); - } - catch (error) { - // If exception is not thrown by a error listener and error listener is - // registered emit `error` event. Otherwise dump exception to the console. - if (type !== 'error' && observers(target, 'error').length) - emit(target, 'error', error); - else - console.exception(error); - } - } -} -exports.emit = emit; - -/** - * Removes an event `listener` for the given event `type` on the given event - * `target`. If no `listener` is passed removes all listeners of the given - * `type`. If `type` is not passed removes all the listeners of the given - * event `target`. - * @param {Object} target - * The event target object. - * @param {String} type - * The type of event. - * @param {Function} listener - * The listener function that processes the event. - */ -function off(target, type, listener) { - let length = arguments.length; - if (length === 3) { - let listeners = observers(target, type); - let index = listeners.indexOf(listener); - if (~index) - listeners.splice(index, 1); - } - else if (length === 2) { - observers(target, type).splice(0); - } - else if (length === 1) { - let listeners = event(target); - Object.keys(listeners).forEach(function(type) delete listeners[type]); - } -} -exports.off = off; - -/** - * Returns a number of event listeners registered for the given event `type` - * on the given event `target`. - */ -function count(target, type) { - return observers(target, type).length; -} -exports.count = count; diff --git a/tools/addon-sdk-1.12/lib/sdk/event/target.js b/tools/addon-sdk-1.12/lib/sdk/event/target.js deleted file mode 100644 index b8906c0..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/event/target.js +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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'; - -module.metadata = { - "stability": "stable" -}; - -const { on, once, off } = require('./core'); -const { method } = require('../lang/functional'); -const { Class } = require('../core/heritage'); - -const EVENT_TYPE_PATTERN = /^on([A-Z]\w+$)/; - -/** - * `EventTarget` is an exemplar for creating an objects that can be used to - * add / remove event listeners on them. Events on these objects may be emitted - * via `emit` function exported by 'event/core' module. - */ -const EventTarget = Class({ - /** - * Method initializes `this` event source. It goes through properties of a - * given `options` and registers listeners for the ones that look like an - * event listeners. - */ - initialize: function initialize(options) { - options = options || {}; - // Go through each property and registers event listeners for those - // that have a name matching following pattern (`onEventType`). - Object.keys(options).forEach(function onEach(key) { - let match = EVENT_TYPE_PATTERN.exec(key); - let type = match && match[1].toLowerCase(); - let listener = options[key]; - - if (type && typeof(listener) === 'function') - this.on(type, listener); - }, this); - }, - /** - * Registers an event `listener` that is called every time events of - * specified `type` are emitted. - * @param {String} type - * The type of event. - * @param {Function} listener - * The listener function that processes the event. - * @example - * worker.on('message', function (data) { - * console.log('data received: ' + data) - * }) - */ - on: method(on), - /** - * Registers an event `listener` that is called once the next time an event - * of the specified `type` is emitted. - * @param {String} type - * The type of the event. - * @param {Function} listener - * The listener function that processes the event. - */ - once: method(once), - /** - * Removes an event `listener` for the given event `type`. - * @param {String} type - * The type of event. - * @param {Function} listener - * The listener function that processes the event. - */ - removeListener: function removeListener(type, listener) { - // Note: We can't just wrap `off` in `method` as we do it for other methods - // cause skipping a second or third argument will behave very differently - // than intended. This way we make sure all arguments are passed and only - // one listener is removed at most. - off(this, type, listener); - } -}); -exports.EventTarget = EventTarget; diff --git a/tools/addon-sdk-1.12/lib/sdk/frame/hidden-frame.js b/tools/addon-sdk-1.12/lib/sdk/frame/hidden-frame.js deleted file mode 100644 index 1bf51de..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/frame/hidden-frame.js +++ /dev/null @@ -1,181 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "experimental" -}; - -const {Cc, Ci} = require("chrome"); -const errors = require("../deprecated/errors"); -const apiUtils = require("../deprecated/api-utils"); -const timer = require("../timers"); - -const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - -let hostFrame, hostDocument, hiddenWindow, isHostFrameReady = false; - -if (!require("../system/xul-app").isOneOf(["Firefox", "Fennec", "Thunderbird"])) { - throw new Error([ - "The hidden-frame module currently supports only Firefox and Thunderbird. ", - "In the future, we would like it to support other applications, however. ", - "Please see https://bugzilla.mozilla.org/show_bug.cgi?id=546740 for more ", - "information." - ].join("")); -} - -let appShellService = Cc["@mozilla.org/appshell/appShellService;1"]. - getService(Ci.nsIAppShellService); -hiddenWindow = appShellService.hiddenDOMWindow; - -if (!hiddenWindow) { - throw new Error([ - "The hidden-frame module needs an app that supports a hidden window. ", - "We would like it to support other applications, however. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=546740 for more information." - ].join("")); -} - -// Check if we can use the hidden window itself to host our iframes. -// If it's not a suitable host, the hostFrame will be lazily created -// by the first HiddenFrame instance. -if (hiddenWindow.location.protocol == "chrome:" && - (hiddenWindow.document.contentType == "application/vnd.mozilla.xul+xml" || - hiddenWindow.document.contentType == "application/xhtml+xml")) { - hostFrame = hiddenWindow; - hostDocument = hiddenWindow.document; - isHostFrameReady = true; -} - -function setHostFrameReady() { - hostDocument = hostFrame.contentDocument; - hostFrame.removeEventListener("DOMContentLoaded", setHostFrameReady, false); - isHostFrameReady = true; -} - -// This cache is used to access friend properties between functions -// without exposing them on the public API. -let cache = []; - -exports.HiddenFrame = apiUtils.publicConstructor(HiddenFrame); - -function HiddenFrame(options) { - options = options || {}; - let self = this; - let validOptions = apiUtils.validateOptions(options, { - onReady: { - is: ["undefined", "function", "array"], - ok: function(v) { - if (apiUtils.getTypeOf(v) === "array") { - // make sure every item is a function - return v.every(function (item) typeof(item) === "function") - } - return true; - } - }, - onUnload: { - is: ["undefined", "function"] - } - }); - - for (let key in validOptions) { - let val = validOptions[key]; - if (typeof(val) != "undefined") - options[key] = val; - } - - require("../util/collection").addCollectionProperty(this, "onReady"); - if (options.onReady) - this.onReady.add(options.onReady); - if (options.onUnload) - this.onUnload = options.onUnload; - - if (!hostFrame) { - hostFrame = hiddenWindow.document.createElement("iframe"); - - // ugly ugly hack. This is the most lightweight chrome:// file I could find on the tree - // This hack should be removed by proper platform support on bug 565388 - hostFrame.setAttribute("src", "chrome://global/content/mozilla.xhtml"); - hostFrame.addEventListener("DOMContentLoaded", setHostFrameReady, false); - - hiddenWindow.document.body.appendChild(hostFrame); - } - - this.toString = function toString() "[object Frame]"; -} - -exports.add = function JP_SDK_Frame_add(frame) { - if (!(frame instanceof HiddenFrame)) - throw new Error("The object to be added must be a HiddenFrame."); - - // This instance was already added. - if (cache.filter(function (v) v.frame === frame)[0]) - return frame; - - function createElement() { - hostFrame.removeEventListener("DOMContentLoaded", createElement, false); - - let element = hostDocument.createElementNS(XUL_NS, "iframe"); - - element.setAttribute("type", "content"); - hostDocument.documentElement.appendChild(element); - - /* Public API: hiddenFrame.element */ - frame.__defineGetter__("element", function () element); - - // Notify consumers that the frame is ready. - function onReadyListener(event) { - element.removeEventListener("DOMContentLoaded", onReadyListener, false); - if (event.target == element.contentDocument) { - for (let handler in frame.onReady) - errors.catchAndLog(function () handler.call(frame))(); - } - } - element.addEventListener("DOMContentLoaded", onReadyListener, false); - - cache.push({ - frame: frame, - element: element, - unload: function unload() { - // Call before removing to let a chance to avoid "dead object" exception - if (typeof frame.onUnload === "function") - frame.onUnload(); - hostDocument.documentElement.removeChild(element); - } - }); - } - - /* Begin element construction or schedule it for later */ - if (isHostFrameReady) { - createElement(); - } else { - hostFrame.addEventListener("DOMContentLoaded", createElement, false); - } - - return frame; -} - -exports.remove = function remove(frame) { - if (!(frame instanceof HiddenFrame)) - throw new Error("The object to be removed must be a HiddenFrame."); - - let entry = cache.filter(function (v) v.frame === frame)[0]; - if (!entry) - return; - - // Remove from cache before calling in order to avoid loop - cache.splice(cache.indexOf(entry), 1); - entry.unload(); -} - -require("../system/unload").when(function () { - for each (let entry in cache.slice()) - exports.remove(entry.frame); - - if (hostFrame && hostFrame !== hiddenWindow) - hiddenWindow.document.body.removeChild(hostFrame); -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/frame/utils.js b/tools/addon-sdk-1.12/lib/sdk/frame/utils.js deleted file mode 100644 index ad4ca7c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/frame/utils.js +++ /dev/null @@ -1,63 +0,0 @@ -/* 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'; - -module.metadata = { - "stability": "experimental" -}; - -const XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; - -/** - * Creates a XUL `browser` element in a privileged document. - * @params {nsIDOMDocument} document - * @params {String} options.type - * By default is 'content' for possible values see: - * https://developer.mozilla.org/en/XUL/iframe#a-browser.type - * @params {String} options.uri - * URI of the document to be loaded into created frame. - * @params {Boolean} options.remote - * If `true` separate process will be used for this frame, also in such - * case all the following options are ignored. - * @params {Boolean} options.allowAuth - * Whether to allow auth dialogs. Defaults to `false`. - * @params {Boolean} options.allowJavascript - * Whether to allow Javascript execution. Defaults to `false`. - * @params {Boolean} options.allowPlugins - * Whether to allow plugin execution. Defaults to `false`. - */ -function create(document, options) { - options = options || {}; - let remote = 'remote' in options && options.remote === true; - - let frame = document.createElementNS(XUL, 'browser'); - // Type="content" is mandatory to enable stuff here: - // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1776 - frame.setAttribute('type', options.type || 'content'); - frame.setAttribute('src', options.uri || 'about:blank'); - - // Load in separate process if `options.remote` is `true`. - // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1347 - if (remote) { - // We remove XBL binding to avoid execution of code that is not going to - // work because browser has no docShell attribute in remote mode - // (for example) - frame.setAttribute('style', '-moz-binding: none;'); - frame.setAttribute('remote', 'true'); - } - - document.documentElement.appendChild(frame); - - // If browser is remote it won't have a `docShell`. - if (!remote) { - let docShell = frame.docShell; - docShell.allowAuth = options.allowAuth || false; - docShell.allowJavascript = options.allowJavascript || false; - docShell.allowPlugins = options.allowPlugins || false; - } - - return frame; -} -exports.create = create; diff --git a/tools/addon-sdk-1.12/lib/sdk/hotkeys.js b/tools/addon-sdk-1.12/lib/sdk/hotkeys.js deleted file mode 100644 index 3491d2c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/hotkeys.js +++ /dev/null @@ -1,41 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "stable" -}; - -const INVALID_HOTKEY = "Hotkey must have at least one modifier."; - -const { toJSON: jsonify, toString: stringify, - isFunctionKey } = require("./keyboard/utils"); -const { register, unregister } = require("./keyboard/hotkeys"); - -const Hotkey = exports.Hotkey = function Hotkey(options) { - if (!(this instanceof Hotkey)) - return new Hotkey(options); - - // Parsing key combination string. - let hotkey = jsonify(options.combo); - if (!isFunctionKey(hotkey.key) && !hotkey.modifiers.length) { - throw new TypeError(INVALID_HOTKEY); - } - - this.onPress = options.onPress; - this.toString = stringify.bind(null, hotkey); - // Registering listener on keyboard combination enclosed by this hotkey. - // Please note that `this.toString()` is a normalized version of - // `options.combination` where order of modifiers is sorted and `accel` is - // replaced with platform specific key. - register(this.toString(), this.onPress); - // We freeze instance before returning it in order to make it's properties - // read-only. - return Object.freeze(this); -}; -Hotkey.prototype.destroy = function destroy() { - unregister(this.toString(), this.onPress); -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/io/byte-streams.js b/tools/addon-sdk-1.12/lib/sdk/io/byte-streams.js deleted file mode 100644 index 27b1802..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/io/byte-streams.js +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim:set ts=2 sw=2 sts=2 et filetype=javascript - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -module.metadata = { - "stability": "experimental" -}; - -exports.ByteReader = ByteReader; -exports.ByteWriter = ByteWriter; - -const {Cc, Ci} = require("chrome"); - -// This just controls the maximum number of bytes we read in at one time. -const BUFFER_BYTE_LEN = 0x8000; - -function ByteReader(inputStream) { - const self = this; - - let stream = Cc["@mozilla.org/binaryinputstream;1"]. - createInstance(Ci.nsIBinaryInputStream); - stream.setInputStream(inputStream); - - let manager = new StreamManager(this, stream); - - this.read = function ByteReader_read(numBytes) { - manager.ensureOpened(); - if (typeof(numBytes) !== "number") - numBytes = Infinity; - - let data = ""; - let read = 0; - try { - while (true) { - let avail = stream.available(); - let toRead = Math.min(numBytes - read, avail, BUFFER_BYTE_LEN); - if (toRead <= 0) - break; - data += stream.readBytes(toRead); - read += toRead; - } - } - catch (err) { - throw new Error("Error reading from stream: " + err); - } - - return data; - }; -} - -function ByteWriter(outputStream) { - const self = this; - - let stream = Cc["@mozilla.org/binaryoutputstream;1"]. - createInstance(Ci.nsIBinaryOutputStream); - stream.setOutputStream(outputStream); - - let manager = new StreamManager(this, stream); - - this.write = function ByteWriter_write(str) { - manager.ensureOpened(); - try { - stream.writeBytes(str, str.length); - } - catch (err) { - throw new Error("Error writing to stream: " + err); - } - }; -} - - -// This manages the lifetime of stream, a ByteReader or ByteWriter. It defines -// closed and close() on stream and registers an unload listener that closes -// rawStream if it's still opened. It also provides ensureOpened(), which -// throws an exception if the stream is closed. -function StreamManager(stream, rawStream) { - const self = this; - this.rawStream = rawStream; - this.opened = true; - - stream.__defineGetter__("closed", function stream_closed() { - return !self.opened; - }); - - stream.close = function stream_close() { - self.ensureOpened(); - self.unload(); - }; - - require("../system/unload").ensure(this); -} - -StreamManager.prototype = { - ensureOpened: function StreamManager_ensureOpened() { - if (!this.opened) - throw new Error("The stream is closed and cannot be used."); - }, - unload: function StreamManager_unload() { - this.rawStream.close(); - this.opened = false; - } -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/io/data.js b/tools/addon-sdk-1.12/lib/sdk/io/data.js deleted file mode 100644 index 9dd7246..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/io/data.js +++ /dev/null @@ -1,84 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci, Cu } = require("chrome"); -const base64 = require("../base64"); - -const IOService = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - -const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm"); -const FaviconService = Cc["@mozilla.org/browser/favicon-service;1"]. - getService(Ci.nsIFaviconService); -const { deprecateFunction, deprecatedUsage } = require("../util/deprecate"); - -const PNG_B64 = "data:image/png;base64,"; -const DEF_FAVICON_URI = "chrome://mozapps/skin/places/defaultFavicon.png"; -let DEF_FAVICON = null; - -/** - * Takes URI of the page and returns associated favicon URI. - * If page under passed uri has no favicon then base64 encoded data URI of - * default faveicon is returned. - * @param {String} uri - * @returns {String} - */ -exports.getFaviconURIForLocation = function getFaviconURIForLocation(uri) { - let pageURI = NetUtil.newURI(uri); - try { - return FaviconService.getFaviconDataAsDataURL( - FaviconService.getFaviconForPage(pageURI)); - } - catch(e) { - if (!DEF_FAVICON) { - DEF_FAVICON = PNG_B64 + - base64.encode(getChromeURIContent(DEF_FAVICON_URI)); - } - return DEF_FAVICON; - } -} - -/** - * Takes chrome URI and returns content under that URI. - * @param {String} chromeURI - * @returns {String} - */ -function getChromeURIContent(chromeURI) { - let channel = IOService.newChannel(chromeURI, null, null); - let input = channel.open(); - let stream = Cc["@mozilla.org/binaryinputstream;1"]. - createInstance(Ci.nsIBinaryInputStream); - stream.setInputStream(input); - let content = stream.readBytes(input.available()); - stream.close(); - input.close(); - return content; -} -exports.getChromeURIContent = function deprecated_getChromeURIContent() { - deprecatedUsage( - 'getChromeURIContent is deprecated, ' + - 'please use require("sdk/net/url").readURI instead.' - ); -}; - -/** - * Creates a base-64 encoded ASCII string from a string of binary data. - */ -exports.base64Encode = deprecateFunction(base64.encode, - 'base64Encode is deprecated, ' + - 'please use require("sdk/base64").encode instead.' -); -/** - * Decodes a string of data which has been encoded using base-64 encoding. - */ -exports.base64Decode = deprecateFunction(base64.decode, - 'base64Dencode is deprecated, ' + - 'please use require("sdk/base64").decode instead.' -); diff --git a/tools/addon-sdk-1.12/lib/sdk/io/file.js b/tools/addon-sdk-1.12/lib/sdk/io/file.js deleted file mode 100644 index 735edea..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/io/file.js +++ /dev/null @@ -1,196 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "experimental" -}; - -const {Cc,Ci,Cr} = require("chrome"); -const byteStreams = require("./byte-streams"); -const textStreams = require("./text-streams"); - -// Flags passed when opening a file. See nsprpub/pr/include/prio.h. -const OPEN_FLAGS = { - RDONLY: parseInt("0x01"), - WRONLY: parseInt("0x02"), - CREATE_FILE: parseInt("0x08"), - APPEND: parseInt("0x10"), - TRUNCATE: parseInt("0x20"), - EXCL: parseInt("0x80") -}; - -var dirsvc = Cc["@mozilla.org/file/directory_service;1"] - .getService(Ci.nsIProperties); - -function MozFile(path) { - var file = Cc['@mozilla.org/file/local;1'] - .createInstance(Ci.nsILocalFile); - file.initWithPath(path); - return file; -} - -function ensureReadable(file) { - if (!file.isReadable()) - throw new Error("path is not readable: " + file.path); -} - -function ensureDir(file) { - ensureExists(file); - if (!file.isDirectory()) - throw new Error("path is not a directory: " + file.path); -} - -function ensureFile(file) { - ensureExists(file); - if (!file.isFile()) - throw new Error("path is not a file: " + file.path); -} - -function ensureExists(file) { - if (!file.exists()) - throw friendlyError(Cr.NS_ERROR_FILE_NOT_FOUND, file.path); -} - -function friendlyError(errOrResult, filename) { - var isResult = typeof(errOrResult) === "number"; - var result = isResult ? errOrResult : errOrResult.result; - switch (result) { - case Cr.NS_ERROR_FILE_NOT_FOUND: - return new Error("path does not exist: " + filename); - } - return isResult ? new Error("XPCOM error code: " + errOrResult) : errOrResult; -} - -exports.exists = function exists(filename) { - return MozFile(filename).exists(); -}; - -exports.isFile = function isFile(filename) { - return MozFile(filename).isFile(); -}; - -exports.read = function read(filename, mode) { - if (typeof(mode) !== "string") - mode = ""; - - // Ensure mode is read-only. - mode = /b/.test(mode) ? "b" : ""; - - var stream = exports.open(filename, mode); - try { - var str = stream.read(); - } - finally { - stream.close(); - } - - return str; -}; - -exports.join = function join(base) { - if (arguments.length < 2) - throw new Error("need at least 2 args"); - base = MozFile(base); - for (var i = 1; i < arguments.length; i++) - base.append(arguments[i]); - return base.path; -}; - -exports.dirname = function dirname(path) { - var parent = MozFile(path).parent; - return parent ? parent.path : ""; -}; - -exports.basename = function basename(path) { - var leafName = MozFile(path).leafName; - - // On Windows, leafName when the path is a volume letter and colon ("c:") is - // the path itself. But such a path has no basename, so we want the empty - // string. - return leafName == path ? "" : leafName; -}; - -exports.list = function list(path) { - var file = MozFile(path); - ensureDir(file); - ensureReadable(file); - - var entries = file.directoryEntries; - var entryNames = []; - while(entries.hasMoreElements()) { - var entry = entries.getNext(); - entry.QueryInterface(Ci.nsIFile); - entryNames.push(entry.leafName); - } - return entryNames; -}; - -exports.open = function open(filename, mode) { - var file = MozFile(filename); - if (typeof(mode) !== "string") - mode = ""; - - // File opened for write only. - if (/w/.test(mode)) { - if (file.exists()) - ensureFile(file); - var stream = Cc['@mozilla.org/network/file-output-stream;1']. - createInstance(Ci.nsIFileOutputStream); - var openFlags = OPEN_FLAGS.WRONLY | - OPEN_FLAGS.CREATE_FILE | - OPEN_FLAGS.TRUNCATE; - var permFlags = parseInt("0644"); // u+rw go+r - try { - stream.init(file, openFlags, permFlags, 0); - } - catch (err) { - throw friendlyError(err, filename); - } - return /b/.test(mode) ? - new byteStreams.ByteWriter(stream) : - new textStreams.TextWriter(stream); - } - - // File opened for read only, the default. - ensureFile(file); - stream = Cc['@mozilla.org/network/file-input-stream;1']. - createInstance(Ci.nsIFileInputStream); - try { - stream.init(file, OPEN_FLAGS.RDONLY, 0, 0); - } - catch (err) { - throw friendlyError(err, filename); - } - return /b/.test(mode) ? - new byteStreams.ByteReader(stream) : - new textStreams.TextReader(stream); -}; - -exports.remove = function remove(path) { - var file = MozFile(path); - ensureFile(file); - file.remove(false); -}; - -exports.mkpath = function mkpath(path) { - var file = MozFile(path); - if (!file.exists()) - file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755")); // u+rwx go+rx - else if (!file.isDirectory()) - throw new Error("The path already exists and is not a directory: " + path); -}; - -exports.rmdir = function rmdir(path) { - var file = MozFile(path); - ensureDir(file); - try { - file.remove(false); - } - catch (err) { - // Bug 566950 explains why we're not catching a specific exception here. - throw new Error("The directory is not empty: " + path); - } -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/io/text-streams.js b/tools/addon-sdk-1.12/lib/sdk/io/text-streams.js deleted file mode 100644 index 0aa469d..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/io/text-streams.js +++ /dev/null @@ -1,244 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim:set ts=2 sw=2 sts=2 et filetype=javascript - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -module.metadata = { - "stability": "experimental" -}; - -const {Cc,Ci,Cu,components} = require("chrome"); -var NetUtil = {}; -Cu.import("resource://gre/modules/NetUtil.jsm", NetUtil); -NetUtil = NetUtil.NetUtil; - -// NetUtil.asyncCopy() uses this buffer length, and since we call it, for best -// performance we use it, too. -const BUFFER_BYTE_LEN = 0x8000; -const PR_UINT32_MAX = 0xffffffff; -const DEFAULT_CHARSET = "UTF-8"; - -exports.TextReader = TextReader; -exports.TextWriter = TextWriter; - -/** - * An input stream that reads text from a backing stream using a given text - * encoding. - * - * @param inputStream - * The stream is backed by this nsIInputStream. It must already be - * opened. - * @param charset - * Text in inputStream is expected to be in this character encoding. If - * not given, "UTF-8" is assumed. See nsICharsetConverterManager.idl for - * documentation on how to determine other valid values for this. - */ -function TextReader(inputStream, charset) { - const self = this; - charset = checkCharset(charset); - - let stream = Cc["@mozilla.org/intl/converter-input-stream;1"]. - createInstance(Ci.nsIConverterInputStream); - stream.init(inputStream, charset, BUFFER_BYTE_LEN, - Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); - - let manager = new StreamManager(this, stream); - - /** - * Reads a string from the stream. If the stream is closed, an exception is - * thrown. - * - * @param numChars - * The number of characters to read. If not given, the remainder of - * the stream is read. - * @return The string read. If the stream is already at EOS, returns the - * empty string. - */ - this.read = function TextReader_read(numChars) { - manager.ensureOpened(); - - let readAll = false; - if (typeof(numChars) === "number") - numChars = Math.max(numChars, 0); - else - readAll = true; - - let str = ""; - let totalRead = 0; - let chunkRead = 1; - - // Read in numChars or until EOS, whichever comes first. Note that the - // units here are characters, not bytes. - while (true) { - let chunk = {}; - let toRead = readAll ? - PR_UINT32_MAX : - Math.min(numChars - totalRead, PR_UINT32_MAX); - if (toRead <= 0 || chunkRead <= 0) - break; - - // The converter stream reads in at most BUFFER_BYTE_LEN bytes in a call - // to readString, enough to fill its byte buffer. chunkRead will be the - // number of characters encoded by the bytes in that buffer. - chunkRead = stream.readString(toRead, chunk); - str += chunk.value; - totalRead += chunkRead; - } - - return str; - }; -} - -/** - * A buffered output stream that writes text to a backing stream using a given - * text encoding. - * - * @param outputStream - * The stream is backed by this nsIOutputStream. It must already be - * opened. - * @param charset - * Text will be written to outputStream using this character encoding. - * If not given, "UTF-8" is assumed. See nsICharsetConverterManager.idl - * for documentation on how to determine other valid values for this. - */ -function TextWriter(outputStream, charset) { - const self = this; - charset = checkCharset(charset); - - let stream = outputStream; - - // Buffer outputStream if it's not already. - let ioUtils = Cc["@mozilla.org/io-util;1"].getService(Ci.nsIIOUtil); - if (!ioUtils.outputStreamIsBuffered(outputStream)) { - stream = Cc["@mozilla.org/network/buffered-output-stream;1"]. - createInstance(Ci.nsIBufferedOutputStream); - stream.init(outputStream, BUFFER_BYTE_LEN); - } - - // I'd like to use nsIConverterOutputStream. But NetUtil.asyncCopy(), which - // we use below in writeAsync(), naturally expects its sink to be an instance - // of nsIOutputStream, which nsIConverterOutputStream's only implementation is - // not. So we use uconv and manually convert all strings before writing to - // outputStream. - let uconv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. - createInstance(Ci.nsIScriptableUnicodeConverter); - uconv.charset = charset; - - let manager = new StreamManager(this, stream); - - /** - * Flushes the backing stream's buffer. - */ - this.flush = function TextWriter_flush() { - manager.ensureOpened(); - stream.flush(); - }; - - /** - * Writes a string to the stream. If the stream is closed, an exception is - * thrown. - * - * @param str - * The string to write. - */ - this.write = function TextWriter_write(str) { - manager.ensureOpened(); - let istream = uconv.convertToInputStream(str); - let len = istream.available(); - while (len > 0) { - stream.writeFrom(istream, len); - len = istream.available(); - } - istream.close(); - }; - - /** - * Writes a string on a background thread. After the write completes, the - * backing stream's buffer is flushed, and both the stream and the backing - * stream are closed, also on the background thread. If the stream is already - * closed, an exception is thrown immediately. - * - * @param str - * The string to write. - * @param callback - * An optional function. If given, it's called as callback(error) when - * the write completes. error is an Error object or undefined if there - * was no error. Inside callback, |this| is the stream object. - */ - this.writeAsync = function TextWriter_writeAsync(str, callback) { - manager.ensureOpened(); - let istream = uconv.convertToInputStream(str); - NetUtil.asyncCopy(istream, stream, function (result) { - let err = components.isSuccessCode(result) ? undefined : - new Error("An error occured while writing to the stream: " + result); - if (err) - console.error(err); - - // asyncCopy() closes its output (and input) stream. - manager.opened = false; - - if (typeof(callback) === "function") { - try { - callback.call(self, err); - } - catch (exc) { - console.exception(exc); - } - } - }); - }; -} - -// This manages the lifetime of stream, a TextReader or TextWriter. It defines -// closed and close() on stream and registers an unload listener that closes -// rawStream if it's still opened. It also provides ensureOpened(), which -// throws an exception if the stream is closed. -function StreamManager(stream, rawStream) { - const self = this; - this.rawStream = rawStream; - this.opened = true; - - /** - * True iff the stream is closed. - */ - stream.__defineGetter__("closed", function stream_closed() { - return !self.opened; - }); - - /** - * Closes both the stream and its backing stream. If the stream is already - * closed, an exception is thrown. For TextWriters, this first flushes the - * backing stream's buffer. - */ - stream.close = function stream_close() { - self.ensureOpened(); - self.unload(); - }; - - require("../system/unload").ensure(this); -} - -StreamManager.prototype = { - ensureOpened: function StreamManager_ensureOpened() { - if (!this.opened) - throw new Error("The stream is closed and cannot be used."); - }, - unload: function StreamManager_unload() { - // TextWriter.writeAsync() causes rawStream to close and therefore sets - // opened to false, so check that we're still opened. - if (this.opened) { - // Calling close() on both an nsIUnicharInputStream and - // nsIBufferedOutputStream closes their backing streams. It also forces - // nsIOutputStreams to flush first. - this.rawStream.close(); - this.opened = false; - } - } -}; - -function checkCharset(charset) { - return typeof(charset) === "string" ? charset : DEFAULT_CHARSET; -} diff --git a/tools/addon-sdk-1.12/lib/sdk/keyboard/hotkeys.js b/tools/addon-sdk-1.12/lib/sdk/keyboard/hotkeys.js deleted file mode 100644 index 2d963ab..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/keyboard/hotkeys.js +++ /dev/null @@ -1,111 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { observer: keyboardObserver } = require("./observer"); -const { getKeyForCode, normalize, isFunctionKey, - MODIFIERS } = require("./utils"); - -/** - * Register a global `hotkey` that executes `listener` when the key combination - * in `hotkey` is pressed. If more then one `listener` is registered on the same - * key combination only last one will be executed. - * - * @param {string} hotkey - * Key combination in the format of 'modifier key'. - * - * Examples: - * - * "accel s" - * "meta shift i" - * "control alt d" - * - * Modifier keynames: - * - * - **shift**: The Shift key. - * - **alt**: The Alt key. On the Macintosh, this is the Option key. On - * Macintosh this can only be used in conjunction with another modifier, - * since `Alt+Letter` combinations are reserved for entering special - * characters in text. - * - **meta**: The Meta key. On the Macintosh, this is the Command key. - * - **control**: The Control key. - * - **accel**: The key used for keyboard shortcuts on the user's platform, - * which is Control on Windows and Linux, and Command on Mac. Usually, this - * would be the value you would use. - * - * @param {function} listener - * Function to execute when the `hotkey` is executed. - */ -exports.register = function register(hotkey, listener) { - hotkey = normalize(hotkey); - hotkeys[hotkey] = listener; -}; - -/** - * Unregister a global `hotkey`. If passed `listener` is not the one registered - * for the given `hotkey`, the call to this function will be ignored. - * - * @param {string} hotkey - * Key combination in the format of 'modifier key'. - * @param {function} listener - * Function that will be invoked when the `hotkey` is pressed. - */ -exports.unregister = function unregister(hotkey, listener) { - hotkey = normalize(hotkey); - if (hotkeys[hotkey] === listener) - delete hotkeys[hotkey]; -}; - -/** - * Map of hotkeys and associated functions. - */ -const hotkeys = exports.hotkeys = {}; - -keyboardObserver.on("keydown", function onKeypress(event, window) { - let key, modifiers = []; - let isChar = "isChar" in event && event.isChar; - let which = "which" in event ? event.which : null; - let keyCode = "keyCode" in event ? event.keyCode : null; - - if ("shiftKey" in event && event.shiftKey) - modifiers.push("shift"); - if ("altKey" in event && event.altKey) - modifiers.push("alt"); - if ("ctrlKey" in event && event.ctrlKey) - modifiers.push("control"); - if ("metaKey" in event && event.metaKey) - modifiers.push("meta"); - - // If it's not a printable character then we fall back to a human readable - // equivalent of one of the following constants. - // http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/events/nsIDOMKeyEvent.idl - key = getKeyForCode(keyCode); - - // If only non-function (f1 - f24) key or only modifiers are pressed we don't - // have a valid combination so we return immediately (Also, sometimes - // `keyCode` may be one for the modifier which means we do not have a - // modifier). - if (!key || (!isFunctionKey(key) && !modifiers.length) || key in MODIFIERS) - return; - - let combination = normalize({ key: key, modifiers: modifiers }); - let hotkey = hotkeys[combination]; - - if (hotkey) { - try { - hotkey(); - } catch (exception) { - console.exception(exception); - } finally { - // Work around bug 582052 by preventing the (nonexistent) default action. - event.preventDefault(); - } - } -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/keyboard/observer.js b/tools/addon-sdk-1.12/lib/sdk/keyboard/observer.js deleted file mode 100644 index 26c0a83..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/keyboard/observer.js +++ /dev/null @@ -1,58 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Trait } = require("../deprecated/light-traits"); -const { EventEmitterTrait: EventEmitter } = require("../deprecated/events"); -const { DOMEventAssembler } = require("../deprecated/events/assembler"); -const { browserWindowIterator } = require('../deprecated/window-utils'); -const { isBrowser } = require('../window/utils'); -const { observer: windowObserver } = require("../windows/observer"); - -// Event emitter objects used to register listeners and emit events on them -// when they occur. -const observer = Trait.compose(DOMEventAssembler, EventEmitter).create({ - /** - * Method is implemented by `EventEmitter` and is used just for emitting - * events on registered listeners. - */ - _emit: Trait.required, - /** - * Events that are supported and emitted by the module. - */ - supportedEventsTypes: [ "keydown", "keyup", "keypress" ], - /** - * Function handles all the supported events on all the windows that are - * observed. Method is used to proxy events to the listeners registered on - * this event emitter. - * @param {Event} event - * Keyboard event being emitted. - */ - handleEvent: function handleEvent(event) { - this._emit(event.type, event, event.target.ownerDocument.defaultView); - } -}); - -// Adding each opened window to a list of observed windows. -windowObserver.on("open", function onOpen(window) { - if (isBrowser(window)) - observer.observe(window); -}); -// Removing each closed window form the list of observed windows. -windowObserver.on("close", function onClose(window) { - if (isBrowser(window)) - observer.ignore(window); -}); - -// Making observer aware of already opened windows. -for each (let window in browserWindowIterator()) - observer.observe(window); - -exports.observer = observer; diff --git a/tools/addon-sdk-1.12/lib/sdk/keyboard/utils.js b/tools/addon-sdk-1.12/lib/sdk/keyboard/utils.js deleted file mode 100644 index a4a8f76..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/keyboard/utils.js +++ /dev/null @@ -1,190 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci } = require("chrome"); -const runtime = require("../system/runtime"); -const { isString } = require("../lang/type"); -const array = require("../util/array"); - - -const SWP = "{{SEPARATOR}}"; -const SEPARATOR = "-" -const INVALID_COMBINATION = "Hotkey key combination must contain one or more " + - "modifiers and only one key"; - -// Map of modifier key mappings. -const MODIFIERS = exports.MODIFIERS = { - 'accel': runtime.OS === "Darwin" ? 'meta' : 'control', - 'meta': 'meta', - 'control': 'control', - 'ctrl': 'control', - 'option': 'alt', - 'command': 'meta', - 'alt': 'alt', - 'shift': 'shift' -}; - -// Hash of key:code pairs for all the chars supported by `nsIDOMKeyEvent`. -// This is just a copy of the `nsIDOMKeyEvent` hash with normalized names. -// @See: http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/events/nsIDOMKeyEvent.idl -const CODES = exports.CODES = new function Codes() { - let nsIDOMKeyEvent = Ci.nsIDOMKeyEvent; - // Names that will be substituted with a shorter analogs. - let aliases = { - 'subtract': '-', - 'add': '+', - 'equals': '=', - 'slash': '/', - 'backslash': '\\', - 'openbracket': '[', - 'closebracket': ']', - 'quote': '\'', - 'backquote': '`', - 'period': '.', - 'semicolon': ';', - 'comma': ',' - }; - - // Normalizing keys and copying values to `this` object. - Object.keys(nsIDOMKeyEvent).filter(function(key) { - // Filter out only key codes. - return key.indexOf('DOM_VK') === 0; - }).map(function(key) { - // Map to key:values - return [ key, nsIDOMKeyEvent[key] ]; - }).map(function([key, value]) { - return [ key.replace('DOM_VK_', '').replace('_', '').toLowerCase(), value ]; - }).forEach(function ([ key, value ]) { - this[aliases[key] || key] = value; - }, this); -}; - -// Inverted `CODES` hash of `code:key`. -const KEYS = exports.KEYS = new function Keys() { - Object.keys(CODES).forEach(function(key) { - this[CODES[key]] = key; - }, this) -} - -exports.getKeyForCode = function getKeyForCode(code) { - return (code in KEYS) && KEYS[code]; -}; -exports.getCodeForKey = function getCodeForKey(key) { - return (key in CODES) && CODES[key]; -}; - -/** - * Utility function that takes string or JSON that defines a `hotkey` and - * returns normalized string version of it. - * @param {JSON|String} hotkey - * @param {String} [separator=" "] - * Optional string that represents separator used to concatenate keys in the - * given `hotkey`. - * @returns {String} - * @examples - * - * require("keyboard/hotkeys").normalize("b Shift accel"); - * // 'control shift b' -> on windows & linux - * // 'meta shift b' -> on mac - * require("keyboard/hotkeys").normalize("alt-d-shift", "-"); - * // 'alt shift d' - */ -var normalize = exports.normalize = function normalize(hotkey, separator) { - if (!isString(hotkey)) - hotkey = toString(hotkey, separator); - return toString(toJSON(hotkey, separator), separator); -}; - -/* - * Utility function that splits a string of characters that defines a `hotkey` - * into modifier keys and the defining key. - * @param {String} hotkey - * @param {String} [separator=" "] - * Optional string that represents separator used to concatenate keys in the - * given `hotkey`. - * @returns {JSON} - * @examples - * - * require("keyboard/hotkeys").toJSON("accel shift b"); - * // { key: 'b', modifiers: [ 'control', 'shift' ] } -> on windows & linux - * // { key: 'b', modifiers: [ 'meta', 'shift' ] } -> on mac - * - * require("keyboard/hotkeys").normalize("alt-d-shift", "-"); - * // { key: 'd', modifiers: [ 'alt', 'shift' ] } - */ -var toJSON = exports.toJSON = function toJSON(hotkey, separator) { - separator = separator || SEPARATOR; - // Since default separator is `-`, combination may take form of `alt--`. To - // avoid misbehavior we replace `--` with `-{{SEPARATOR}}` where - // `{{SEPARATOR}}` can be swapped later. - hotkey = hotkey.toLowerCase().replace(separator + separator, separator + SWP); - - let value = {}; - let modifiers = []; - let keys = hotkey.split(separator); - keys.forEach(function(name) { - // If name is `SEPARATOR` than we swap it back. - if (name === SWP) - name = separator; - if (name in MODIFIERS) { - array.add(modifiers, MODIFIERS[name]); - } else { - if (!value.key) - value.key = name; - else - throw new TypeError(INVALID_COMBINATION); - } - }); - - if (!value.key) - throw new TypeError(INVALID_COMBINATION); - - value.modifiers = modifiers.sort(); - return value; -}; - -/** - * Utility function that takes object that defines a `hotkey` and returns - * string representation of it. - * - * _Please note that this function does not validates data neither it normalizes - * it, if you are unsure that data is well formed use `normalize` function - * instead. - * - * @param {JSON} hotkey - * @param {String} [separator=" "] - * Optional string that represents separator used to concatenate keys in the - * given `hotkey`. - * @returns {String} - * @examples - * - * require("keyboard/hotkeys").toString({ - * key: 'b', - * modifiers: [ 'control', 'shift' ] - * }, '+'); - * // 'control+shift+b - * - */ -var toString = exports.toString = function toString(hotkey, separator) { - let keys = hotkey.modifiers.slice(); - keys.push(hotkey.key); - return keys.join(separator || SEPARATOR); -}; - -/** - * Utility function takes `key` name and returns `true` if it's function key - * (F1, ..., F24) and `false` if it's not. - */ -var isFunctionKey = exports.isFunctionKey = function isFunctionKey(key) { - var $ - return key[0].toLowerCase() === 'f' && - ($ = parseInt(key.substr(1)), 0 < $ && $ < 25); -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n.js b/tools/addon-sdk-1.12/lib/sdk/l10n.js deleted file mode 100644 index 40f219c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/l10n.js +++ /dev/null @@ -1,83 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "stable" -}; - -const core = require("./l10n/core"); -const { getRulesForLocale } = require("./l10n/plural-rules"); - -// Retrieve the plural mapping function -let pluralMappingFunction = getRulesForLocale(core.language()) || - getRulesForLocale("en"); - -exports.get = function get(k) { - // For now, we only accept a "string" as first argument - // TODO: handle plural forms in gettext pattern - if (typeof k !== "string") - throw new Error("First argument of localization method should be a string"); - - // Get translation from big hashmap or default to hard coded string: - let localized = core.get(k) || k; - - // # Simplest usecase: - // // String hard coded in source code: - // _("Hello world") - // // Identifier of a key stored in properties file - // _("helloString") - if (arguments.length <= 1) - return localized; - - let args = arguments; - - if (typeof localized == "object" && "other" in localized) { - // # Plural form: - // // Strings hard coded in source code: - // _(["One download", "%d downloads"], 10); - // // Identifier of a key stored in properties file - // _("downloadNumber", 0); - let n = arguments[1]; - - // First handle simple universal forms that may not be mandatory - // for each language, (i.e. not different than 'other' form, - // but still usefull for better phrasing) - // For example 0 in english is the same form than 'other' - // but we accept 'zero' form if specified in localization file - if (n === 0 && "zero" in localized) - localized = localized["zero"]; - else if (n === 1 && "one" in localized) - localized = localized["one"]; - else if (n === 2 && "two" in localized) - localized = localized["two"]; - else { - let pluralForm = pluralMappingFunction(n); - if (pluralForm in localized) - localized = localized[pluralForm]; - else // Fallback in case of error: missing plural form - localized = localized["other"]; - } - - // Simulate a string with one placeholder: - args = [null, n]; - } - - // # String with placeholders: - // // Strings hard coded in source code: - // _("Hello %s", username) - // // Identifier of a key stored in properties file - // _("helloString", username) - // * We supports `%1s`, `%2s`, ... pattern in order to change arguments order - // in translation. - // * In case of plural form, we has `%d` instead of `%s`. - let offset = 1; - localized = localized.replace(/%(\d*)(s|d)/g, function (v, n) { - let rv = args[n != "" ? n : offset]; - offset++; - return rv; - }); - - return localized; -} diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/core.js b/tools/addon-sdk-1.12/lib/sdk/l10n/core.js deleted file mode 100644 index 50472e9..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/l10n/core.js +++ /dev/null @@ -1,35 +0,0 @@ -/* 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/. */ - -// Following pseudo module is set by `api-utils/addon/runner` and its load -// method needs to be called before loading `core` module. But it may have -// failed, so that this pseudo won't be available - -module.metadata = { - "stability": "unstable" -}; - - -let hash = {}, bestMatchingLocale = null; -try { - let data = require("@l10n/data"); - hash = data.hash; - bestMatchingLocale = data.bestMatchingLocale; -} -catch(e) {} - -// Returns the translation for a given key, if available. -exports.get = function get(k) { - return k in hash ? hash[k] : null; -} - -// Returns the full length locale code: ja-JP-mac, en-US or fr -exports.locale = function locale() { - return bestMatchingLocale; -} -// Returns the short locale code: ja, en, fr -exports.language = function language() { - return bestMatchingLocale ? bestMatchingLocale.split("-")[0].toLowerCase() - : null; -} diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/html.js b/tools/addon-sdk-1.12/lib/sdk/l10n/html.js deleted file mode 100644 index 1a36415..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/l10n/html.js +++ /dev/null @@ -1,93 +0,0 @@ -/* 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/. */ - -module.metadata = { - "stability": "unstable" -}; - -const { Ci } = require("chrome"); -const events = require("../system/events"); -const core = require("./core"); - -const assetsURI = require('../self').data.url(); - -// Taken from Gaia: -// https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470 -function translateElement(element) { - element = element || document; - - // check all translatable children (= w/ a `data-l10n-id' attribute) - var children = element.querySelectorAll('*[data-l10n-id]'); - var elementCount = children.length; - for (var i = 0; i < elementCount; i++) { - var child = children[i]; - - // translate the child - var key = child.dataset.l10nId; - var data = core.get(key); - if (data) - child.textContent = data; - } -} -exports.translateElement = translateElement; - -function onDocumentReady2Translate(event) { - let document = event.target; - document.removeEventListener("DOMContentLoaded", onDocumentReady2Translate, - false); - - translateElement(document); - - // Finally display document when we finished replacing all text content - document.documentElement.style.visibility = "visible"; -} - -function onContentWindow(event) { - let document = event.subject; - - // Accept only HTML documents - if (!(document instanceof Ci.nsIDOMHTMLDocument)) - return; - - // Bug 769483: data:URI documents instanciated with nsIDOMParser - // have a null `location` attribute at this time - if (!document.location) - return; - - // Accept only document from this addon - if (document.location.href.indexOf(assetsURI) !== 0) - return; - - // First hide content of the document in order to have content blinking - // between untranslated and translated states - // TODO: use result of bug 737003 discussion in order to avoid any conflict - // with document CSS - document.documentElement.style.visibility = "hidden"; - - // Wait for DOM tree to be built before applying localization - document.addEventListener("DOMContentLoaded", onDocumentReady2Translate, - false); -} - -// Listen to creation of content documents in order to translate them as soon -// as possible in their loading process -const ON_CONTENT = "document-element-inserted"; -let enabled = false; -function enable() { - if (!enabled) { - events.on(ON_CONTENT, onContentWindow); - enabled = true; - } -} -exports.enable = enable; - -function disable() { - if (enabled) { - events.off(ON_CONTENT, onContentWindow); - enabled = false; - } -} -exports.disable = disable; - -require("api-utils/unload").when(disable); diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/loader.js b/tools/addon-sdk-1.12/lib/sdk/l10n/loader.js deleted file mode 100644 index 9af6c5c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/l10n/loader.js +++ /dev/null @@ -1,71 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci } = require("chrome"); -const { getPreferedLocales, findClosestLocale } = require("./locale"); -const { readURI } = require("../net/url"); -const { resolve } = require("../core/promise"); - -function parseJsonURI(uri) { - return readURI(uri). - then(JSON.parse). - then(null, function (error) { - throw Error("Failed to parse locale file:\n" + uri + "\n" + error); - }); -} - -// Returns the array stored in `locales.json` manifest that list available -// locales files -function getAvailableLocales(rootURI) { - let uri = rootURI + "locales.json"; - return parseJsonURI(uri).then(function (manifest) { - return "locales" in manifest && - Array.isArray(manifest.locales) ? - manifest.locales : []; - }); -} - -// Returns URI of the best locales file to use from the XPI -function getBestLocale(rootURI) { - // Read localization manifest file that contains list of available languages - return getAvailableLocales(rootURI).then(function (availableLocales) { - // Retrieve list of prefered locales to use - let preferedLocales = getPreferedLocales(); - - // Compute the most preferable locale to use by using these two lists - return findClosestLocale(availableLocales, preferedLocales); - }); -} - -/** - * Read localization files and returns a promise of data to put in `@l10n/data` - * pseudo module, in order to allow l10n/core to fetch it. - */ -exports.load = function load(rootURI) { - // First, search for a locale file: - return getBestLocale(rootURI).then(function (bestMatchingLocale) { - // It may be null if the addon doesn't have any locale file - if (!bestMatchingLocale) - return resolve(null); - - let localeURI = rootURI + "locale/" + bestMatchingLocale + ".json"; - - // Locale files only contains one big JSON object that is used as - // an hashtable of: "key to translate" => "translated key" - // TODO: We are likely to change this in order to be able to overload - // a specific key translation. For a specific package, module or line? - return parseJsonURI(localeURI).then(function (json) { - return { - hash: json, - bestMatchingLocale: bestMatchingLocale - }; - }); - }); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/locale.js b/tools/addon-sdk-1.12/lib/sdk/l10n/locale.js deleted file mode 100644 index 0e59a64..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/l10n/locale.js +++ /dev/null @@ -1,126 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const prefs = require("../preferences/service"); -const { Cu, Cc, Ci } = require("chrome"); -const { Services } = Cu.import("resource://gre/modules/Services.jsm"); - - -/** - * Gets the currently selected locale for display. - * Gets all usable locale that we can use sorted by priority of relevance - * @return Array of locales, begins with highest priority - */ -const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; -const PREF_SELECTED_LOCALE = "general.useragent.locale"; -const PREF_ACCEPT_LANGUAGES = "intl.accept_languages"; -exports.getPreferedLocales = function getPreferedLocales() { - let locales = []; - - function addLocale(locale) { - locale = locale.toLowerCase(); - if (locales.indexOf(locale) === -1) - locales.push(locale); - } - - // Most important locale is OS one. But we use it, only if - // "intl.locale.matchOS" pref is set to `true`. - // Currently only used for multi-locales mobile builds. - // http://mxr.mozilla.org/mozilla-central/source/mobile/android/installer/Makefile.in#46 - if (prefs.get(PREF_MATCH_OS_LOCALE, false)) { - let localeService = Cc["@mozilla.org/intl/nslocaleservice;1"]. - getService(Ci.nsILocaleService); - let osLocale = localeService.getLocaleComponentForUserAgent(); - addLocale(osLocale); - } - - // In some cases, mainly on Fennec and on Linux version, - // `general.useragent.locale` is a special 'localized' value, like: - // "chrome://global/locale/intl.properties" - let browserUiLocale = prefs.getLocalized(PREF_SELECTED_LOCALE, "") || - prefs.get(PREF_SELECTED_LOCALE, ""); - if (browserUiLocale) - addLocale(browserUiLocale); - - - // Third priority is the list of locales used for web content - let contentLocales = prefs.get(PREF_ACCEPT_LANGUAGES, ""); - if (contentLocales) { - // This list is a string of locales seperated by commas. - // There is spaces after commas, so strip each item - for each(let locale in contentLocales.split(",")) - addLocale(locale.replace(/(^\s+)|(\s+$)/g, "")); - } - - // Finally, we ensure that en-US is the final fallback if it wasn't added - addLocale("en-US"); - - return locales; -} - -/** - * Selects the closest matching locale from a list of locales. - * - * @param aLocales - * An array of available locales - * @param aMatchLocales - * An array of prefered locales, ordered by priority. Most wanted first. - * Locales have to be in lowercase. - * If null, uses getPreferedLocales() results - * @return the best match for the currently selected locale - * - * Stolen from http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/XPIProvider.jsm - */ -exports.findClosestLocale = function findClosestLocale(aLocales, aMatchLocales) { - - aMatchLocales = aMatchLocales || exports.getPreferedLocales(); - - // Holds the best matching localized resource - let bestmatch = null; - // The number of locale parts it matched with - let bestmatchcount = 0; - // The number of locale parts in the match - let bestpartcount = 0; - - for each (let locale in aMatchLocales) { - let lparts = locale.split("-"); - for each (let localized in aLocales) { - let found = localized.toLowerCase(); - // Exact match is returned immediately - if (locale == found) - return localized; - - let fparts = found.split("-"); - /* If we have found a possible match and this one isn't any longer - then we dont need to check further. */ - if (bestmatch && fparts.length < bestmatchcount) - continue; - - // Count the number of parts that match - let maxmatchcount = Math.min(fparts.length, lparts.length); - let matchcount = 0; - while (matchcount < maxmatchcount && - fparts[matchcount] == lparts[matchcount]) - matchcount++; - - /* If we matched more than the last best match or matched the same and - this locale is less specific than the last best match. */ - if (matchcount > bestmatchcount || - (matchcount == bestmatchcount && fparts.length < bestpartcount)) { - bestmatch = localized; - bestmatchcount = matchcount; - bestpartcount = fparts.length; - } - } - // If we found a valid match for this locale return it - if (bestmatch) - return bestmatch; - } - return null; -} diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/plural-rules.js b/tools/addon-sdk-1.12/lib/sdk/l10n/plural-rules.js deleted file mode 100644 index 22e5b8b..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/l10n/plural-rules.js +++ /dev/null @@ -1,403 +0,0 @@ -/* 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 file is automatically generated with /python-lib/plural-rules-generator.py -// Fetching data from: http://unicode.org/repos/cldr/trunk/common/supplemental/plurals.xml - -// Mapping of short locale name == to == > rule index in following list - -module.metadata = { - "stability": "unstable" -}; - -const LOCALES_TO_RULES = { - "af": 3, - "ak": 4, - "am": 4, - "ar": 1, - "asa": 3, - "az": 0, - "be": 11, - "bem": 3, - "bez": 3, - "bg": 3, - "bh": 4, - "bm": 0, - "bn": 3, - "bo": 0, - "br": 20, - "brx": 3, - "bs": 11, - "ca": 3, - "cgg": 3, - "chr": 3, - "cs": 12, - "cy": 17, - "da": 3, - "de": 3, - "dv": 3, - "dz": 0, - "ee": 3, - "el": 3, - "en": 3, - "eo": 3, - "es": 3, - "et": 3, - "eu": 3, - "fa": 0, - "ff": 5, - "fi": 3, - "fil": 4, - "fo": 3, - "fr": 5, - "fur": 3, - "fy": 3, - "ga": 8, - "gd": 24, - "gl": 3, - "gsw": 3, - "gu": 3, - "guw": 4, - "gv": 23, - "ha": 3, - "haw": 3, - "he": 2, - "hi": 4, - "hr": 11, - "hu": 0, - "id": 0, - "ig": 0, - "ii": 0, - "is": 3, - "it": 3, - "iu": 7, - "ja": 0, - "jmc": 3, - "jv": 0, - "ka": 0, - "kab": 5, - "kaj": 3, - "kcg": 3, - "kde": 0, - "kea": 0, - "kk": 3, - "kl": 3, - "km": 0, - "kn": 0, - "ko": 0, - "ksb": 3, - "ksh": 21, - "ku": 3, - "kw": 7, - "lag": 18, - "lb": 3, - "lg": 3, - "ln": 4, - "lo": 0, - "lt": 10, - "lv": 6, - "mas": 3, - "mg": 4, - "mk": 16, - "ml": 3, - "mn": 3, - "mo": 9, - "mr": 3, - "ms": 0, - "mt": 15, - "my": 0, - "nah": 3, - "naq": 7, - "nb": 3, - "nd": 3, - "ne": 3, - "nl": 3, - "nn": 3, - "no": 3, - "nr": 3, - "nso": 4, - "ny": 3, - "nyn": 3, - "om": 3, - "or": 3, - "pa": 3, - "pap": 3, - "pl": 13, - "ps": 3, - "pt": 3, - "rm": 3, - "ro": 9, - "rof": 3, - "ru": 11, - "rwk": 3, - "sah": 0, - "saq": 3, - "se": 7, - "seh": 3, - "ses": 0, - "sg": 0, - "sh": 11, - "shi": 19, - "sk": 12, - "sl": 14, - "sma": 7, - "smi": 7, - "smj": 7, - "smn": 7, - "sms": 7, - "sn": 3, - "so": 3, - "sq": 3, - "sr": 11, - "ss": 3, - "ssy": 3, - "st": 3, - "sv": 3, - "sw": 3, - "syr": 3, - "ta": 3, - "te": 3, - "teo": 3, - "th": 0, - "ti": 4, - "tig": 3, - "tk": 3, - "tl": 4, - "tn": 3, - "to": 0, - "tr": 0, - "ts": 3, - "tzm": 22, - "uk": 11, - "ur": 3, - "ve": 3, - "vi": 0, - "vun": 3, - "wa": 4, - "wae": 3, - "wo": 0, - "xh": 3, - "xog": 3, - "yo": 0, - "zh": 0, - "zu": 3 -}; - -// Utility functions for plural rules methods -function isIn(n, list) list.indexOf(n) !== -1; -function isBetween(n, start, end) start <= n && n <= end; - -// List of all plural rules methods, that maps an integer to the plural form name to use -const RULES = { - "0": function (n) { - - return "other" - }, - "1": function (n) { - if ((isBetween((n % 100), 3, 10))) - return "few"; - if (n == 0) - return "zero"; - if ((isBetween((n % 100), 11, 99))) - return "many"; - if (n == 2) - return "two"; - if (n == 1) - return "one"; - return "other" - }, - "2": function (n) { - if (n != 0 && (n % 10) == 0) - return "many"; - if (n == 2) - return "two"; - if (n == 1) - return "one"; - return "other" - }, - "3": function (n) { - if (n == 1) - return "one"; - return "other" - }, - "4": function (n) { - if ((isBetween(n, 0, 1))) - return "one"; - return "other" - }, - "5": function (n) { - if ((isBetween(n, 0, 2)) && n != 2) - return "one"; - return "other" - }, - "6": function (n) { - if (n == 0) - return "zero"; - if ((n % 10) == 1 && (n % 100) != 11) - return "one"; - return "other" - }, - "7": function (n) { - if (n == 2) - return "two"; - if (n == 1) - return "one"; - return "other" - }, - "8": function (n) { - if ((isBetween(n, 3, 6))) - return "few"; - if ((isBetween(n, 7, 10))) - return "many"; - if (n == 2) - return "two"; - if (n == 1) - return "one"; - return "other" - }, - "9": function (n) { - if (n == 0 || n != 1 && (isBetween((n % 100), 1, 19))) - return "few"; - if (n == 1) - return "one"; - return "other" - }, - "10": function (n) { - if ((isBetween((n % 10), 2, 9)) && !(isBetween((n % 100), 11, 19))) - return "few"; - if ((n % 10) == 1 && !(isBetween((n % 100), 11, 19))) - return "one"; - return "other" - }, - "11": function (n) { - if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) - return "few"; - if ((n % 10) == 0 || (isBetween((n % 10), 5, 9)) || (isBetween((n % 100), 11, 14))) - return "many"; - if ((n % 10) == 1 && (n % 100) != 11) - return "one"; - return "other" - }, - "12": function (n) { - if ((isBetween(n, 2, 4))) - return "few"; - if (n == 1) - return "one"; - return "other" - }, - "13": function (n) { - if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) - return "few"; - if (n != 1 && (isBetween((n % 10), 0, 1)) || (isBetween((n % 10), 5, 9)) || (isBetween((n % 100), 12, 14))) - return "many"; - if (n == 1) - return "one"; - return "other" - }, - "14": function (n) { - if ((isBetween((n % 100), 3, 4))) - return "few"; - if ((n % 100) == 2) - return "two"; - if ((n % 100) == 1) - return "one"; - return "other" - }, - "15": function (n) { - if (n == 0 || (isBetween((n % 100), 2, 10))) - return "few"; - if ((isBetween((n % 100), 11, 19))) - return "many"; - if (n == 1) - return "one"; - return "other" - }, - "16": function (n) { - if ((n % 10) == 1 && n != 11) - return "one"; - return "other" - }, - "17": function (n) { - if (n == 3) - return "few"; - if (n == 0) - return "zero"; - if (n == 6) - return "many"; - if (n == 2) - return "two"; - if (n == 1) - return "one"; - return "other" - }, - "18": function (n) { - if (n == 0) - return "zero"; - if ((isBetween(n, 0, 2)) && n != 0 && n != 2) - return "one"; - return "other" - }, - "19": function (n) { - if ((isBetween(n, 2, 10))) - return "few"; - if ((isBetween(n, 0, 1))) - return "one"; - return "other" - }, - "20": function (n) { - if ((isBetween((n % 10), 3, 4) || ((n % 10) == 9)) && !(isBetween((n % 100), 10, 19) || isBetween((n % 100), 70, 79) || isBetween((n % 100), 90, 99))) - return "few"; - if ((n % 1000000) == 0 && n != 0) - return "many"; - if ((n % 10) == 2 && !isIn((n % 100), [12, 72, 92])) - return "two"; - if ((n % 10) == 1 && !isIn((n % 100), [11, 71, 91])) - return "one"; - return "other" - }, - "21": function (n) { - if (n == 0) - return "zero"; - if (n == 1) - return "one"; - return "other" - }, - "22": function (n) { - if ((isBetween(n, 0, 1)) || (isBetween(n, 11, 99))) - return "one"; - return "other" - }, - "23": function (n) { - if ((isBetween((n % 10), 1, 2)) || (n % 20) == 0) - return "one"; - return "other" - }, - "24": function (n) { - if ((isBetween(n, 3, 10) || isBetween(n, 13, 19))) - return "few"; - if (isIn(n, [2, 12])) - return "two"; - if (isIn(n, [1, 11])) - return "one"; - return "other" - }, -}; - -/** - * Return a function that gives the plural form name for a given integer - * for the specified `locale` - * let fun = getRulesForLocale('en'); - * fun(1) -> 'one' - * fun(0) -> 'other' - * fun(1000) -> 'other' - */ -exports.getRulesForLocale = function getRulesForLocale(locale) { - let index = LOCALES_TO_RULES[locale]; - if (!(index in RULES)) { - console.warn('Plural form unknown for locale "' + locale + '"'); - return function () { return "other"; }; - } - return RULES[index]; -} - diff --git a/tools/addon-sdk-1.12/lib/sdk/l10n/prefs.js b/tools/addon-sdk-1.12/lib/sdk/l10n/prefs.js deleted file mode 100644 index 016077c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/l10n/prefs.js +++ /dev/null @@ -1,44 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const observers = require("../deprecated/observer-service"); -const core = require("./core"); -const { id: jetpackId} = require('../self'); - -const OPTIONS_DISPLAYED = "addon-options-displayed"; - -function onOptionsDisplayed(document, addonId) { - if (addonId !== jetpackId) - return; - let query = 'setting[data-jetpack-id="' + jetpackId + '"][pref-name], ' + - 'button[data-jetpack-id="' + jetpackId + '"][pref-name]'; - let nodes = document.querySelectorAll(query); - for (let node of nodes) { - let name = node.getAttribute("pref-name"); - if (node.tagName == "setting") { - let desc = core.get(name + "_description"); - if (desc) - node.setAttribute("desc", desc); - let title = core.get(name + "_title"); - if (title) - node.setAttribute("title", title); - - for (let item of node.querySelectorAll("menuitem, radio")) { - let key = name + "_options." + item.getAttribute("label"); - let label = core.get(key); - if (label) - item.setAttribute("label", label); - } - } - else if (node.tagName == "button") { - let label = core.get(name + "_label"); - if (label) - node.setAttribute("label", label); - } - } -} - -observers.add(OPTIONS_DISPLAYED, onOptionsDisplayed); diff --git a/tools/addon-sdk-1.12/lib/sdk/lang/functional.js b/tools/addon-sdk-1.12/lib/sdk/lang/functional.js deleted file mode 100644 index 3595a27..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/lang/functional.js +++ /dev/null @@ -1,163 +0,0 @@ -/* 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/. */ - -// Disclaimer: Most of the functions in this module implement APIs from -// Jeremy Ashkenas's http://underscorejs.org/ library and all credits for -// those goes to him. - -"use strict"; - -module.metadata = { - "stability": "unstable" -}; - -const { setTimeout } = require("../timers"); - -/** - * Takes `lambda` function and returns a method. When returned method is - * invoked it calls wrapped `lambda` and passes `this` as a first argument - * and given argument as rest. - */ -function method(lambda) { - return function method() { - return lambda.apply(null, [this].concat(Array.slice(arguments))); - } -} -exports.method = method; - -/** - * Takes a function and returns a wrapped one instead, calling which will call - * original function in the next turn of event loop. This is basically utility - * to do `setTimeout(function() { ... }, 0)`, with a difference that returned - * function is reused, instead of creating a new one each time. This also allows - * to use this functions as event listeners. - */ -function defer(f) { - return function deferred() - setTimeout(invoke, 0, f, arguments, this); -} -exports.defer = defer; -// Exporting `remit` alias as `defer` may conflict with promises. -exports.remit = defer; - -/** - * Invokes `callee` by passing `params` as an arguments and `self` as `this` - * pseudo-variable. Returns value that is returned by a callee. - * @param {Function} callee - * Function to invoke. - * @param {Array} params - * Arguments to invoke function with. - * @param {Object} self - * Object to be passed as a `this` pseudo variable. - */ -function invoke(callee, params, self) callee.apply(self, params); -exports.invoke = invoke; - -/** - * Curries a function with the arguments given. - * - * @param {Function} fn - * The function to curry - * - * @returns The function curried - */ -function curry(fn) { - if (typeof fn !== "function") - throw new TypeError(String(fn) + " is not a function"); - - let args = Array.slice(arguments, 1); - - return function() fn.apply(this, args.concat(Array.slice(arguments))); -} -exports.curry = curry; - -/** - * Returns the composition of a list of functions, where each function consumes - * the return value of the function that follows. In math terms, composing the - * functions `f()`, `g()`, and `h()` produces `f(g(h()))`. - * @example - * - * var greet = function(name) { return "hi: " + name; }; - * var exclaim = function(statement) { return statement + "!"; }; - * var welcome = compose(exclaim, greet); - * - * welcome('moe'); // => 'hi: moe!' - */ -function compose() { - let lambdas = Array.slice(arguments); - return function composed() { - let args = Array.slice(arguments), index = lambdas.length; - while (0 <= --index) - args = [ lambdas[index].apply(this, args) ]; - return args[0]; - }; -} -exports.compose = compose; - -/* - * Returns the first function passed as an argument to the second, - * allowing you to adjust arguments, run code before and after, and - * conditionally execute the original function. - * @example - * - * var hello = function(name) { return "hello: " + name; }; - * hello = wrap(hello, function(f) { - * return "before, " + f("moe") + ", after"; - * }); - * - * hello(); // => 'before, hello: moe, after' - */ -function wrap(f, wrapper) { - return function wrapped() - wrapper.apply(this, [ f ].concat(Array.slice(arguments))) -}; -exports.wrap = wrap; - -/** - * Returns the same value that is used as the argument. In math: f(x) = x - */ -function identity(value) value -exports.identity = identity; - -/** - * Memoizes a given function by caching the computed result. Useful for - * speeding up slow-running computations. If passed an optional hashFunction, - * it will be used to compute the hash key for storing the result, based on - * the arguments to the original function. The default hashFunction just uses - * the first argument to the memoized function as the key. - */ -function memoize(f, hasher) { - let memo = Object.create(null); - hasher = hasher || identity; - return function memoizer() { - let key = hasher.apply(this, arguments); - return key in memo ? memo[key] : (memo[key] = f.apply(this, arguments)); - }; -} -exports.memoize = memoize; - -/** - * Much like setTimeout, invokes function after wait milliseconds. If you pass - * the optional arguments, they will be forwarded on to the function when it is - * invoked. - */ -function delay(f, ms) { - let args = Array.slice(arguments, 2); - setTimeout(function(context) { return f.apply(context, args); }, ms, this); -}; -exports.delay = delay; - -/** - * Creates a version of the function that can only be called one time. Repeated - * calls to the modified function will have no effect, returning the value from - * the original call. Useful for initialization functions, instead of having to - * set a boolean flag and then check it later. - */ -function once(f) { - let ran = false, cache; - return function() ran ? cache : (ran = true, cache = f.apply(this, arguments)) -}; -exports.once = once; -// export cache as once will may be conflicting with event once a lot. -exports.cache = once; diff --git a/tools/addon-sdk-1.12/lib/sdk/lang/type.js b/tools/addon-sdk-1.12/lib/sdk/lang/type.js deleted file mode 100644 index a184edf..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/lang/type.js +++ /dev/null @@ -1,344 +0,0 @@ -/* vim:ts=2:sts=2:sw=2: - * 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"; - -module.metadata = { - "stability": "unstable" -}; - -/** - * Returns `true` if `value` is `undefined`. - * @examples - * var foo; isUndefined(foo); // true - * isUndefined(0); // false - */ -function isUndefined(value) { - return value === undefined; -} -exports.isUndefined = isUndefined; - -/** - * Returns `true` if value is `null`. - * @examples - * isNull(null); // true - * isNull(undefined); // false - */ -function isNull(value) { - return value === null; -} -exports.isNull = isNull; - -/** - * Returns `true` if value is a string. - * @examples - * isString("moe"); // true - */ -function isString(value) { - return typeof value === "string"; -} -exports.isString = isString; - -/** - * Returns `true` if `value` is a number. - * @examples - * isNumber(8.4 * 5); // true - */ -function isNumber(value) { - return typeof value === "number"; -} -exports.isNumber = isNumber; - -/** - * Returns `true` if `value` is a `RegExp`. - * @examples - * isRegExp(/moe/); // true - */ -function isRegExp(value) { - return isObject(value) && instanceOf(value, RegExp); -} -exports.isRegExp = isRegExp; - -/** - * Returns true if `value` is a `Date`. - * @examples - * isDate(new Date()); // true - */ -function isDate(value) { - return isObject(value) && instanceOf(value, Date); -} -exports.isDate = isDate; - -/** - * Returns true if object is a Function. - * @examples - * isFunction(function foo(){}) // true - */ -function isFunction(value) { - return typeof value === "function"; -} -exports.isFunction = isFunction; - -/** - * Returns `true` if `value` is an object (please note that `null` is considered - * to be an atom and not an object). - * @examples - * isObject({}) // true - * isObject(null) // false - */ -function isObject(value) { - return typeof value === "object" && value !== null; -} -exports.isObject = isObject; - -/** - * Returns true if `value` is an Array. - * @examples - * isArray([1, 2, 3]) // true - * isArray({ 0: 'foo', length: 1 }) // false - */ -var isArray = Array.isArray || function isArray(value) { - Object.prototype.toString.call(value) === "[object Array]"; -} -exports.isArray = isArray; - -/** - * Returns `true` if `value` is an Arguments object. - * @examples - * (function(){ return isArguments(arguments); })(1, 2, 3); // true - * isArguments([1,2,3]); // false - */ -function isArguments(value) { - Object.prototype.toString.call(value) === "[object Arguments]"; -} -exports.isArguments = isArguments; - -/** - * Returns true if it is a primitive `value`. (null, undefined, number, - * boolean, string) - * @examples - * isPrimitive(3) // true - * isPrimitive('foo') // true - * isPrimitive({ bar: 3 }) // false - */ -function isPrimitive(value) { - return !isFunction(value) && !isObject(value); -} -exports.isPrimitive = isPrimitive; - -/** - * Returns `true` if given `object` is flat (it is direct decedent of - * `Object.prototype` or `null`). - * @examples - * isFlat({}) // true - * isFlat(new Type()) // false - */ -function isFlat(object) { - return isObject(object) && (isNull(Object.getPrototypeOf(object)) || - isNull(Object.getPrototypeOf( - Object.getPrototypeOf(object)))); -} -exports.isFlat = isFlat; - -/** - * Returns `true` if object contains no values. - */ -function isEmpty(object) { - if (isObject(object)) { - for (var key in object) - return false; - return true; - } - return false; -} -exports.isEmpty = isEmpty; - -/** - * Returns `true` if `value` is an array / flat object containing only atomic - * values and other flat objects. - */ -function isJSON(value, visited) { - // Adding value to array of visited values. - (visited || (visited = [])).push(value); - // If `value` is an atom return `true` cause it's valid JSON. - return isPrimitive(value) || - // If `value` is an array of JSON values that has not been visited - // yet. - (isArray(value) && value.every(function(element) { - return isJSON(element, visited); - })) || - // If `value` is a plain object containing properties with a JSON - // values it's a valid JSON. - (isFlat(value) && Object.keys(value).every(function(key) { - var $ = Object.getOwnPropertyDescriptor(value, key); - // Check every proprety of a plain object to verify that - // it's neither getter nor setter, but a JSON value, that - // has not been visited yet. - return ((!isObject($.value) || !~visited.indexOf($.value)) && - !('get' in $) && !('set' in $) && - isJSON($.value, visited)); - })); -} -exports.isJSON = function (value) { - return isJSON(value); -}; - -/** - * Returns if `value` is an instance of a given `Type`. This is exactly same as - * `value instanceof Type` with a difference that `Type` can be from a scope - * that has a different top level object. (Like in case where `Type` is a - * function from different iframe / jetpack module / sandbox). - */ -function instanceOf(value, Type) { - var isConstructorNameSame; - var isConstructorSourceSame; - - // If `instanceof` returned `true` we know result right away. - var isInstanceOf = value instanceof Type; - - // If `instanceof` returned `false` we do ducktype check since `Type` may be - // from a different sandbox. If a constructor of the `value` or a constructor - // of the value's prototype has same name and source we assume that it's an - // instance of the Type. - if (!isInstanceOf && value) { - isConstructorNameSame = value.constructor.name === Type.name; - isConstructorSourceSame = String(value.constructor) == String(Type); - isInstanceOf = (isConstructorNameSame && isConstructorSourceSame) || - instanceOf(Object.getPrototypeOf(value), Type); - } - return isInstanceOf; -} -exports.instanceOf = instanceOf; - -/** - * Function returns textual representation of a value passed to it. Function - * takes additional `indent` argument that is used for indentation. Also - * optional `limit` argument may be passed to limit amount of detail returned. - * @param {Object} value - * @param {String} [indent=" "] - * @param {Number} [limit] - */ -function source(value, indent, limit, offset, visited) { - var result; - var names; - var nestingIndex; - var isCompact = !isUndefined(limit); - - indent = indent || " "; - offset = (offset || ""); - result = ""; - visited = visited || []; - - if (isUndefined(value)) { - result += "undefined"; - } - else if (isNull(value)) { - result += "null"; - } - else if (isString(value)) { - result += '"' + value + '"'; - } - else if (isFunction(value)) { - value = String(value).split("\n"); - if (isCompact && value.length > 2) { - value = value.splice(0, 2); - value.push("...}"); - } - result += value.join("\n" + offset); - } - else if (isArray(value)) { - if ((nestingIndex = (visited.indexOf(value) + 1))) { - result = "#" + nestingIndex + "#"; - } - else { - visited.push(value); - - if (isCompact) - value = value.slice(0, limit); - - result += "[\n"; - result += value.map(function(value) { - return offset + indent + source(value, indent, limit, offset + indent, - visited); - }).join(",\n"); - result += isCompact && value.length > limit ? - ",\n" + offset + "...]" : "\n" + offset + "]"; - } - } - else if (isObject(value)) { - if ((nestingIndex = (visited.indexOf(value) + 1))) { - result = "#" + nestingIndex + "#" - } - else { - visited.push(value) - - names = Object.keys(value); - - result += "{ // " + value + "\n"; - result += (isCompact ? names.slice(0, limit) : names).map(function(name) { - var _limit = isCompact ? limit - 1 : limit; - var descriptor = Object.getOwnPropertyDescriptor(value, name); - var result = offset + indent + "// "; - var accessor; - if (0 <= name.indexOf(" ")) - name = '"' + name + '"'; - - if (descriptor.writable) - result += "writable "; - if (descriptor.configurable) - result += "configurable "; - if (descriptor.enumerable) - result += "enumerable "; - - result += "\n"; - if ("value" in descriptor) { - result += offset + indent + name + ": "; - result += source(descriptor.value, indent, _limit, indent + offset, - visited); - } - else { - - if (descriptor.get) { - result += offset + indent + "get " + name + " "; - accessor = source(descriptor.get, indent, _limit, indent + offset, - visited); - result += accessor.substr(accessor.indexOf("{")); - } - - if (descriptor.set) { - result += offset + indent + "set " + name + " "; - accessor = source(descriptor.set, indent, _limit, indent + offset, - visited); - result += accessor.substr(accessor.indexOf("{")); - } - } - return result; - }).join(",\n"); - - if (isCompact) { - if (names.length > limit && limit > 0) { - result += ",\n" + offset + indent + "//..."; - } - } - else { - if (names.length) - result += ","; - - result += "\n" + offset + indent + '"__proto__": '; - result += source(Object.getPrototypeOf(value), indent, 0, - offset + indent); - } - - result += "\n" + offset + "}"; - } - } - else { - result += String(value); - } - return result; -} -exports.source = function (value, indentation, limit) { - return source(value, indentation, limit); -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/loader/cuddlefish.js b/tools/addon-sdk-1.12/lib/sdk/loader/cuddlefish.js deleted file mode 100644 index cac05a9..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/loader/cuddlefish.js +++ /dev/null @@ -1,78 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 expandtab */ -/* 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'; - -module.metadata = { - "stability": "unstable" -}; - -// This module is manually loaded by bootstrap.js in a sandbox and immediatly -// put in module cache so that it is never loaded in any other way. - -/* Workarounds to include dependencies in the manifest -require('chrome') // Otherwise CFX will complain about Components -require('toolkit/loader') // Otherwise CFX will stip out loader.js -require('sdk/addon/runner') // Otherwise CFX will stip out addon/runner.js -*/ - -const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components; - -// `loadSandbox` is exposed by bootstrap.js -const loaderURI = module.uri.replace("sdk/loader/cuddlefish.js", - "toolkit/loader.js"); -// We need to keep a reference to the sandbox in order to unload it in -// bootstrap.js -const loaderSandbox = loadSandbox(loaderURI); -const loaderModule = loaderSandbox.exports; - -const { override } = loaderModule; - -function CuddlefishLoader(options) { - let { manifest } = options; - - options = override(options, { - // Put `api-utils/loader` and `api-utils/cuddlefish` loaded as JSM to module - // cache to avoid subsequent loads via `require`. - modules: override({ - 'toolkit/loader': loaderModule, - 'addon-sdk/sdk/loader/cuddlefish': exports - }, options.modules), - resolve: function resolve(id, requirer) { - let entry = requirer && requirer in manifest && manifest[requirer]; - let uri = null; - - // If manifest entry for this requirement is present we follow manifest. - // Note: Standard library modules like 'panel' will be present in - // manifest unless they were moved to platform. - if (entry) { - let requirement = entry.requirements[id]; - // If requirer entry is in manifest and it's requirement is not, than - // it has no authority to load since linker was not able to find it. - if (!requirement) - throw Error('Module: ' + requirer.id + ' located at ' + requirer.uri - + ' has no authority to load: ' + id, requirer.uri); - - uri = requirement; - } - // If requirer is off manifest than it's a system module and we allow it - // to go off manifest. - else { - uri = id; - } - return uri; - } - }); - - let loader = loaderModule.Loader(options); - // Hack to allow loading from `toolkit/loader`. - loader.modules[loaderURI] = loaderSandbox; - return loader; -} - -exports = override(loaderModule, { - Loader: CuddlefishLoader -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/loader/sandbox.js b/tools/addon-sdk-1.12/lib/sdk/loader/sandbox.js deleted file mode 100644 index 3c085aa..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/loader/sandbox.js +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "experimental" -}; - -const { Cc, Ci, CC, Cu } = require('chrome'); -const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')(); -const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1']. - getService(Ci.mozIJSSubScriptLoader); - -/** - * Make a new sandbox that inherits given `source`'s principals. Source can be - * URI string, DOMWindow or `null` for system principals. - */ -function sandbox(target, options) { - return Cu.Sandbox(target || systemPrincipal, options || {}); -} -exports.sandbox = sandbox - -/** - * Evaluates given `source` in a given `sandbox` and returns result. - */ -function evaluate(sandbox, code, uri, line, version) { - return Cu.evalInSandbox(code, sandbox, version || '1.8', uri || '', line || 1); -} -exports.evaluate = evaluate; - -/** - * Evaluates code under the given `uri` in the given `sandbox`. - * - * @param {String} uri - * The URL pointing to the script to load. - * It must be a local chrome:, resource:, file: or data: URL. - */ -function load(sandbox, uri) { - if (uri.indexOf('data:') === 0) { - let source = uri.substr(uri.indexOf(',') + 1); - - return evaluate(sandbox, decodeURIComponent(source), '1.8', uri, 0); - } else { - return scriptLoader.loadSubScript(uri, sandbox, 'UTF-8'); - } -} -exports.load = load;
\ No newline at end of file diff --git a/tools/addon-sdk-1.12/lib/sdk/net/url.js b/tools/addon-sdk-1.12/lib/sdk/net/url.js deleted file mode 100644 index 5992e0d..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/net/url.js +++ /dev/null @@ -1,108 +0,0 @@ -/* 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 { Cu, components } = require("chrome"); -const { defer } = require("../core/promise"); -const { merge } = require("../util/object"); - -const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); - -/** - * Open a channel synchronously for the URI given, with an optional charset, and - * returns a resolved promise if succeed; rejected promise otherwise. - */ -function readSync(uri, charset) { - let { promise, resolve, reject } = defer(); - - try { - resolve(readURISync(uri, charset)); - } - catch (e) { - reject("Failed to read: '" + uri + "' (Error Code: " + e.result + ")"); - } - - return promise; -} - -/** - * Open a channel synchronously for the URI given, with an optional charset, and - * returns a promise. - */ -function readAsync(uri, charset) { - let channel = NetUtil.newChannel(uri, charset, null); - - let { promise, resolve, reject } = defer(); - - NetUtil.asyncFetch(channel, function (stream, result) { - if (components.isSuccessCode(result)) { - let count = stream.available(); - let data = NetUtil.readInputStreamToString(stream, count, { charset : charset }); - - resolve(data); - } else { - reject("Failed to read: '" + uri + "' (Error Code: " + result + ")"); - } - }); - - return promise; -} - -/** - * Reads a URI and returns a promise. If the `sync` option is set to `true`, the - * promise will be resolved synchronously. - * - * @param uri {string} The URI to read - * @param [options] {object} This parameter can have any or all of the following - * fields: `sync`, `charset`. By default the `charset` is set to 'UTF-8'. - * - * @returns {promise} The promise that will be resolved with the content of the - * URL given. - * - * @example - * let promise = readURI('resource://gre/modules/NetUtil.jsm', { - * sync: true, - * charset: 'US-ASCII' - }); - */ -function readURI(uri, options) { - options = merge({ - charset: "UTF-8", - sync: false - }, options); - - return options.sync - ? readSync(uri, options.charset) - : readAsync(uri, options.charset); -} - -exports.readURI = readURI; - -/** - * Reads a URI synchronously. - * This function is intentionally undocumented to favorites the `readURI` usage. - * - * @param uri {string} The URI to read - * @param [charset] {string} The character set to use when read the content of - * the `uri` given. By default is set to 'UTF-8'. - * - * @returns {string} The content of the URI given. - * - * @example - * let data = readURISync('resource://gre/modules/NetUtil.jsm'); - */ -function readURISync(uri, charset) { - charset = typeof charset === "string" ? charset : "UTF-8"; - - let channel = NetUtil.newChannel(uri, charset, null); - let stream = channel.open(); - - let count = stream.available(); - let data = NetUtil.readInputStreamToString(stream, count, { charset : charset }); - - stream.close(); - - return data; -} - -exports.readURISync = readURISync; diff --git a/tools/addon-sdk-1.12/lib/sdk/net/xhr.js b/tools/addon-sdk-1.12/lib/sdk/net/xhr.js deleted file mode 100644 index b4fc1ef..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/net/xhr.js +++ /dev/null @@ -1,154 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci } = require("chrome"); -const memory = require('../deprecated/memory'); -const { when: unload } = require("../system/unload"); - -// ## Implementation Notes ## -// -// Making `XMLHttpRequest` objects available to Jetpack code involves a -// few key principles universal to all low-level module implementations: -// -// * **Unloadability**. A Jetpack-based extension using this module can be -// asked to unload itself at any time, e.g. because the user decides to -// uninstall or disable the extension. This means we need to keep track of -// all in-progress reqests and abort them on unload. -// -// * **Developer-Ergonomic Tracebacks**. Whenever an exception is raised -// by a Jetpack-based extension, we want it to be logged in a -// place that is specific to that extension--so that a developer -// can distinguish it from an error on a web page or in another -// extension, for instance. We also want it to be logged with a -// full stack traceback, which the Mozilla platform doesn't usually -// do. -// -// Because of this, we don't actually want to give the Mozilla -// platform's "real" XHR implementation to clients, but instead provide -// a simple wrapper that trivially delegates to the implementation in -// all cases except where callbacks are involved: whenever Mozilla -// platform code calls into the extension, such as during the XHR's -// `onreadystatechange` callback, we want to wrap the client's callback -// in a try-catch clause that traps any exceptions raised by the -// callback and logs them via console.exception() instead of allowing -// them to propagate back into Mozilla platform code. - -// This is a private list of all active requests, so we know what to -// abort if we're asked to unload. -var requests = []; - -// Events on XHRs that we should listen for, so we know when to remove -// a request from our private list. -const TERMINATE_EVENTS = ["load", "error", "abort"]; - -// Read-only properties of XMLHttpRequest objects that we want to -// directly delegate to. -const READ_ONLY_PROPS = ["readyState", "responseText", "responseXML", - "status", "statusText"]; - -// Methods of XMLHttpRequest that we want to directly delegate to. -const DELEGATED_METHODS = ["abort", "getAllResponseHeaders", - "getResponseHeader", "overrideMimeType", - "send", "sendAsBinary", "setRequestHeader", - "open"]; - -var getRequestCount = exports.getRequestCount = function getRequestCount() { - return requests.length; -}; - -var XMLHttpRequest = exports.XMLHttpRequest = function XMLHttpRequest() { - var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(Ci.nsIXMLHttpRequest); - // For the sake of simplicity, don't tie this request to any UI. - req.mozBackgroundRequest = true; - - memory.track(req, "XMLHttpRequest"); - - this._req = req; - this._orsc = null; - - requests.push(this); - - var self = this; - - this._boundCleanup = function _boundCleanup() { - self._cleanup(); - }; - - TERMINATE_EVENTS.forEach( - function(name) { - self._req.addEventListener(name, self._boundCleanup, false); - }); -}; - -XMLHttpRequest.prototype = { - _cleanup: function _cleanup() { - this.onreadystatechange = null; - var index = requests.indexOf(this); - if (index != -1) { - var self = this; - TERMINATE_EVENTS.forEach( - function(name) { - self._req.removeEventListener(name, self._boundCleanup, false); - }); - requests.splice(index, 1); - } - }, - _unload: function _unload() { - this._req.abort(); - this._cleanup(); - }, - addEventListener: function addEventListener() { - throw new Error("not implemented"); - }, - removeEventListener: function removeEventListener() { - throw new Error("not implemented"); - }, - set upload(newValue) { - throw new Error("not implemented"); - }, - get onreadystatechange() { - return this._orsc; - }, - set onreadystatechange(cb) { - this._orsc = cb; - if (cb) { - var self = this; - this._req.onreadystatechange = function() { - try { - self._orsc.apply(self, arguments); - } catch (e) { - console.exception(e); - } - }; - } else - this._req.onreadystatechange = null; - } -}; - -READ_ONLY_PROPS.forEach( - function(name) { - XMLHttpRequest.prototype.__defineGetter__( - name, - function() { - return this._req[name]; - }); - }); - -DELEGATED_METHODS.forEach( - function(name) { - XMLHttpRequest.prototype[name] = function() { - return this._req[name].apply(this._req, arguments); - }; - }); - -unload(function() { - requests.slice().forEach(function(request) { request._unload(); }); -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/notifications.js b/tools/addon-sdk-1.12/lib/sdk/notifications.js deleted file mode 100644 index 909552f..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/notifications.js +++ /dev/null @@ -1,83 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim:set ts=2 sw=2 sts=2 et filetype=javascript - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -module.metadata = { - "stability": "stable" -}; - -const { Cc, Ci, Cr } = require("chrome"); -const apiUtils = require("./deprecated/api-utils"); -const errors = require("./deprecated/errors"); - -try { - let alertServ = Cc["@mozilla.org/alerts-service;1"]. - getService(Ci.nsIAlertsService); - - // The unit test sets this to a mock notification function. - var notify = alertServ.showAlertNotification.bind(alertServ); -} -catch (err) { - // An exception will be thrown if the platform doesn't provide an alert - // service, e.g., if Growl is not installed on OS X. In that case, use a - // mock notification function that just logs to the console. - notify = notifyUsingConsole; -} - -exports.notify = function notifications_notify(options) { - let valOpts = validateOptions(options); - let clickObserver = !valOpts.onClick ? null : { - observe: function notificationClickObserved(subject, topic, data) { - if (topic === "alertclickcallback") - errors.catchAndLog(valOpts.onClick).call(exports, valOpts.data); - } - }; - function notifyWithOpts(notifyFn) { - notifyFn(valOpts.iconURL, valOpts.title, valOpts.text, !!clickObserver, - valOpts.data, clickObserver); - } - try { - notifyWithOpts(notify); - } - catch (err if err instanceof Ci.nsIException && - err.result == Cr.NS_ERROR_FILE_NOT_FOUND) { - console.warn("The notification icon named by " + valOpts.iconURL + - " does not exist. A default icon will be used instead."); - delete valOpts.iconURL; - notifyWithOpts(notify); - } - catch (err) { - notifyWithOpts(notifyUsingConsole); - } -}; - -function notifyUsingConsole(iconURL, title, text) { - title = title ? "[" + title + "]" : ""; - text = text || ""; - let str = [title, text].filter(function (s) s).join(" "); - console.log(str); -} - -function validateOptions(options) { - return apiUtils.validateOptions(options, { - data: { - is: ["string", "undefined"] - }, - iconURL: { - is: ["string", "undefined"] - }, - onClick: { - is: ["function", "undefined"] - }, - text: { - is: ["string", "undefined"] - }, - title: { - is: ["string", "undefined"] - } - }); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/page-mod.js b/tools/addon-sdk-1.12/lib/sdk/page-mod.js deleted file mode 100644 index 8bd4e5f..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/page-mod.js +++ /dev/null @@ -1,380 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "stable" -}; - -const observers = require('./deprecated/observer-service'); -const { Loader, validationAttributes } = require('./content/loader'); -const { Worker } = require('./content/worker'); -const { EventEmitter } = require('./deprecated/events'); -const { List } = require('./deprecated/list'); -const { Registry } = require('./util/registry'); -const { MatchPattern } = require('./page-mod/match-pattern'); -const { validateOptions : validate } = require('./deprecated/api-utils'); -const { Cc, Ci } = require('chrome'); -const { merge } = require('./util/object'); -const { readURISync } = require('./net/url'); -const { windowIterator } = require('./deprecated/window-utils'); -const { isBrowser, getFrames } = require('./window/utils'); -const { getTabs, getTabContentWindow, getTabForContentWindow, - getURI: getTabURI } = require('./tabs/utils'); -const { has, hasAny } = require('./util/array'); - -const styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"]. - getService(Ci.nsIStyleSheetService); - -const USER_SHEET = styleSheetService.USER_SHEET; - -const io = Cc['@mozilla.org/network/io-service;1']. - getService(Ci.nsIIOService); - -// Valid values for `attachTo` option -const VALID_ATTACHTO_OPTIONS = ['existing', 'top', 'frame']; - -// contentStyle* / contentScript* are sharing the same validation constraints, -// so they can be mostly reused, except for the messages. -const validStyleOptions = { - contentStyle: merge(Object.create(validationAttributes.contentScript), { - msg: 'The `contentStyle` option must be a string or an array of strings.' - }), - contentStyleFile: merge(Object.create(validationAttributes.contentScriptFile), { - msg: 'The `contentStyleFile` option must be a local URL or an array of URLs' - }) -}; - -// rules registry -const RULES = {}; - -const Rules = EventEmitter.resolve({ toString: null }).compose(List, { - add: function() Array.slice(arguments).forEach(function onAdd(rule) { - if (this._has(rule)) - return; - // registering rule to the rules registry - if (!(rule in RULES)) - RULES[rule] = new MatchPattern(rule); - this._add(rule); - this._emit('add', rule); - }.bind(this)), - remove: function() Array.slice(arguments).forEach(function onRemove(rule) { - if (!this._has(rule)) - return; - this._remove(rule); - this._emit('remove', rule); - }.bind(this)), -}); - -/** - * PageMod constructor (exported below). - * @constructor - */ -const PageMod = Loader.compose(EventEmitter, { - on: EventEmitter.required, - _listeners: EventEmitter.required, - attachTo: [], - contentScript: Loader.required, - contentScriptFile: Loader.required, - contentScriptWhen: Loader.required, - contentScriptOptions: Loader.required, - include: null, - constructor: function PageMod(options) { - this._onContent = this._onContent.bind(this); - options = options || {}; - - let { contentStyle, contentStyleFile } = validate(options, validStyleOptions); - - if ('contentScript' in options) - this.contentScript = options.contentScript; - if ('contentScriptFile' in options) - this.contentScriptFile = options.contentScriptFile; - if ('contentScriptOptions' in options) - this.contentScriptOptions = options.contentScriptOptions; - if ('contentScriptWhen' in options) - this.contentScriptWhen = options.contentScriptWhen; - if ('onAttach' in options) - this.on('attach', options.onAttach); - if ('onError' in options) - this.on('error', options.onError); - if ('attachTo' in options) { - if (typeof options.attachTo == 'string') - this.attachTo = [options.attachTo]; - else if (Array.isArray(options.attachTo)) - this.attachTo = options.attachTo; - else - throw new Error('The `attachTo` option must be a string or an array ' + - 'of strings.'); - - let isValidAttachToItem = function isValidAttachToItem(item) { - return typeof item === 'string' && - VALID_ATTACHTO_OPTIONS.indexOf(item) !== -1; - } - if (!this.attachTo.every(isValidAttachToItem)) - throw new Error('The `attachTo` option valid accept only following ' + - 'values: '+ VALID_ATTACHTO_OPTIONS.join(', ')); - if (!hasAny(this.attachTo, ["top", "frame"])) - throw new Error('The `attachTo` option must always contain at least' + - ' `top` or `frame` value'); - } - else { - this.attachTo = ["top", "frame"]; - } - - let include = options.include; - let rules = this.include = Rules(); - rules.on('add', this._onRuleAdd = this._onRuleAdd.bind(this)); - rules.on('remove', this._onRuleRemove = this._onRuleRemove.bind(this)); - - if (Array.isArray(include)) - rules.add.apply(null, include); - else - rules.add(include); - - let styleRules = ""; - - if (contentStyleFile) - styleRules = [].concat(contentStyleFile).map(readURISync).join(""); - - if (contentStyle) - styleRules += [].concat(contentStyle).join(""); - - if (styleRules) { - this._onRuleUpdate = this._onRuleUpdate.bind(this); - - this._styleRules = styleRules; - - this._registerStyleSheet(); - rules.on('add', this._onRuleUpdate); - rules.on('remove', this._onRuleUpdate); - } - - this.on('error', this._onUncaughtError = this._onUncaughtError.bind(this)); - pageModManager.add(this._public); - - this._loadingWindows = []; - - // `_applyOnExistingDocuments` has to be called after `pageModManager.add()` - // otherwise its calls to `_onContent` method won't do anything. - if ('attachTo' in options && has(options.attachTo, 'existing')) - this._applyOnExistingDocuments(); - }, - - destroy: function destroy() { - - this._unregisterStyleSheet(); - - this.include.removeListener('add', this._onRuleUpdate); - this.include.removeListener('remove', this._onRuleUpdate); - - for each (let rule in this.include) - this.include.remove(rule); - pageModManager.remove(this._public); - this._loadingWindows = []; - - }, - - _loadingWindows: [], - - _applyOnExistingDocuments: function _applyOnExistingDocuments() { - let mod = this; - // Returns true if the tab match one rule - function isMatchingURI(uri) { - // Use Array.some as `include` isn't a native array - return Array.some(mod.include, function (rule) { - return RULES[rule].test(uri); - }); - } - let tabs = getAllTabs().filter(function (tab) { - return isMatchingURI(getTabURI(tab)); - }); - - tabs.forEach(function (tab) { - // Fake a newly created document - let window = getTabContentWindow(tab); - if (has(mod.attachTo, "top")) - mod._onContent(window); - if (has(mod.attachTo, "frame")) - getFrames(window).forEach(mod._onContent); - }); - }, - - _onContent: function _onContent(window) { - // not registered yet - if (!pageModManager.has(this)) - return; - - let isTopDocument = window.top === window; - // Is a top level document and `top` is not set, ignore - if (isTopDocument && !has(this.attachTo, "top")) - return; - // Is a frame document and `frame` is not set, ignore - if (!isTopDocument && !has(this.attachTo, "frame")) - return; - - // Immediatly evaluate content script if the document state is already - // matching contentScriptWhen expectations - let state = window.document.readyState; - if ('start' === this.contentScriptWhen || - // Is `load` event already dispatched? - 'complete' === state || - // Is DOMContentLoaded already dispatched and waiting for it? - ('ready' === this.contentScriptWhen && state === 'interactive') ) { - this._createWorker(window); - return; - } - - let eventName = 'end' == this.contentScriptWhen ? 'load' : 'DOMContentLoaded'; - let self = this; - window.addEventListener(eventName, function onReady(event) { - if (event.target.defaultView != window) - return; - window.removeEventListener(eventName, onReady, true); - - self._createWorker(window); - }, true); - }, - _createWorker: function _createWorker(window) { - let worker = Worker({ - window: window, - contentScript: this.contentScript, - contentScriptFile: this.contentScriptFile, - contentScriptOptions: this.contentScriptOptions, - onError: this._onUncaughtError - }); - this._emit('attach', worker); - let self = this; - worker.once('detach', function detach() { - worker.destroy(); - }); - }, - _onRuleAdd: function _onRuleAdd(url) { - pageModManager.on(url, this._onContent); - }, - _onRuleRemove: function _onRuleRemove(url) { - pageModManager.off(url, this._onContent); - }, - _onUncaughtError: function _onUncaughtError(e) { - if (this._listeners('error').length == 1) - console.exception(e); - }, - _onRuleUpdate: function _onRuleUpdate(){ - this._registerStyleSheet(); - }, - - _registerStyleSheet : function _registerStyleSheet() { - let rules = this.include; - let styleRules = this._styleRules; - - let documentRules = []; - - this._unregisterStyleSheet(); - - for each (let rule in rules) { - let pattern = RULES[rule]; - - if (!pattern) - continue; - - if (pattern.regexp) - documentRules.push("regexp(\"" + pattern.regexp.source + "\")"); - else if (pattern.exactURL) - documentRules.push("url(" + pattern.exactURL + ")"); - else if (pattern.domain) - documentRules.push("domain(" + pattern.domain + ")"); - else if (pattern.urlPrefix) - documentRules.push("url-prefix(" + pattern.urlPrefix + ")"); - else if (pattern.anyWebPage) { - documentRules.push("regexp(\"^(https?|ftp)://.*?\")"); - break; - } - } - - let uri = "data:text/css;charset=utf-8,"; - if (documentRules.length > 0) - uri += encodeURIComponent("@-moz-document " + - documentRules.join(",") + " {" + styleRules + "}"); - else - uri += encodeURIComponent(styleRules); - - this._registeredStyleURI = io.newURI(uri, null, null); - - styleSheetService.loadAndRegisterSheet( - this._registeredStyleURI, - USER_SHEET - ); - }, - - _unregisterStyleSheet : function () { - let uri = this._registeredStyleURI; - - if (uri && styleSheetService.sheetRegistered(uri, USER_SHEET)) - styleSheetService.unregisterSheet(uri, USER_SHEET); - - this._registeredStyleURI = null; - } -}); -exports.PageMod = function(options) PageMod(options) -exports.PageMod.prototype = PageMod.prototype; - -const PageModManager = Registry.resolve({ - constructor: '_init', - _destructor: '_registryDestructor' -}).compose({ - constructor: function PageModRegistry(constructor) { - this._init(PageMod); - observers.add( - 'document-element-inserted', - this._onContentWindow = this._onContentWindow.bind(this) - ); - }, - _destructor: function _destructor() { - observers.remove('document-element-inserted', this._onContentWindow); - this._removeAllListeners(); - for (let rule in RULES) { - delete RULES[rule]; - } - - // We need to do some cleaning er PageMods, like unregistering any - // `contentStyle*` - this._registry.forEach(function(pageMod) { - pageMod.destroy(); - }); - - this._registryDestructor(); - }, - _onContentWindow: function _onContentWindow(document) { - let window = document.defaultView; - // XML documents don't have windows, and we don't yet support them. - if (!window) - return; - // We apply only on documents in tabs of Firefox - if (!getTabForContentWindow(window)) - return; - - for (let rule in RULES) - if (RULES[rule].test(document.URL)) - this._emit(rule, window); - }, - off: function off(topic, listener) { - this.removeListener(topic, listener); - if (!this._listeners(topic).length) - delete RULES[topic]; - } -}); -const pageModManager = PageModManager(); - -// Returns all tabs on all currently opened windows -function getAllTabs() { - let tabs = []; - // Iterate over all chrome windows - for (let window in windowIterator()) { - if (!isBrowser(window)) - continue; - tabs = tabs.concat(getTabs(window)); - } - return tabs; -} diff --git a/tools/addon-sdk-1.12/lib/sdk/page-mod/match-pattern.js b/tools/addon-sdk-1.12/lib/sdk/page-mod/match-pattern.js deleted file mode 100644 index 5c42e66..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/page-mod/match-pattern.js +++ /dev/null @@ -1,108 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { URL } = require("../url"); - -exports.MatchPattern = MatchPattern; - -function MatchPattern(pattern) { - if (typeof pattern.test == "function") { - - // For compatibility with -moz-document rules, we require the RegExp's - // global, ignoreCase, and multiline flags to be set to false. - if (pattern.global) { - throw new Error("A RegExp match pattern cannot be set to `global` " + - "(i.e. //g)."); - } - if (pattern.ignoreCase) { - throw new Error("A RegExp match pattern cannot be set to `ignoreCase` " + - "(i.e. //i)."); - } - if (pattern.multiline) { - throw new Error("A RegExp match pattern cannot be set to `multiline` " + - "(i.e. //m)."); - } - - this.regexp = pattern; - } - else { - let firstWildcardPosition = pattern.indexOf("*"); - let lastWildcardPosition = pattern.lastIndexOf("*"); - if (firstWildcardPosition != lastWildcardPosition) - throw new Error("There can be at most one '*' character in a wildcard."); - - if (firstWildcardPosition == 0) { - if (pattern.length == 1) - this.anyWebPage = true; - else if (pattern[1] != ".") - throw new Error("Expected a *.<domain name> string, got: " + pattern); - else - this.domain = pattern.substr(2); - } - else { - if (pattern.indexOf(":") == -1) { - throw new Error("When not using *.example.org wildcard, the string " + - "supplied is expected to be either an exact URL to " + - "match or a URL prefix. The provided string ('" + - pattern + "') is unlikely to match any pages."); - } - - if (firstWildcardPosition == -1) - this.exactURL = pattern; - else if (firstWildcardPosition == pattern.length - 1) - this.urlPrefix = pattern.substr(0, pattern.length - 1); - else { - throw new Error("The provided wildcard ('" + pattern + "') has a '*' " + - "in an unexpected position. It is expected to be the " + - "first or the last character in the wildcard."); - } - } - } -} - -MatchPattern.prototype = { - - test: function MatchPattern_test(urlStr) { - try { - var url = URL(urlStr); - } - catch (err) { - return false; - } - - // Test the URL against a RegExp pattern. For compatibility with - // -moz-document rules, we require the RegExp to match the entire URL, - // so we not only test for a match, we also make sure the matched string - // is the entire URL string. - // - // Assuming most URLs don't match most match patterns, we call `test` for - // speed when determining whether or not the URL matches, then call `exec` - // for the small subset that match to make sure the entire URL matches. - // - if (this.regexp && this.regexp.test(urlStr) && - this.regexp.exec(urlStr)[0] == urlStr) - return true; - - if (this.anyWebPage && /^(https?|ftp)$/.test(url.scheme)) - return true; - if (this.exactURL && this.exactURL == urlStr) - return true; - if (this.domain && url.host && - url.host.slice(-this.domain.length) == this.domain) - return true; - if (this.urlPrefix && 0 == urlStr.indexOf(this.urlPrefix)) - return true; - - return false; - } - -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/page-worker.js b/tools/addon-sdk-1.12/lib/sdk/page-worker.js deleted file mode 100644 index f667e4d..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/page-worker.js +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "stable" -}; - -const { Symbiont } = require("./content/symbiont"); -const { Trait } = require("./deprecated/traits"); - -if (!require("./system/xul-app").isOneOf(["Firefox", "Thunderbird"])) { - throw new Error([ - "The page-worker module currently supports only Firefox and Thunderbird. ", - "In the future, we would like it to support other applications, however. ", - "Please see https://bugzilla.mozilla.org/show_bug.cgi?id=546740 for more ", - "information." - ].join("")); -} - -const Page = Trait.compose( - Symbiont.resolve({ - constructor: '_initSymbiont' - }), - { - _frame: Trait.required, - _initFrame: Trait.required, - postMessage: Symbiont.required, - on: Symbiont.required, - destroy: Symbiont.required, - - constructor: function Page(options) { - options = options || {}; - - this.contentURL = 'contentURL' in options ? options.contentURL - : 'about:blank'; - if ('contentScriptWhen' in options) - this.contentScriptWhen = options.contentScriptWhen; - if ('contentScriptFile' in options) - this.contentScriptFile = options.contentScriptFile; - if ('contentScriptOptions' in options) - this.contentScriptOptions = options.contentScriptOptions; - if ('contentScript' in options) - this.contentScript = options.contentScript; - if ('allow' in options) - this.allow = options.allow; - if ('onError' in options) - this.on('error', options.onError); - if ('onMessage' in options) - this.on('message', options.onMessage); - - this.on('propertyChange', this._onChange.bind(this)); - - this._initSymbiont(); - }, - - _onChange: function _onChange(e) { - if ('contentURL' in e && this._frame) { - // Cleanup the worker before injecting the content script in the new - // document - this._workerCleanup(); - this._initFrame(this._frame); - } - } - } -); -exports.Page = function(options) Page(options); -exports.Page.prototype = Page.prototype; diff --git a/tools/addon-sdk-1.12/lib/sdk/panel.js b/tools/addon-sdk-1.12/lib/sdk/panel.js deleted file mode 100644 index 3ce6284..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/panel.js +++ /dev/null @@ -1,403 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "stable" -}; - -if (!require("./system/xul-app").is("Firefox")) { - throw new Error([ - "The panel module currently supports only Firefox. In the future ", - "we would like it to support other applications, however. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps ", - "for more information." - ].join("")); -} - -const { Cc, Ci } = require("chrome"); - -const { validateOptions: valid } = require('./deprecated/api-utils'); -const { Symbiont } = require('./content/content'); -const { EventEmitter } = require('./deprecated/events'); -const timer = require('./timers'); -const runtime = require('./system/runtime'); -const { getMostRecentBrowserWindow } = require('./window/utils'); - -const windowMediator = Cc['@mozilla.org/appshell/window-mediator;1']. - getService(Ci.nsIWindowMediator); - -const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", - ON_SHOW = 'popupshown', - ON_HIDE = 'popuphidden', - validNumber = { is: ['number', 'undefined', 'null'] }; - -/** - * Emits show and hide events. - */ -const Panel = Symbiont.resolve({ - constructor: '_init', - _onInit: '_onSymbiontInit', - destroy: '_symbiontDestructor', - _documentUnload: '_workerDocumentUnload' -}).compose({ - _frame: Symbiont.required, - _init: Symbiont.required, - _onSymbiontInit: Symbiont.required, - _symbiontDestructor: Symbiont.required, - _emit: Symbiont.required, - on: Symbiont.required, - removeListener: Symbiont.required, - - _inited: false, - - /** - * If set to `true` frame loaders between xul panel frame and - * hidden frame are swapped. If set to `false` frame loaders are - * set back to normal. Setting the value that was already set will - * have no effect. - */ - set _frameLoadersSwapped(value) { - if (this.__frameLoadersSwapped == value) return; - this._frame.QueryInterface(Ci.nsIFrameLoaderOwner) - .swapFrameLoaders(this._viewFrame); - this.__frameLoadersSwapped = value; - }, - __frameLoadersSwapped: false, - - constructor: function Panel(options) { - this._onShow = this._onShow.bind(this); - this._onHide = this._onHide.bind(this); - this.on('inited', this._onSymbiontInit.bind(this)); - this.on('propertyChange', this._onChange.bind(this)); - - options = options || {}; - if ('onShow' in options) - this.on('show', options.onShow); - if ('onHide' in options) - this.on('hide', options.onHide); - if ('width' in options) - this.width = options.width; - if ('height' in options) - this.height = options.height; - if ('contentURL' in options) - this.contentURL = options.contentURL; - - this._init(options); - }, - _destructor: function _destructor() { - this.hide(); - this._removeAllListeners('show'); - this._removeAllListeners('hide'); - this._removeAllListeners('propertyChange'); - this._removeAllListeners('inited'); - // defer cleanup to be performed after panel gets hidden - this._xulPanel = null; - this._symbiontDestructor(this); - this._removeAllListeners(); - }, - destroy: function destroy() { - this._destructor(); - }, - /* Public API: Panel.width */ - get width() this._width, - set width(value) - this._width = valid({ $: value }, { $: validNumber }).$ || this._width, - _width: 320, - /* Public API: Panel.height */ - get height() this._height, - set height(value) - this._height = valid({ $: value }, { $: validNumber }).$ || this._height, - _height: 240, - - /* Public API: Panel.isShowing */ - get isShowing() !!this._xulPanel && this._xulPanel.state == "open", - - /* Public API: Panel.show */ - show: function show(anchor) { - anchor = anchor || null; - let document = getWindow(anchor).document; - let xulPanel = this._xulPanel; - if (!xulPanel) { - xulPanel = this._xulPanel = document.createElementNS(XUL_NS, 'panel'); - xulPanel.setAttribute("type", "arrow"); - - // One anonymous node has a big padding that doesn't work well with - // Jetpack, as we would like to display an iframe that completely fills - // the panel. - // -> Use a XBL wrapper with inner stylesheet to remove this padding. - let css = ".panel-inner-arrowcontent, .panel-arrowcontent {padding: 0;}"; - let originalXBL = "chrome://global/content/bindings/popup.xml#arrowpanel"; - let binding = - '<bindings xmlns="http://www.mozilla.org/xbl">' + - '<binding id="id" extends="' + originalXBL + '">' + - '<resources>' + - '<stylesheet src="data:text/css;charset=utf-8,' + - document.defaultView.encodeURIComponent(css) + '"/>' + - '</resources>' + - '</binding>' + - '</bindings>'; - xulPanel.style.MozBinding = 'url("data:text/xml;charset=utf-8,' + - document.defaultView.encodeURIComponent(binding) + '")'; - - let frame = document.createElementNS(XUL_NS, 'iframe'); - frame.setAttribute('type', 'content'); - frame.setAttribute('flex', '1'); - frame.setAttribute('transparent', 'transparent'); - if (runtime.OS === "Darwin") { - frame.style.borderRadius = "6px"; - frame.style.padding = "1px"; - } - - // Load an empty document in order to have an immediatly loaded iframe, - // so swapFrameLoaders is going to work without having to wait for load. - frame.setAttribute("src","data:;charset=utf-8,"); - - xulPanel.appendChild(frame); - document.getElementById("mainPopupSet").appendChild(xulPanel); - } - let { width, height } = this, x, y, position; - - if (!anchor) { - // Open the popup in the middle of the window. - x = document.documentElement.clientWidth / 2 - width / 2; - y = document.documentElement.clientHeight / 2 - height / 2; - position = null; - } - else { - // Open the popup by the anchor. - let rect = anchor.getBoundingClientRect(); - - let window = anchor.ownerDocument.defaultView; - - let zoom = window.mozScreenPixelsPerCSSPixel; - let screenX = rect.left + window.mozInnerScreenX * zoom; - let screenY = rect.top + window.mozInnerScreenY * zoom; - - // Set up the vertical position of the popup relative to the anchor - // (always display the arrow on anchor center) - let horizontal, vertical; - if (screenY > window.screen.availHeight / 2 + height) - vertical = "top"; - else - vertical = "bottom"; - - if (screenY > window.screen.availWidth / 2 + width) - horizontal = "left"; - else - horizontal = "right"; - - let verticalInverse = vertical == "top" ? "bottom" : "top"; - position = vertical + "center " + verticalInverse + horizontal; - - // Allow panel to flip itself if the panel can't be displayed at the - // specified position (useful if we compute a bad position or if the - // user moves the window and panel remains visible) - xulPanel.setAttribute("flip","both"); - } - - // Resize the iframe instead of using panel.sizeTo - // because sizeTo doesn't work with arrow panels - xulPanel.firstChild.style.width = width + "px"; - xulPanel.firstChild.style.height = height + "px"; - - // Wait for the XBL binding to be constructed - function waitForBinding() { - if (!xulPanel.openPopup) { - timer.setTimeout(waitForBinding, 50); - return; - } - xulPanel.openPopup(anchor, position, x, y); - } - waitForBinding(); - - return this._public; - }, - /* Public API: Panel.hide */ - hide: function hide() { - // The popuphiding handler takes care of swapping back the frame loaders - // and removing the XUL panel from the application window, we just have to - // trigger it by hiding the popup. - // XXX Sometimes I get "TypeError: xulPanel.hidePopup is not a function" - // when quitting the host application while a panel is visible. To suppress - // them, this now checks for "hidePopup" in xulPanel before calling it. - // It's not clear if there's an actual issue or the error is just normal. - let xulPanel = this._xulPanel; - if (xulPanel && "hidePopup" in xulPanel) - xulPanel.hidePopup(); - return this._public; - }, - - /* Public API: Panel.resize */ - resize: function resize(width, height) { - this.width = width; - this.height = height; - // Resize the iframe instead of using panel.sizeTo - // because sizeTo doesn't work with arrow panels - let xulPanel = this._xulPanel; - if (xulPanel) { - xulPanel.firstChild.style.width = width + "px"; - xulPanel.firstChild.style.height = height + "px"; - } - }, - - // While the panel is visible, this is the XUL <panel> we use to display it. - // Otherwise, it's null. - get _xulPanel() this.__xulPanel, - set _xulPanel(value) { - let xulPanel = this.__xulPanel; - if (value === xulPanel) return; - if (xulPanel) { - xulPanel.removeEventListener(ON_HIDE, this._onHide, false); - xulPanel.removeEventListener(ON_SHOW, this._onShow, false); - xulPanel.parentNode.removeChild(xulPanel); - } - if (value) { - value.addEventListener(ON_HIDE, this._onHide, false); - value.addEventListener(ON_SHOW, this._onShow, false); - } - this.__xulPanel = value; - }, - __xulPanel: null, - get _viewFrame() this.__xulPanel.children[0], - /** - * When the XUL panel becomes hidden, we swap frame loaders back to move - * the content of the panel to the hidden frame & remove panel element. - */ - _onHide: function _onHide() { - try { - this._frameLoadersSwapped = false; - this._xulPanel = null; - this._emit('hide'); - } catch(e) { - this._emit('error', e); - } - }, - - /** - * Retrieve computed text color style in order to apply to the iframe - * document. As MacOS background is dark gray, we need to use skin's - * text color. - */ - _applyStyleToDocument: function _applyStyleToDocument() { - try { - let win = this._xulPanel.ownerDocument.defaultView; - let node = win.document.getAnonymousElementByAttribute( - this._xulPanel, "class", "panel-arrowcontent"); - if (!node) { - // Before bug 764755, anonymous content was different: - // TODO: Remove this when targeting FF16+ - node = win.document.getAnonymousElementByAttribute( - this._xulPanel, "class", "panel-inner-arrowcontent"); - } - let textColor = win.getComputedStyle(node).getPropertyValue("color"); - let doc = this._xulPanel.firstChild.contentDocument; - let style = doc.createElement("style"); - style.textContent = "body { color: " + textColor + "; }"; - let container = doc.head ? doc.head : doc.documentElement; - - if (container.firstChild) - container.insertBefore(style, container.firstChild); - else - container.appendChild(style); - } - catch(e) { - console.error("Unable to apply panel style"); - console.exception(e); - } - }, - - /** - * When the XUL panel becomes shown, we swap frame loaders between panel - * frame and hidden frame to preserve state of the content dom. - */ - _onShow: function _onShow() { - try { - if (!this._inited) { // defer if not initialized yet - this.on('inited', this._onShow.bind(this)); - } else { - this._frameLoadersSwapped = true; - this._applyStyleToDocument(); - this._emit('show'); - } - } catch(e) { - this._emit('error', e); - } - }, - /** - * Notification that panel was fully initialized. - */ - _onInit: function _onInit() { - this._inited = true; - - // Avoid panel document from resizing the browser window - // New platform capability added through bug 635673 - if ("allowWindowControl" in this._frame.docShell) - this._frame.docShell.allowWindowControl = false; - - // perform all deferred tasks like initSymbiont, show, hide ... - // TODO: We're publicly exposing a private event here; this - // 'inited' event should really be made private, somehow. - this._emit('inited'); - }, - - // Catch document unload event in order to rebind load event listener with - // Symbiont._initFrame if Worker._documentUnload destroyed the worker - _documentUnload: function(subject, topic, data) { - if (this._workerDocumentUnload(subject, topic, data)) { - this._initFrame(this._frame); - return true; - } - return false; - }, - - _onChange: function _onChange(e) { - if ('contentURL' in e && this._frame) { - // Cleanup the worker before injecting the content script in the new - // document - this._workerCleanup(); - this._initFrame(this._frame); - } - } -}); -exports.Panel = function(options) Panel(options) -exports.Panel.prototype = Panel.prototype; - -function getWindow(anchor) { - let window; - - if (anchor) { - let anchorWindow = anchor.ownerDocument.defaultView.top; - let anchorDocument = anchorWindow.document; - - let enumerator = windowMediator.getEnumerator("navigator:browser"); - while (enumerator.hasMoreElements()) { - let enumWindow = enumerator.getNext(); - - // Check if the anchor is in this browser window. - if (enumWindow == anchorWindow) { - window = anchorWindow; - break; - } - - // Check if the anchor is in a browser tab in this browser window. - let browser = enumWindow.gBrowser.getBrowserForDocument(anchorDocument); - if (browser) { - window = enumWindow; - break; - } - - // Look in other subdocuments (sidebar, etc.)? - } - } - - // If we didn't find the anchor's window (or we have no anchor), - // return the most recent browser window. - if (!window) - window = getMostRecentBrowserWindow(); - - return window; -} - diff --git a/tools/addon-sdk-1.12/lib/sdk/passwords.js b/tools/addon-sdk-1.12/lib/sdk/passwords.js deleted file mode 100644 index 7aeb22a..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/passwords.js +++ /dev/null @@ -1,62 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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'; - -module.metadata = { - "stability": "stable" -}; - -const { search, remove, store } = require("./passwords/utils"); -const { defer, delay } = require("./lang/functional"); - -/** - * Utility function that returns `onComplete` and `onError` callbacks form the - * given `options` objects. Also properties are removed from the passed - * `options` objects. - * @param {Object} options - * Object that is passed to the exported functions of this module. - * @returns {Function[]} - * Array with two elements `onComplete` and `onError` functions. - */ -function getCallbacks(options) { - let value = [ - 'onComplete' in options ? options.onComplete : null, - 'onError' in options ? defer(options.onError) : console.exception - ]; - - delete options.onComplete; - delete options.onError; - - return value; -}; - -/** - * Creates a wrapper function that tries to call `onComplete` with a return - * value of the wrapped function or falls back to `onError` if wrapped function - * throws an exception. - */ -function createWrapperMethod(wrapped) { - return function (options) { - let [ onComplete, onError ] = getCallbacks(options); - try { - let value = wrapped(options); - if (onComplete) { - delay(function() { - try { - onComplete(value); - } catch (exception) { - onError(exception); - } - }); - } - } catch (exception) { - onError(exception); - } - }; -} - -exports.search = createWrapperMethod(search); -exports.store = createWrapperMethod(store); -exports.remove = createWrapperMethod(remove); diff --git a/tools/addon-sdk-1.12/lib/sdk/passwords/utils.js b/tools/addon-sdk-1.12/lib/sdk/passwords/utils.js deleted file mode 100644 index 8cc2230..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/passwords/utils.js +++ /dev/null @@ -1,105 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci, CC } = require("chrome"); -const { uri: ADDON_URI } = require("../self"); -const loginManager = Cc["@mozilla.org/login-manager;1"]. - getService(Ci.nsILoginManager); -const { URL: parseURL } = require("../url"); -const LoginInfo = CC("@mozilla.org/login-manager/loginInfo;1", - "nsILoginInfo", "init"); - -function filterMatchingLogins(loginInfo) - Object.keys(this).every(function(key) loginInfo[key] === this[key], this); - -/** - * Removes `user`, `password` and `path` fields from the given `url` if it's - * 'http', 'https' or 'ftp'. All other URLs are returned unchanged. - * @example - * http://user:pass@www.site.com/foo/?bar=baz#bang -> http://www.site.com - */ -function normalizeURL(url) { - let { scheme, host, port } = parseURL(url); - // We normalize URL only if it's `http`, `https` or `ftp`. All other types of - // URLs (`resource`, `chrome`, etc..) should not be normalized as they are - // used with add-on associated credentials path. - return scheme === "http" || scheme === "https" || scheme === "ftp" ? - scheme + "://" + (host || "") + (port ? ":" + port : "") : - url -} - -function Login(options) { - let login = Object.create(Login.prototype); - Object.keys(options || {}).forEach(function(key) { - if (key === 'url') - login.hostname = normalizeURL(options.url); - else if (key === 'formSubmitURL') - login.formSubmitURL = options.formSubmitURL ? - normalizeURL(options.formSubmitURL) : null; - else if (key === 'realm') - login.httpRealm = options.realm; - else - login[key] = options[key]; - }); - - return login; -} -Login.prototype.toJSON = function toJSON() { - return { - url: this.hostname || ADDON_URI, - realm: this.httpRealm || null, - formSubmitURL: this.formSubmitURL || null, - username: this.username || null, - password: this.password || null, - usernameField: this.usernameField || '', - passwordField: this.passwordField || '', - } -}; -Login.prototype.toLoginInfo = function toLoginInfo() { - let { url, realm, formSubmitURL, username, password, usernameField, - passwordField } = this.toJSON(); - - return new LoginInfo(url, formSubmitURL, realm, username, password, - usernameField, passwordField); -}; - -function loginToJSON(value) Login(value).toJSON() - -/** - * Returns array of `nsILoginInfo` objects that are stored in the login manager - * and have all the properties with matching values as a given `options` object. - * @param {Object} options - * @returns {nsILoginInfo[]} - */ -exports.search = function search(options) { - return loginManager.getAllLogins() - .filter(filterMatchingLogins, Login(options)) - .map(loginToJSON); -}; - -/** - * Stores login info created from the given `options` to the applications - * built-in login management system. - * @param {Object} options. - */ -exports.store = function store(options) { - loginManager.addLogin(Login(options).toLoginInfo()); -}; - -/** - * Removes login info from the applications built-in login management system. - * _Please note: When removing a login info the specified properties must - * exactly match to the one that is already stored or exception will be thrown._ - * @param {Object} options. - */ -exports.remove = function remove(options) { - loginManager.removeLogin(Login(options).toLoginInfo()); -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/platform/xpcom.js b/tools/addon-sdk-1.12/lib/sdk/platform/xpcom.js deleted file mode 100644 index 279d234..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/platform/xpcom.js +++ /dev/null @@ -1,229 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci, Cr, Cm, components: { classesByID } } = require('chrome'); -const { registerFactory, unregisterFactory, isCIDRegistered } = - Cm.QueryInterface(Ci.nsIComponentRegistrar); - -const { merge } = require('../util/object'); -const { Class, extend, mix } = require('../core/heritage'); -const { uuid } = require('../util/uuid'); - -// This is a base prototype, that provides bare bones of XPCOM. JS based -// components can be easily implement by extending it. -const Unknown = new function() { - function hasInterface(component, iid) { - return component && component.interfaces && - ( component.interfaces.some(function(id) iid.equals(Ci[id])) || - component.implements.some(function($) hasInterface($, iid)) || - hasInterface(Object.getPrototypeOf(component), iid)); - } - - return Class({ - /** - * The `QueryInterface` method provides runtime type discovery used by XPCOM. - * This method return queried instance of `this` if given `iid` is listed in - * the `interfaces` property or in equivalent properties of objects in it's - * prototype chain. In addition it will look up in the prototypes under - * `implements` array property, this ways compositions made via `Class` - * utility will carry interfaces implemented by composition components. - */ - QueryInterface: function QueryInterface(iid) { - // For some reason there are cases when `iid` is `null`. In such cases we - // just return `this`. Otherwise we verify that component implements given - // `iid` interface. This will be no longer necessary once Bug 748003 is - // fixed. - if (iid && !hasInterface(this, iid)) - throw Cr.NS_ERROR_NO_INTERFACE; - - return this; - }, - /** - * Array of `XPCOM` interfaces (as strings) implemented by this component. - * All components implement `nsISupports` by default which is default value - * here. Provide array of interfaces implemented by an object when - * extending, to append them to this list (Please note that there is no - * need to repeat interfaces implemented by super as they will be added - * automatically). - */ - interfaces: Object.freeze([ 'nsISupports' ]) - }); -} -exports.Unknown = Unknown; - -// Base exemplar for creating instances implementing `nsIFactory` interface, -// that maybe registered into runtime via `register` function. Instances of -// this factory create instances of enclosed component on `createInstance`. -const Factory = Class({ - extends: Unknown, - interfaces: [ 'nsIFactory' ], - /** - * All the descendants will get auto generated `id` (also known as `classID` - * in XPCOM world) unless one is manually provided. - */ - get id() { throw Error('Factory must implement `id` property') }, - /** - * XPCOM `contractID` may optionally be provided to associate this factory - * with it. `contract` is a unique string that has a following format: - * '@vendor.com/unique/id;1'. - */ - contract: null, - /** - * Class description that is being registered. This value is intended as a - * human-readable description for the given class and does not needs to be - * globally unique. - */ - description: 'Jetpack generated factory', - /** - * This method is required by `nsIFactory` interfaces, but as in most - * implementations it does nothing interesting. - */ - lockFactory: function lockFactory(lock) undefined, - /** - * If property is `true` XPCOM service / factory will be registered - * automatically on creation. - */ - register: true, - /** - * If property is `true` XPCOM factory will be unregistered prior to add-on - * unload. - */ - unregister: true, - /** - * Method is called on `Service.new(options)` passing given `options` to - * it. Options is expected to have `component` property holding XPCOM - * component implementation typically decedent of `Unknown` or any custom - * implementation with a `new` method and optional `register`, `unregister` - * flags. Unless `register` is `false` Service / Factory will be - * automatically registered. Unless `unregister` is `false` component will - * be automatically unregistered on add-on unload. - */ - initialize: function initialize(options) { - merge(this, { - id: 'id' in options ? options.id : uuid(), - register: 'register' in options ? options.register : this.register, - unregister: 'unregister' in options ? options.unregister : this.unregister, - contract: 'contract' in options ? options.contract : null, - Component: options.Component - }); - - // If service / factory has auto registration enabled then register. - if (this.register) - register(this); - }, - /** - * Creates an instance of the class associated with this factory. - */ - createInstance: function createInstance(outer, iid) { - try { - if (outer) - throw Cr.NS_ERROR_NO_AGGREGATION; - return this.create().QueryInterface(iid); - } - catch (error) { - throw error instanceof Ci.nsIException ? error : Cr.NS_ERROR_FAILURE; - } - }, - create: function create() this.Component() -}); -exports.Factory = Factory; - -// Exemplar for creating services that implement `nsIFactory` interface, that -// can be registered into runtime via call to `register`. This services return -// enclosed `component` on `getService`. -const Service = Class({ - extends: Factory, - initialize: function initialize(options) { - this.component = options.Component(); - Factory.prototype.initialize.call(this, options); - }, - description: 'Jetpack generated service', - /** - * Creates an instance of the class associated with this factory. - */ - create: function create() this.component -}); -exports.Service = Service; - -function isRegistered({ id }) isCIDRegistered(id) -exports.isRegistered = isRegistered; - -/** - * Registers given `component` object to be used to instantiate a particular - * class identified by `component.id`, and creates an association of class - * name and `component.contract` with the class. - */ -function register(factory) { - if (!(factory instanceof Factory)) { - throw new Error("xpcom.register() expect a Factory instance.\n" + - "Please refactor your code to new xpcom module if you" + - " are repacking an addon from SDK <= 1.5:\n" + - "https://addons.mozilla.org/en-US/developers/docs/sdk/latest/packages/api-utils/xpcom.html"); - } - - registerFactory(factory.id, factory.description, factory.contract, factory); - - if (factory.unregister) - require('../system/unload').when(unregister.bind(null, factory)); -} -exports.register = register; - -/** - * Unregister a factory associated with a particular class identified by - * `factory.classID`. - */ -function unregister(factory) { - if (isRegistered(factory)) - unregisterFactory(factory.id, factory); -} -exports.unregister = unregister; - -function autoRegister(path) { - // TODO: This assumes that the url points to a directory - // that contains subdirectories corresponding to OS/ABI and then - // further subdirectories corresponding to Gecko platform version. - // we should probably either behave intelligently here or allow - // the caller to pass-in more options if e.g. there aren't - // Gecko-specific binaries for a component (which will be the case - // if only frozen interfaces are used). - - var runtime = require("../system/runtime"); - var osDirName = runtime.OS + "_" + runtime.XPCOMABI; - var platformVersion = require("../system/xul-app").platformVersion.substring(0, 5); - - var file = Cc['@mozilla.org/file/local;1'] - .createInstance(Ci.nsILocalFile); - file.initWithPath(path); - file.append(osDirName); - file.append(platformVersion); - - if (!(file.exists() && file.isDirectory())) - throw new Error("component not available for OS/ABI " + - osDirName + " and platform " + platformVersion); - - Cm.QueryInterface(Ci.nsIComponentRegistrar); - Cm.autoRegister(file); -} -exports.autoRegister = autoRegister; - -/** - * Returns registered factory that has a given `id` or `null` if not found. - */ -function factoryByID(id) classesByID[id] || null -exports.factoryByID = factoryByID; - -/** - * Returns factory registered with a given `contract` or `null` if not found. - * In contrast to `Cc[contract]` that does ignores new factory registration - * with a given `contract` this will return a factory currently associated - * with a `contract`. - */ -function factoryByContract(contract) factoryByID(Cm.contractIDToCID(contract)) -exports.factoryByContract = factoryByContract; diff --git a/tools/addon-sdk-1.12/lib/sdk/preferences/event-target.js b/tools/addon-sdk-1.12/lib/sdk/preferences/event-target.js deleted file mode 100644 index fe9e07a..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/preferences/event-target.js +++ /dev/null @@ -1,60 +0,0 @@ -/* 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'; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci } = require('chrome'); -const { Class } = require('../core/heritage'); -const { EventTarget } = require('../event/target'); -const { Branch } = require('./service'); -const { emit, off } = require('../event/core'); -const { when: unload } = require('../system/unload'); - -const prefTargetNS = require('../core/namespace').ns(); - -const PrefsTarget = Class({ - extends: EventTarget, - initialize: function(options) { - options = options || {}; - EventTarget.prototype.initialize.call(this, options); - - let branchName = options.branchName || ''; - let branch = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService). - getBranch(branchName). - QueryInterface(Ci.nsIPrefBranch2); - prefTargetNS(this).branch = branch; - - // provides easy access to preference values - this.prefs = Branch(branchName); - - // start listening to preference changes - let observer = prefTargetNS(this).observer = onChange.bind(this); - branch.addObserver('', observer, false); - - // Make sure to destroy this on unload - unload(destroy.bind(this)); - } -}); -exports.PrefsTarget = PrefsTarget; - -/* HELPERS */ - -function onChange(subject, topic, name) { - if (topic === 'nsPref:changed') - emit(this, name, name); - emit(this, '', name); -} - -function destroy() { - off(this); - - // stop listening to preference changes - let branch = prefTargetNS(this).branch; - branch.removeObserver('', prefTargetNS(this).observer, false); - prefTargetNS(this).observer = null; -} diff --git a/tools/addon-sdk-1.12/lib/sdk/preferences/service.js b/tools/addon-sdk-1.12/lib/sdk/preferences/service.js deleted file mode 100644 index 4303fc1..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/preferences/service.js +++ /dev/null @@ -1,174 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -// The minimum and maximum integers that can be set as preferences. -// The range of valid values is narrower than the range of valid JS values -// because the native preferences code treats integers as NSPR PRInt32s, -// which are 32-bit signed integers on all platforms. -const MAX_INT = 0x7FFFFFFF; -const MIN_INT = -0x80000000; - -const {Cc,Ci,Cr} = require("chrome"); - -const prefService = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService); -const prefSvc = prefService.getBranch(null); -const defaultBranch = prefService.getDefaultBranch(null); - -function Branch(branchName) { - function getPrefKeys() { - return keys(branchName).map(function(key) { - return key.replace(branchName, ""); - }); - } - - return Proxy.create({ - get: function(receiver, pref) { - return get(branchName + pref); - }, - set: function(receiver, pref, val) { - set(branchName + pref, val); - }, - delete: function(pref) { - reset(branchName + pref); - return true; - }, - has: function hasPrefKey(pref) { - return has(branchName + pref) - }, - getPropertyDescriptor: function(name) { - return { - value: get(branchName + name) - }; - }, - enumerate: getPrefKeys, - keys: getPrefKeys - }, Branch.prototype); -} - -function get(name, defaultValue) { - switch (prefSvc.getPrefType(name)) { - case Ci.nsIPrefBranch.PREF_STRING: - return prefSvc.getComplexValue(name, Ci.nsISupportsString).data; - - case Ci.nsIPrefBranch.PREF_INT: - return prefSvc.getIntPref(name); - - case Ci.nsIPrefBranch.PREF_BOOL: - return prefSvc.getBoolPref(name); - - case Ci.nsIPrefBranch.PREF_INVALID: - return defaultValue; - - default: - // This should never happen. - throw new Error("Error getting pref " + name + - "; its value's type is " + - prefSvc.getPrefType(name) + - ", which I don't know " + - "how to handle."); - } -} -exports.get = get; - -function set(name, value) { - var prefType; - if (typeof value != "undefined" && value != null) - prefType = value.constructor.name; - - switch (prefType) { - case "String": - { - var string = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - string.data = value; - prefSvc.setComplexValue(name, Ci.nsISupportsString, string); - } - break; - - case "Number": - // We throw if the number is outside the range or not an integer, since - // the result will not be what the consumer wanted to store. - if (value > MAX_INT || value < MIN_INT) - throw new Error("you cannot set the " + name + - " pref to the number " + value + - ", as number pref values must be in the signed " + - "32-bit integer range -(2^31) to 2^31-1. " + - "To store numbers outside that range, store " + - "them as strings."); - if (value % 1 != 0) - throw new Error("cannot store non-integer number: " + value); - prefSvc.setIntPref(name, value); - break; - - case "Boolean": - prefSvc.setBoolPref(name, value); - break; - - default: - throw new Error("can't set pref " + name + " to value '" + value + - "'; it isn't a string, integer, or boolean"); - } -} -exports.set = set; - -function has(name) { - return (prefSvc.getPrefType(name) != Ci.nsIPrefBranch.PREF_INVALID); -} -exports.has = has; - -function keys(root) { - return prefSvc.getChildList(root); -} -exports.keys = keys; - -function isSet(name) { - return (has(name) && prefSvc.prefHasUserValue(name)); -} -exports.isSet = isSet; - -function reset(name) { - try { - prefSvc.clearUserPref(name); - } catch (e if e.result == Cr.NS_ERROR_UNEXPECTED) { - // The pref service throws NS_ERROR_UNEXPECTED when the caller tries - // to reset a pref that doesn't exist or is already set to its default - // value. This interface fails silently in those cases, so callers - // can unconditionally reset a pref without having to check if it needs - // resetting first or trap exceptions after the fact. It passes through - // other exceptions, however, so callers know about them, since we don't - // know what other exceptions might be thrown and what they might mean. - } -} -exports.reset = reset; - -function getLocalized(name, defaultValue) { - let value = null; - try { - value = prefSvc.getComplexValue(name, Ci.nsIPrefLocalizedString).data; - } - finally { - return value || defaultValue; - } -} -exports.getLocalized = getLocalized; - -function setLocalized(name, value) { - // We can't use `prefs.set` here as we have to use `getDefaultBranch` - // (instead of `getBranch`) in order to have `mIsDefault` set to true, here: - // http://mxr.mozilla.org/mozilla-central/source/modules/libpref/src/nsPrefBranch.cpp#233 - // Otherwise, we do not enter into this expected condition: - // http://mxr.mozilla.org/mozilla-central/source/modules/libpref/src/nsPrefBranch.cpp#244 - defaultBranch.setCharPref(name, value); -} -exports.setLocalized = setLocalized; - -exports.Branch = Branch; - diff --git a/tools/addon-sdk-1.12/lib/sdk/private-browsing.js b/tools/addon-sdk-1.12/lib/sdk/private-browsing.js deleted file mode 100644 index 6d2a709..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/private-browsing.js +++ /dev/null @@ -1,36 +0,0 @@ -/* 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'; - -module.metadata = { - "stability": "stable" -}; - -const { setMode, getMode, on: onStateChange } = require('./private-browsing/utils'); -const { emit, on, once, off } = require('./event/core'); -const { when: unload } = require('./system/unload'); -const observers = require('./deprecated/observer-service'); - -onStateChange('start', function onStart() { - emit(exports, 'start'); -}); - -onStateChange('stop', function onStop() { - emit(exports, 'stop'); -}); - -// Make sure listeners are cleaned up. -unload(function() off(exports)); - -Object.defineProperty(exports, "isActive", { get: function() getMode() }); -exports.activate = function activate() setMode(true); -exports.deactivate = function deactivate() setMode(false); -exports.on = on.bind(null, exports); -exports.once = once.bind(null, exports); -exports.removeListener = function removeListener(type, listener) { - // Note: We can't just bind `off` as we do it for other methods cause skipping - // a listener argument will remove all listeners for the given event type - // causing misbehavior. This way we make sure all arguments are passed. - off(exports, type, listener); -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/private-browsing/utils.js b/tools/addon-sdk-1.12/lib/sdk/private-browsing/utils.js deleted file mode 100644 index 1f57ca0..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/private-browsing/utils.js +++ /dev/null @@ -1,74 +0,0 @@ -/* 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'; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci } = require('chrome'); -const { defer } = require('../lang/functional'); -const observers = require('../deprecated/observer-service'); -const { emit, on, once, off } = require('../event/core'); -const { when: unload } = require('../system/unload'); -const { getWindowLoadingContext } = require('../window/utils'); -const { deprecateFunction } = require('../util/deprecate'); - -let deferredEmit = defer(emit); - -let pbService; - -// Currently, only Firefox implements the private browsing service. -if (require("../system/xul-app").is("Firefox")) { - pbService = Cc["@mozilla.org/privatebrowsing;1"]. - getService(Ci.nsIPrivateBrowsingService); - - // set up an observer for private browsing switches. - observers.add('private-browsing-transition-complete', function onChange() { - // Emit event with in next turn of event loop. - deferredEmit(exports, pbService.privateBrowsingEnabled ? 'start' : 'stop'); - }); -} - -// checks that per-window private browsing implemented -let isWindowPBEnabled = function isWindowPBEnabled(chromeWin) { - return !!(chromeWin && - 'gPrivateBrowsingUI' in chromeWin && - 'privateWindow' in chromeWin.gPrivateBrowsingUI); -} -exports.isWindowPBEnabled = isWindowPBEnabled; - -// We toggle private browsing mode asynchronously in order to work around -// bug 659629. Since private browsing transitions are asynchronous -// anyway, this doesn't significantly change the behavior of the API. -// Note: this method should not be used with `chromeWin` argument until -// the UI has been implemented. Bug 729865 -let setMode = defer(function setMode(value, chromeWin) { - value = !!value; // Cast to boolean. - - if (isWindowPBEnabled(chromeWin)) - return getWindowLoadingContext(chromeWin).usePrivateBrowsing = value; - - // default - return pbService && (pbService.privateBrowsingEnabled = value); -}); -exports.setMode = deprecateFunction( - setMode, - 'require("private-browsing").activate and require("private-browsing").deactivate ' + - 'is deprecated.' -); - -let getMode = function getMode(chromeWin) { - if (isWindowPBEnabled(chromeWin)) - return getWindowLoadingContext(chromeWin).usePrivateBrowsing; - - // default - return pbService ? pbService.privateBrowsingEnabled : false; -}; -exports.getMode = getMode; - -exports.on = on.bind(null, exports); - -// Make sure listeners are cleaned up. -unload(function() off(exports)); diff --git a/tools/addon-sdk-1.12/lib/sdk/querystring.js b/tools/addon-sdk-1.12/lib/sdk/querystring.js deleted file mode 100644 index e0e2273..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/querystring.js +++ /dev/null @@ -1,121 +0,0 @@ -/* 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'; - -module.metadata = { - "stability": "unstable" -}; - -let unescape = decodeURIComponent; -exports.unescape = unescape; - -// encodes a string safely for application/x-www-form-urlencoded -// adheres to RFC 3986 -// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURIComponent -function escape(query) { - return encodeURIComponent(query). - replace(/%20/g, '+'). - replace(/!/g, '%21'). - replace(/'/g, '%27'). - replace(/\(/g, '%28'). - replace(/\)/g, '%29'). - replace(/\*/g, '%2A'); -} -exports.escape = escape; - -// Converts an object of unordered key-vals to a string that can be passed -// as part of a request -function stringify(options, separator, assigner) { - separator = separator || '&'; - assigner = assigner || '='; - // Explicitly return null if we have null, and empty string, or empty object. - if (!options) - return ''; - - // If content is already a string, just return it as is. - if (typeof(options) == 'string') - return options; - - // At this point we have a k:v object. Iterate over it and encode each value. - // Arrays and nested objects will get encoded as needed. For example... - // - // { foo: [1, 2, { omg: 'bbq', 'all your base!': 'are belong to us' }], bar: 'baz' } - // - // will be encoded as - // - // foo[0]=1&foo[1]=2&foo[2][omg]=bbq&foo[2][all+your+base!]=are+belong+to+us&bar=baz - // - // Keys (including '[' and ']') and values will be encoded with - // `escape` before returning. - // - // Execution was inspired by jQuery, but some details have changed and numeric - // array keys are included (whereas they are not in jQuery). - - let encodedContent = []; - function add(key, val) { - encodedContent.push(escape(key) + assigner + escape(val)); - } - - function make(key, value) { - if (value && typeof(value) === 'object') - Object.keys(value).forEach(function(name) { - make(key + '[' + name + ']', value[name]); - }); - else - add(key, value); - } - - Object.keys(options).forEach(function(name) { make(name, options[name]); }); - return encodedContent.join(separator); - - //XXXzpao In theory, we can just use a FormData object on 1.9.3, but I had - // trouble getting that working. It would also be nice to stay - // backwards-compat as long as possible. Keeping this in for now... - // let formData = Cc['@mozilla.org/files/formdata;1']. - // createInstance(Ci.nsIDOMFormData); - // for ([k, v] in Iterator(content)) { - // formData.append(k, v); - // } - // return formData; -} -exports.stringify = stringify; - -// Exporting aliases that nodejs implements just for the sake of -// interoperability. -exports.encode = stringify; -exports.serialize = stringify; - -// Note: That `stringify` and `parse` aren't bijective as we use `stringify` -// as it was implement in request module, but implement `parse` to match nodejs -// behavior. -// TODO: Make `stringify` implement API as in nodejs and figure out backwards -// compatibility. -function parse(query, separator, assigner) { - separator = separator || '&'; - assigner = assigner || '='; - let result = {}; - - if (typeof query !== 'string' || query.length === 0) - return result; - - query.split(separator).forEach(function(chunk) { - let pair = chunk.split(assigner); - let key = unescape(pair[0]); - let value = unescape(pair.slice(1).join(assigner)); - - if (!(key in result)) - result[key] = value; - else if (Array.isArray(result[key])) - result[key].push(value); - else - result[key] = [result[key], value]; - }); - - return result; -}; -exports.parse = parse; -// Exporting aliases that nodejs implements just for the sake of -// interoperability. -exports.decode = parse; diff --git a/tools/addon-sdk-1.12/lib/sdk/request.js b/tools/addon-sdk-1.12/lib/sdk/request.js deleted file mode 100644 index da5be7f..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/request.js +++ /dev/null @@ -1,213 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "stable" -}; - -const { ns } = require("./core/namespace"); -const { emit } = require("./event/core"); -const { merge } = require("./util/object"); -const { stringify } = require("./querystring"); -const { EventTarget } = require("./event/target"); -const { Class } = require("./core/heritage"); -const { XMLHttpRequest } = require("./net/xhr"); -const apiUtils = require("./deprecated/api-utils"); - -const response = ns(); -const request = ns(); - -// Instead of creating a new validator for each request, just make one and -// reuse it. -const { validateOptions, validateSingleOption } = new OptionsValidator({ - url: { - //XXXzpao should probably verify that url is a valid url as well - is: ["string"] - }, - headers: { - map: function (v) v || {}, - is: ["object"], - }, - content: { - map: function (v) v || null, - is: ["string", "object", "null"], - }, - contentType: { - map: function (v) v || "application/x-www-form-urlencoded", - is: ["string"], - }, - overrideMimeType: { - map: function(v) v || null, - is: ["string", "null"], - } -}); - -const REUSE_ERROR = "This request object has been used already. You must " + - "create a new one to make a new request." - -// Utility function to prep the request since it's the same between GET and -// POST -function runRequest(mode, target) { - let source = request(target) - let { xhr, url, content, contentType, headers, overrideMimeType } = source; - - // If this request has already been used, then we can't reuse it. - // Throw an error. - if (xhr) - throw new Error(REUSE_ERROR); - - xhr = source.xhr = new XMLHttpRequest(); - - // Build the data to be set. For GET requests, we want to append that to - // the URL before opening the request. - let data = stringify(content); - // If the URL already has ? in it, then we want to just use & - if (mode == "GET" && data) - url = url + (/\?/.test(url) ? "&" : "?") + data; - - // open the request - xhr.open(mode, url); - - // request header must be set after open, but before send - xhr.setRequestHeader("Content-Type", contentType); - - // set other headers - Object.keys(headers).forEach(function(name) { - xhr.setRequestHeader(name, headers[name]); - }); - - // set overrideMimeType - if (overrideMimeType) - xhr.overrideMimeType(overrideMimeType); - - // handle the readystate, create the response, and call the callback - xhr.onreadystatechange = function onreadystatechange() { - if (xhr.readyState === 4) { - let response = Response(xhr); - source.response = response; - emit(target, 'complete', response); - } - }; - - // actually send the request. - // We don't want to send data on GET requests. - xhr.send(mode !== "GET" ? data : null); -} - -const Request = Class({ - extends: EventTarget, - initialize: function initialize(options) { - // `EventTarget.initialize` will set event listeners that are named - // like `onEvent` in this case `onComplete` listener will be set to - // `complete` event. - EventTarget.prototype.initialize.call(this, options); - - // Copy normalized options. - merge(request(this), validateOptions(options)); - }, - get url() { return request(this).url; }, - set url(value) { request(this).url = validateSingleOption('url', value); }, - get headers() { return request(this).headers; }, - set headers(value) { - return request(this).headers = validateSingleOption('headers', value); - }, - get content() { return request(this).content; }, - set content(value) { - request(this).content = validateSingleOption('content', value); - }, - get contentType() { return request(this).contentType; }, - set contentType(value) { - request(this).contentType = validateSingleOption('contentType', value); - }, - get response() { return request(this).response; }, - get: function() { - runRequest('GET', this); - return this; - }, - post: function() { - runRequest('POST', this); - return this; - }, - put: function() { - runRequest('PUT', this); - return this; - } -}); -exports.Request = Request; - -const Response = Class({ - initialize: function initialize(request) { - response(this).request = request; - }, - get text() response(this).request.responseText, - get xml() { - throw new Error("Sorry, the 'xml' property is no longer available. " + - "see bug 611042 for more information."); - }, - get status() response(this).request.status, - get statusText() response(this).request.statusText, - get json() { - try { - return JSON.parse(this.text); - } catch(error) { - return null; - } - }, - get headers() { - let headers = {}, lastKey; - // Since getAllResponseHeaders() will return null if there are no headers, - // defend against it by defaulting to "" - let rawHeaders = response(this).request.getAllResponseHeaders() || ""; - rawHeaders.split("\n").forEach(function (h) { - // According to the HTTP spec, the header string is terminated by an empty - // line, so we can just skip it. - if (!h.length) { - return; - } - - let index = h.indexOf(":"); - // The spec allows for leading spaces, so instead of assuming a single - // leading space, just trim the values. - let key = h.substring(0, index).trim(), - val = h.substring(index + 1).trim(); - - // For empty keys, that means that the header value spanned multiple lines. - // In that case we should append the value to the value of lastKey with a - // new line. We'll assume lastKey will be set because there should never - // be an empty key on the first pass. - if (key) { - headers[key] = val; - lastKey = key; - } - else { - headers[lastKey] += "\n" + val; - } - }); - return headers; - } -}); - -// apiUtils.validateOptions doesn't give the ability to easily validate single -// options, so this is a wrapper that provides that ability. -function OptionsValidator(rules) { - return { - validateOptions: function (options) { - return apiUtils.validateOptions(options, rules); - }, - validateSingleOption: function (field, value) { - // We need to create a single rule object from our listed rules. To avoid - // JavaScript String warnings, check for the field & default to an empty object. - let singleRule = {}; - if (field in rules) { - singleRule[field] = rules[field]; - } - let singleOption = {}; - singleOption[field] = value; - // This should throw if it's invalid, which will bubble up & out. - return apiUtils.validateOptions(singleOption, singleRule)[field]; - } - }; -} diff --git a/tools/addon-sdk-1.12/lib/sdk/selection.js b/tools/addon-sdk-1.12/lib/sdk/selection.js deleted file mode 100644 index da94efe..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/selection.js +++ /dev/null @@ -1,481 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "stable" -}; - -if (!require("./system/xul-app").is("Firefox")) { - throw new Error([ - "The selection module currently supports only Firefox. In the future ", - "we would like it to support other applications, however. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." - ].join("")); -} - -let { Ci, Cc } = require("chrome"), - { setTimeout } = require("./timers"), - { emit, off } = require("./event/core"), - { Unknown } = require("./platform/xpcom"), - { Class, obscure } = require("./core/heritage"), - { EventTarget } = require("./event/target"), - observers = require("./deprecated/observer-service"), - { ns } = require("./core/namespace"); - -// When a document is not visible anymore the selection object is detached, and -// a new selection object is created when it becomes visible again. -// That makes the previous selection's listeners added previously totally -// useless – the listeners are not notified anymore. -// To fix that we're listening for `document-shown` event in order to add -// the listeners to the new selection object created. -// -// See bug 665386 for further details. - -let selections = ns(); - -observers.add("document-shown", function (document) { - var window = document.defaultView; - - // We are not interested in documents without valid defaultView - if (!window) - return; - - let selection = selections(window).selection; - - // We want to handle only the windows where we added selection's listeners - if (selection) { - let currentSelection = window.getSelection(); - - // If the current selection for the window given is different from the one - // stored in the namespace, we need to add the listeners again, and replace - // the previous selection in our list with the new one. - // - // Notice that we don't have to remove the listeners from the old selection, - // because is detached. An attempt to remove the listener, will raise an - // error (see http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsSelection.cpp#5343 ) - // - // We ensure that the current selection is an instance of - // `nsISelectionPrivate` before working on it, in case is `null`. - if (currentSelection instanceof Ci.nsISelectionPrivate && - currentSelection !== selection) { - - currentSelection.addSelectionListener(SelectionListenerManager); - selections(window).selection = currentSelection; - } - } -}); - -const windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"]. - getService(Ci.nsIWindowMediator); - -// The selection type HTML -const HTML = 0x01; - -// The selection type TEXT -const TEXT = 0x02; - -// The selection type DOM (internal use only) -const DOM = 0x03; - -// A more developer-friendly message than the caught exception when is not -// possible change a selection. -const ERR_CANNOT_CHANGE_SELECTION = - "It isn't possible to change the selection, as there isn't currently a selection"; - -const Selection = Class({ - /** - * Creates an object from which a selection can be set, get, etc. Each - * object has an associated with a range number. Range numbers are the - * 0-indexed counter of selection ranges as explained at - * https://developer.mozilla.org/en/DOM/Selection. - * - * @param rangeNumber - * The zero-based range index into the selection - */ - initialize: function initialize(rangeNumber) { - // In order to hide the private `rangeNumber` argument from API consumers - // while still enabling Selection getters/setters to access it, we define - // it as non enumerable, non configurable property. While consumers still - // may discover it they won't be able to do any harm which is good enough - // in this case. - Object.defineProperties(this, { - rangeNumber: { - enumerable: false, - configurable: false, - value: rangeNumber - } - }); - }, - get text() { return getSelection(TEXT, this.rangeNumber); }, - set text(value) { setSelection(value, this.rangeNumber); }, - get html() { return getSelection(HTML, this.rangeNumber); }, - set html(value) { setSelection(value, this.rangeNumber); }, - get isContiguous() { - let selection = getSelection(DOM); - - // If there are multiple ranges, the selection is definitely discontiguous. - // It returns `false` also if there are no selection; and `true` if there is - // a single non empty range, or a selection in a text field - contiguous or - // not (text field selection APIs doesn't support multiple selections). - - if (selection.rangeCount > 1) - return false; - - return !!(safeGetRange(selection, 0) || getElementWithSelection()); - } -}); - -/** - * Returns the most recent content window - */ -function context() { - // Overlay names should probably go into the xul-app module instead of here - return windowMediator.getMostRecentWindow("navigator:browser").document. - commandDispatcher.focusedWindow; -} - -/** - * Returns the current selection from most recent content window. Depending on - * the specified |type|, the value returned can be a string of text, stringified - * HTML, or a DOM selection object as described at - * https://developer.mozilla.org/en/DOM/Selection. - * - * @param type - * Specifies the return type of the selection. Valid values are the one - * of the constants HTML, TEXT, or DOM. - * - * @param rangeNumber - * Specifies the zero-based range index of the returned selection. - */ -function getSelection(type, rangeNumber) { - let window, selection; - try { - window = context(); - selection = window.getSelection(); - } - catch (e) { - return null; - } - - // Get the selected content as the specified type - if (type == DOM) - return selection; - else if (type == TEXT) { - let range = safeGetRange(selection, rangeNumber); - - if (range) - return range.toString(); - - let node = getElementWithSelection(window); - - if (!node) - return null; - - return node.value.substring(node.selectionStart, node.selectionEnd); - } - else if (type == HTML) { - let range = safeGetRange(selection, rangeNumber); - // Another way, but this includes the xmlns attribute for all elements in - // Gecko 1.9.2+ : - // return Cc["@mozilla.org/xmlextras/xmlserializer;1"]. - // createInstance(Ci.nsIDOMSerializer).serializeToSTring(range. - // cloneContents()); - if (!range) - return null; - let node = window.document.createElement("span"); - node.appendChild(range.cloneContents()); - return node.innerHTML; - } - throw new Error("Type " + type + " is unrecognized."); -} - -/** - * Returns the specified range in a selection without throwing an exception. - * - * @param selection - * A selection object as described at - * https://developer.mozilla.org/en/DOM/Selection - * - * @param rangeNumber - * Specifies the zero-based range index of the returned selection. - */ -function safeGetRange(selection, rangeNumber) { - try { - let range = selection.getRangeAt(rangeNumber); - if (!range || range.toString() == "") - return null; - return range; - } - catch (e) { - return null; - } -} - -/** - * Returns a reference of the DOM's active element for the window given, if it - * supports the text field selection API and has a text selected. - * - * Note: - * we need this method because window.getSelection doesn't return a selection - * for text selected in a form field (see bug 85686) - * - * @param {nsIWindow} [window] - * A reference to a window - */ -function getElementWithSelection(window) { - let element; - - try { - element = (window || context()).document.activeElement; - } - catch (e) { - element = null; - } - - if (!element) - return null; - - let { value, selectionStart, selectionEnd } = element; - - let hasSelection = typeof value === "string" && - !isNaN(selectionStart) && - !isNaN(selectionEnd) && - selectionStart !== selectionEnd; - - return hasSelection ? element : null; -} -/** - * Sets the current selection of the most recent content document by changing - * the existing selected text/HTML range to the specified value. - * - * @param val - * The value for the new selection - * - * @param rangeNumber - * The zero-based range index of the selection to be set - * - */ -function setSelection(val, rangeNumber) { - // Make sure we have a window context & that there is a current selection. - // Selection cannot be set unless there is an existing selection. - let window, selection; - - try { - window = context(); - selection = window.getSelection(); - } - catch (e) { - throw new Error(ERR_CANNOT_CHANGE_SELECTION); - } - - let range = safeGetRange(selection, rangeNumber); - - if (range) { - // Get rid of the current selection and insert our own - range.deleteContents(); - let node = window.document.createElement("span"); - range.surroundContents(node); - - // Some relevant JEP-111 requirements: - - // Setting the text property replaces the selection with the value to - // which the property is set and sets the html property to the same value - // to which the text property is being set. - - // Setting the html property replaces the selection with the value to - // which the property is set and sets the text property to the text version - // of the HTML value. - - // This sets both the HTML and text properties. - node.innerHTML = val; - } else { - let node = getElementWithSelection(window); - - if (!node) - throw new Error(ERR_CANNOT_CHANGE_SELECTION); - - let { value, selectionStart, selectionEnd } = node; - - let newSelectionEnd = selectionStart + val.length; - - node.value = value.substring(0, selectionStart) + - val + - value.substring(selectionEnd, value.length); - - node.setSelectionRange(selectionStart, newSelectionEnd); - } -} - -function onLoad(event) { - SelectionListenerManager.onLoad(event); -} - -function onUnload(event) { - SelectionListenerManager.onUnload(event); -} - -function onSelect() { - SelectionListenerManager.onSelect(); -} - -let SelectionListenerManager = Class({ - extends: Unknown, - interfaces: [ 'nsISelectionListener' ], - /** - * This is the nsISelectionListener implementation. This function is called - * by Gecko when a selection is changed interactively. - * - * We only pay attention to the SELECTALL, KEYPRESS, and MOUSEUP selection - * reasons. All reasons are listed here: - * - * http://mxr.mozilla.org/mozilla1.9.2/source/content/base/public/ - * nsISelectionListener.idl - * - * The other reasons (NO_REASON, DRAG_REASON, MOUSEDOWN_REASON) aren't - * applicable to us. - */ - notifySelectionChanged: function notifySelectionChanged(document, selection, - reason) { - if (!["SELECTALL", "KEYPRESS", "MOUSEUP"].some(function(type) reason & - Ci.nsISelectionListener[type + "_REASON"]) || selection.toString() == "") - return; - - this.onSelect(); - }, - - onSelect : function onSelect() { - setTimeout(emit, 0, module.exports, "select"); - }, - - /** - * Part of the Tracker implementation. This function is called by the - * tabs module when a browser is being tracked. Often, that means a new tab - * has been opened, but it can also mean an addon has been installed while - * tabs are already opened. In that case, this function is called for those - * already-opened tabs. - * - * @param browser - * The browser being tracked - */ - onTrack: function onTrack(browser) { - browser.addEventListener("load", onLoad, true); - browser.addEventListener("unload", onUnload, true); - }, - - onLoad: function onLoad(event) { - // Nothing to do without a useful window - let window = event.target.defaultView; - if (!window) - return; - - // Wrap the add selection call with some number of setTimeout 0 because some - // reason it's possible to add a selection listener "too early". 2 sometimes - // works for gmail, and more consistently with 3, so make it 5 to be safe. - let count = 0; - let self = this; - function wrap(count, func) { - if (count-- > 0) - setTimeout(wrap, 0); - else - self.addSelectionListener(window); - } - wrap(); - }, - - addSelectionListener: function addSelectionListener(window) { - // Don't add the selection's listener more than once to the same window. - if ("selection" in selections(window)) - return; - - let selection = window.getSelection(); - - // We ensure that the current selection is an instance of - // `nsISelectionPrivate` before working on it, in case is `null`. - if (selection instanceof Ci.nsISelectionPrivate) { - selection.addSelectionListener(this); - - // nsISelectionListener implementation seems not fire a notification if - // a selection is in a text field, therefore we need to add a listener to - // window.onselect, that is fired only for text fields. - // For consistency, we add it only when the nsISelectionListener is added. - // - // https://developer.mozilla.org/en/DOM/window.onselect - window.addEventListener("select", onSelect, true); - - selections(window).selection = selection; - } - }, - - onUnload: function onUnload(event) { - // Nothing to do without a useful window - let window = event.target.defaultView; - if (!window) - return; - this.removeSelectionListener(window); - off(exports); - }, - - removeSelectionListener: function removeSelectionListener(window) { - // Don't remove the selection's listener to a window that wasn't handled. - if (!("selection" in selections(window))) - return; - - let selection = window.getSelection(); - - // We ensure that the current selection is an instance of - // `nsISelectionPrivate` before working on it, in case is `null`. - if (selection instanceof Ci.nsISelectionPrivate) { - selection.removeSelectionListener(this); - - window.removeEventListener("select", onSelect); - - delete selections(window).selection; - } - }, - - /** - * Part of the TabTracker implementation. This function is called by the - * tabs module when a browser is being untracked. Usually, that means a tab - * has been closed. - * - * @param browser - * The browser being untracked - */ - onUntrack: function onUntrack(browser) { - browser.removeEventListener("load", onLoad, true); - browser.removeEventListener("unload", onUnload, true); - } -})(); - -/** - * Install |SelectionListenerManager| as tab tracker in order to watch - * tab opening/closing - */ -require("./deprecated/tab-browser").Tracker(SelectionListenerManager); - -var SelectionIterator = Class(obscure({ - /** - * Exports an iterator so that discontiguous selections can be iterated. - * - * If discontiguous selections are in a text field, only the first one - * is returned because the text field selection APIs doesn't support - * multiple selections. - */ - __iterator__: function() { - let selection = getSelection(DOM); - let count = selection.rangeCount || (getElementWithSelection() ? 1 : 0); - - for (let i = 0; i < count; i++) - yield Selection(i); - } -})); - -var selection = Class({ - extends: EventTarget, - implements: [ Selection, SelectionIterator ] -})(0); - -module.exports = selection; diff --git a/tools/addon-sdk-1.12/lib/sdk/self.js b/tools/addon-sdk-1.12/lib/sdk/self.js deleted file mode 100644 index f9fca1e..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/self.js +++ /dev/null @@ -1,36 +0,0 @@ -/* vim:st=2:sts=2:sw=2: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { CC } = require('chrome'); -const { id, name, prefixURI, rootURI, - version, loadReason } = require('@loader/options'); - -const { readURISync } = require('./net/url'); - -const addonDataURI = prefixURI + name + '/data/'; - -function uri(path) { - return addonDataURI + (path || ''); -} - - -// Some XPCOM APIs require valid URIs as an argument for certain operations -// (see `nsILoginManager` for example). This property represents add-on -// associated unique URI string that can be used for that. -exports.uri = 'addon:' + id; -exports.id = id; -exports.name = name; -exports.loadReason = loadReason; -exports.version = version; -// If `rootURI` is jar:file://...!/ than add-on is packed. -exports.packed = rootURI.indexOf('jar:') === 0 -exports.data = Object.freeze({ - url: uri, - load: function read(path) { - return readURISync(uri(path)); - } -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/simple-prefs.js b/tools/addon-sdk-1.12/lib/sdk/simple-prefs.js deleted file mode 100644 index 5cbde49..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/simple-prefs.js +++ /dev/null @@ -1,32 +0,0 @@ -/* 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'; - -module.metadata = { - "stability": "experimental" -}; - -const { emit, off } = require("./event/core"); -const { when: unload } = require("./system/unload"); -const { PrefsTarget } = require("./preferences/event-target"); -const { id } = require("./self"); -const observers = require("./deprecated/observer-service"); - -const ADDON_BRANCH = "extensions." + id + "."; -const BUTTON_PRESSED = id + "-cmdPressed"; - -const target = PrefsTarget({ branchName: ADDON_BRANCH }); - -// Listen to clicks on buttons -function buttonClick(subject, data) { - emit(target, data); -} -observers.add(BUTTON_PRESSED, buttonClick); - -// Make sure we cleanup listeners on unload. -unload(function() { - observers.remove(BUTTON_PRESSED, buttonClick); -}); - -module.exports = target; diff --git a/tools/addon-sdk-1.12/lib/sdk/simple-storage.js b/tools/addon-sdk-1.12/lib/sdk/simple-storage.js deleted file mode 100644 index 2b88cc1..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/simple-storage.js +++ /dev/null @@ -1,237 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim:set ts=2 sw=2 sts=2 et filetype=javascript - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -module.metadata = { - "stability": "stable" -}; - -const { Cc, Ci } = require("chrome"); -const file = require("./io/file"); -const prefs = require("./preferences/service"); -const jpSelf = require("./self"); -const timer = require("./timers"); -const unload = require("./system/unload"); -const { emit, on, off } = require("./event/core"); - -const WRITE_PERIOD_PREF = "extensions.addon-sdk.simple-storage.writePeriod"; -const WRITE_PERIOD_DEFAULT = 300000; // 5 minutes - -const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota"; -const QUOTA_DEFAULT = 5242880; // 5 MiB - -const JETPACK_DIR_BASENAME = "jetpack"; - -Object.defineProperties(exports, { - storage: { - enumerable: true, - get: function() { return manager.root; }, - set: function(value) { manager.root = value; } - }, - quotaUsage: { - get: function() { return manager.quotaUsage; } - } -}); - -// A generic JSON store backed by a file on disk. This should be isolated -// enough to move to its own module if need be... -function JsonStore(options) { - this.filename = options.filename; - this.quota = options.quota; - this.writePeriod = options.writePeriod; - this.onOverQuota = options.onOverQuota; - this.onWrite = options.onWrite; - - unload.ensure(this); - - this.writeTimer = timer.setInterval(this.write.bind(this), - this.writePeriod); -} - -JsonStore.prototype = { - // The store's root. - get root() { - return this.isRootInited ? this._root : {}; - }, - - // Performs some type checking. - set root(val) { - let types = ["array", "boolean", "null", "number", "object", "string"]; - if (types.indexOf(typeof(val)) < 0) { - throw new Error("storage must be one of the following types: " + - types.join(", ")); - } - this._root = val; - return val; - }, - - // True if the root has ever been set (either via the root setter or by the - // backing file's having been read). - get isRootInited() { - return this._root !== undefined; - }, - - // Percentage of quota used, as a number [0, Inf). > 1 implies over quota. - // Undefined if there is no quota. - get quotaUsage() { - return this.quota > 0 ? - JSON.stringify(this.root).length / this.quota : - undefined; - }, - - // Removes the backing file and all empty subdirectories. - purge: function JsonStore_purge() { - try { - // This'll throw if the file doesn't exist. - file.remove(this.filename); - let parentPath = this.filename; - do { - parentPath = file.dirname(parentPath); - // This'll throw if the dir isn't empty. - file.rmdir(parentPath); - } while (file.basename(parentPath) !== JETPACK_DIR_BASENAME); - } - catch (err) {} - }, - - // Initializes the root by reading the backing file. - read: function JsonStore_read() { - try { - let str = file.read(this.filename); - - // Ideally we'd log the parse error with console.error(), but logged - // errors cause tests to fail. Supporting "known" errors in the test - // harness appears to be non-trivial. Maybe later. - this.root = JSON.parse(str); - } - catch (err) { - this.root = {}; - } - }, - - // If the store is under quota, writes the root to the backing file. - // Otherwise quota observers are notified and nothing is written. - write: function JsonStore_write() { - if (this.quotaUsage > 1) - this.onOverQuota(this); - else - this._write(); - }, - - // Cleans up on unload. If unloading because of uninstall, the store is - // purged; otherwise it's written. - unload: function JsonStore_unload(reason) { - timer.clearInterval(this.writeTimer); - this.writeTimer = null; - - if (reason === "uninstall") - this.purge(); - else - this._write(); - }, - - // True if the root is an empty object. - get _isEmpty() { - if (this.root && typeof(this.root) === "object") { - let empty = true; - for (let key in this.root) { - empty = false; - break; - } - return empty; - } - return false; - }, - - // Writes the root to the backing file, notifying write observers when - // complete. If the store is over quota or if it's empty and the store has - // never been written, nothing is written and write observers aren't notified. - _write: function JsonStore__write() { - // Don't write if the root is uninitialized or if the store is empty and the - // backing file doesn't yet exist. - if (!this.isRootInited || (this._isEmpty && !file.exists(this.filename))) - return; - - // If the store is over quota, don't write. The current under-quota state - // should persist. - if (this.quotaUsage > 1) - return; - - // Finally, write. - let stream = file.open(this.filename, "w"); - try { - stream.writeAsync(JSON.stringify(this.root), function writeAsync(err) { - if (err) - console.error("Error writing simple storage file: " + this.filename); - else if (this.onWrite) - this.onWrite(this); - }.bind(this)); - } - catch (err) { - // writeAsync closes the stream after it's done, so only close on error. - stream.close(); - } - } -}; - - -// This manages a JsonStore singleton and tailors its use to simple storage. -// The root of the JsonStore is lazy-loaded: The backing file is only read the -// first time the root's gotten. -let manager = ({ - jsonStore: null, - - // The filename of the store, based on the profile dir and extension ID. - get filename() { - let storeFile = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties). - get("ProfD", Ci.nsIFile); - storeFile.append(JETPACK_DIR_BASENAME); - storeFile.append(jpSelf.id); - storeFile.append("simple-storage"); - file.mkpath(storeFile.path); - storeFile.append("store.json"); - return storeFile.path; - }, - - get quotaUsage() { - return this.jsonStore.quotaUsage; - }, - - get root() { - if (!this.jsonStore.isRootInited) - this.jsonStore.read(); - return this.jsonStore.root; - }, - - set root(val) { - return this.jsonStore.root = val; - }, - - unload: function manager_unload() { - off(this); - }, - - new: function manager_constructor() { - let manager = Object.create(this); - unload.ensure(manager); - - manager.jsonStore = new JsonStore({ - filename: manager.filename, - writePeriod: prefs.get(WRITE_PERIOD_PREF, WRITE_PERIOD_DEFAULT), - quota: prefs.get(QUOTA_PREF, QUOTA_DEFAULT), - onOverQuota: emit.bind(null, exports, "OverQuota") - }); - - return manager; - } -}).new(); - -exports.on = on.bind(null, exports); -exports.removeListener = function(type, listener) { - off(exports, type, listener); -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/system.js b/tools/addon-sdk-1.12/lib/sdk/system.js deleted file mode 100644 index 1e374d3..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/system.js +++ /dev/null @@ -1,155 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 expandtab */ -/* 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'; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci, CC } = require('chrome'); -const options = require('@loader/options'); -const file = require('./io/file'); -const runtime = require("./system/runtime"); - -const appStartup = Cc['@mozilla.org/toolkit/app-startup;1']. - getService(Ci.nsIAppStartup); -const appInfo = Cc["@mozilla.org/xre/app-info;1"]. - getService(Ci.nsIXULAppInfo); -const directoryService = Cc['@mozilla.org/file/directory_service;1']. - getService(Ci.nsIProperties); - -const PR_WRONLY = parseInt("0x02"); -const PR_CREATE_FILE = parseInt("0x08"); -const PR_APPEND = parseInt("0x10"); -const PR_TRUNCATE = parseInt("0x20"); - -function openFile(path, mode) { - let file = Cc["@mozilla.org/file/local;1"]. - createInstance(Ci.nsILocalFile); - file.initWithPath(path); - let stream = Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(Ci.nsIFileOutputStream); - stream.init(file, mode, -1, 0); - return stream -} - -const { eAttemptQuit: E_ATTEMPT, eForceQuit: E_FORCE } = appStartup; - -/** - * Parsed JSON object that was passed via `cfx --static-args "{ foo: 'bar' }"` - */ -exports.staticArgs = options.staticArgs; - -/** - * Environment variables. Environment variables are non-enumerable properties - * of this object (key is name and value is value). - */ -exports.env = require('./system/environment').env; - -/** - * Ends the process with the specified `code`. If omitted, exit uses the - * 'success' code 0. To exit with failure use `1`. - * TODO: Improve platform to actually quit with an exit code. - */ -exports.exit = function exit(code) { - // This is used by 'cfx' to find out exit code. - if ('resultFile' in options && options.resultFile) { - let mode = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE; - let stream = openFile(options.resultFile, mode); - let status = code ? 'FAIL' : 'OK'; - stream.write(status, status.length); - stream.flush(); - stream.close(); - } - - appStartup.quit(code ? E_ATTEMPT : E_FORCE); -}; - -exports.stdout = new function() { - let write = dump - if ('logFile' in options && options.logFile) { - let mode = PR_WRONLY | PR_CREATE_FILE | PR_APPEND; - let stream = openFile(options.logFile, mode); - write = function write(data) { - let text = String(data); - stream.write(text, text.length); - stream.flush(); - } - } - return Object.freeze({ write: write }); -}; - -/** - * Returns a path of the system's or application's special directory / file - * associated with a given `id`. For list of possible `id`s please see: - * https://developer.mozilla.org/en/Code_snippets/File_I%2F%2FO#Getting_special_files - * http://mxr.mozilla.org/mozilla-central/source/xpcom/io/nsAppDirectoryServiceDefs.h - * @example - * - * // get firefox profile path - * let profilePath = require('system').pathFor('ProfD'); - * // get OS temp files directory (/tmp) - * let temps = require('system').pathFor('TmpD'); - * // get OS desktop path for an active user (~/Desktop on linux - * // or C:\Documents and Settings\username\Desktop on windows). - * let desktopPath = require('system').pathFor('Desk'); - */ -exports.pathFor = function pathFor(id) { - return directoryService.get(id, Ci.nsIFile).path; -}; - -/** - * What platform you're running on (all lower case string). - * For possible values see: - * https://developer.mozilla.org/en/OS_TARGET - */ -exports.platform = runtime.OS.toLowerCase(); - -/** - * What processor architecture you're running on: - * `'arm', 'ia32', or 'x64'`. - */ -exports.architecture = runtime.XPCOMABI.split('_')[0]; - -/** - * What compiler used for build: - * `'msvc', 'n32', 'gcc2', 'gcc3', 'sunc', 'ibmc'...` - */ -exports.compiler = runtime.XPCOMABI.split('_')[1]; - -/** - * The application's build ID/date, for example "2004051604". - */ -exports.build = appInfo.appBuildID; - -/** - * The XUL application's UUID. - * This has traditionally been in the form - * `{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}` but for some applications it may - * be: "appname@vendor.tld". - */ -exports.id = appInfo.ID; - -/** - * The name of the application. - */ -exports.name = appInfo.name; - -/** - * The XUL application's version, for example "0.8.0+" or "3.7a1pre". - */ -exports.version = appInfo.version; - -/** - * XULRunner version. - */ -exports.platformVersion = appInfo.platformVersion; - - -/** - * The name of the application vendor, for example "Mozilla". - */ -exports.vendor = appInfo.vendor; diff --git a/tools/addon-sdk-1.12/lib/sdk/system/environment.js b/tools/addon-sdk-1.12/lib/sdk/system/environment.js deleted file mode 100644 index 045bab1..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/system/environment.js +++ /dev/null @@ -1,58 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 expandtab */ -/* 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'; - -module.metadata = { - "stability": "stable" -}; - -const { Cc, Ci } = require('chrome'); -const { get, set, exists } = Cc['@mozilla.org/process/environment;1']. - getService(Ci.nsIEnvironment); - -exports.env = Proxy.create({ - // XPCOM does not provides a way to enumerate environment variables, so we - // just don't support enumeration. - getPropertyNames: function() [], - getOwnPropertyNames: function() [], - enumerate: function() [], - keys: function() [], - // We do not support freezing, cause it would make it impossible to set new - // environment variables. - fix: function() undefined, - // We present all environment variables as own properties of this object, - // so we just delegate this call to `getOwnPropertyDescriptor`. - getPropertyDescriptor: function(name) this.getOwnPropertyDescriptor(name), - // If environment variable with this name is defined, we generate proprety - // descriptor for it, otherwise fall back to `undefined` so that for consumer - // this property does not exists. - getOwnPropertyDescriptor: function(name) { - return !exists(name) ? undefined : { - value: get(name), - enumerable: false, // Non-enumerable as we don't support enumeration. - configurable: true, // Configurable as it may be deleted. - writable: true // Writable as we do support set. - } - }, - - // New environment variables can be defined just by defining properties - // on this object. - defineProperty: function(name, { value }) set(name, value), - delete: function(name) set(name, null), - - // We present all properties as own, there for we just delegate to `hasOwn`. - has: function(name) this.hasOwn(name), - // We do support checks for existence of an environment variable, via `in` - // operator on this object. - hasOwn: function(name) exists(name), - - // On property get / set we do read / write appropriate environment variables, - // please note though, that variables with names of standard object properties - // intentionally (so that this behaves as normal object) can not be - // read / set. - get: function(proxy, name) Object.prototype[name] || get(name) || undefined, - set: function(proxy, name, value) Object.prototype[name] || set(name, value) -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/system/events.js b/tools/addon-sdk-1.12/lib/sdk/system/events.js deleted file mode 100644 index a75dddc..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/system/events.js +++ /dev/null @@ -1,113 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci } = require('chrome'); -const { Unknown } = require('../platform/xpcom'); -const { Class } = require('../core/heritage'); -const { ns } = require('../core/namespace'); -const { addObserver, removeObserver, notifyObservers } = - Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); - -const Subject = Class({ - extends: Unknown, - initialize: function initialize(object) { - // Double-wrap the object and set a property identifying the - // wrappedJSObject as one of our wrappers to distinguish between - // subjects that are one of our wrappers (which we should unwrap - // when notifying our observers) and those that are real JS XPCOM - // components (which we should pass through unaltered). - this.wrappedJSObject = { - observersModuleSubjectWrapper: true, - object: object - }; - }, - getHelperForLanguage: function() {}, - getInterfaces: function() {} -}); - -function emit(type, event) { - let subject = 'subject' in event ? Subject(event.subject) : null; - let data = 'data' in event ? event.data : null; - notifyObservers(subject, type, data); -} -exports.emit = emit; - -const Observer = Class({ - extends: Unknown, - initialize: function initialize(listener) { - this.listener = listener; - }, - interfaces: [ 'nsIObserver', 'nsISupportsWeakReference' ], - observe: function(subject, topic, data) { - // Extract the wrapped object for subjects that are one of our - // wrappers around a JS object. This way we support both wrapped - // subjects created using this module and those that are real - // XPCOM components. - if (subject && typeof(subject) == 'object' && - ('wrappedJSObject' in subject) && - ('observersModuleSubjectWrapper' in subject.wrappedJSObject)) - subject = subject.wrappedJSObject.object; - - try { - this.listener({ - type: topic, - subject: subject, - data: data - }); - } - catch (error) { - console.exception(error); - } - } -}); - -const subscribers = ns(); - -function on(type, listener, strong) { - // Unless last optional argument is `true` we use a weak reference to a - // listener. - let weak = !strong; - // Take list of observers associated with given `listener` function. - let observers = subscribers(listener); - // If `observer` for the given `type` is not registered yet, then - // associate an `observer` and register it. - if (!(type in observers)) { - let observer = Observer(listener); - observers[type] = observer; - addObserver(observer, type, weak); - } -} -exports.on = on; - -function once(type, listener) { - // Note: this code assumes order in which listeners are called, which is fine - // as long as dispatch happens in same order as listener registration which - // is the case now. That being said we should be aware that this may break - // in a future if order will change. - on(type, listener); - on(type, function cleanup() { - off(type, listener); - off(type, cleanup); - }, true); -} -exports.once = once; - -function off(type, listener) { - // Take list of observers as with the given `listener`. - let observers = subscribers(listener); - // If `observer` for the given `type` is registered, then - // remove it & unregister. - if (type in observers) { - let observer = observers[type]; - delete observers[type]; - removeObserver(observer, type); - } -} -exports.off = off; diff --git a/tools/addon-sdk-1.12/lib/sdk/system/globals.js b/tools/addon-sdk-1.12/lib/sdk/system/globals.js deleted file mode 100644 index df77295..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/system/globals.js +++ /dev/null @@ -1,54 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 expandtab */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -let { Cc, Ci, CC } = require('chrome'); -let { PlainTextConsole } = require('../console/plain-text'); -let { stdout } = require('../system'); -let ScriptError = CC('@mozilla.org/scripterror;1', 'nsIScriptError'); -let consoleService = Cc['@mozilla.org/consoleservice;1'].getService(). - QueryInterface(Ci.nsIConsoleService); - -// On windows dump does not writes into stdout so cfx can't read thous dumps. -// To workaround this issue we write to a special file from which cfx will -// read and print to the console. -// For more details see: bug-673383 -exports.dump = stdout.write; - -// Bug 718230: We need to send console messages to stdout and JS Console -function forsakenConsoleDump(msg, level) { - stdout.write(msg); - - if (level === 'error') { - let error = ScriptError(); - msg = msg.replace(/^error: /, ''); - error.init(msg, null, null, 0, 0, 0, 'Add-on SDK'); - consoleService.logMessage(error); - } - else - consoleService.logStringMessage(msg); -}; -exports.console = new PlainTextConsole(forsakenConsoleDump); - -// Provide CommonJS `define` to allow authoring modules in a format that can be -// loaded both into jetpack and into browser via AMD loaders. -Object.defineProperty(exports, 'define', { - // `define` is provided as a lazy getter that binds below defined `define` - // function to the module scope, so that require, exports and module - // variables remain accessible. - configurable: true, - get: function() { - let sandbox = this; - return function define(factory) { - factory = Array.slice(arguments).pop(); - factory.call(sandbox, sandbox.require, sandbox.exports, sandbox.module); - } - } -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/system/runtime.js b/tools/addon-sdk-1.12/lib/sdk/system/runtime.js deleted file mode 100644 index e64b663..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/system/runtime.js +++ /dev/null @@ -1,19 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci } = require("chrome"); -const runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime); - -exports.inSafeMode = runtime.inSafeMode; -exports.OS = runtime.OS; -exports.processType = runtime.processType; -exports.widgetToolkit = runtime.widgetToolkit; -exports.XPCOMABI = runtime.XPCOMABI; diff --git a/tools/addon-sdk-1.12/lib/sdk/system/unload.js b/tools/addon-sdk-1.12/lib/sdk/system/unload.js deleted file mode 100644 index 4d2575b..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/system/unload.js +++ /dev/null @@ -1,82 +0,0 @@ -/* 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/. */ - -// Parts of this module were taken from narwhal: -// -// http://narwhaljs.org - -module.metadata = { - "stability": "experimental" -}; - -const { on, off } = require('./events'); -const unloadSubject = require('@loader/unload'); - -const observers = []; -const unloaders = []; - -var when = exports.when = function when(observer) { - if (observers.indexOf(observer) != -1) - return; - observers.unshift(observer); -}; - -var ensure = exports.ensure = function ensure(obj, destructorName) { - if (!destructorName) - destructorName = "unload"; - if (!(destructorName in obj)) - throw new Error("object has no '" + destructorName + "' property"); - - let called = false; - let originalDestructor = obj[destructorName]; - - function unloadWrapper(reason) { - if (!called) { - called = true; - let index = unloaders.indexOf(unloadWrapper); - if (index == -1) - throw new Error("internal error: unloader not found"); - unloaders.splice(index, 1); - originalDestructor.call(obj, reason); - originalDestructor = null; - destructorName = null; - obj = null; - } - }; - - // TODO: Find out why the order is inverted here. It seems that - // it may be causing issues! - unloaders.push(unloadWrapper); - - obj[destructorName] = unloadWrapper; -}; - -function unload(reason) { - observers.forEach(function(observer) { - try { - observer(reason); - } - catch (error) { - console.exception(error); - } - }); -} - -when(function(reason) { - unloaders.slice().forEach(function(unloadWrapper) { - unloadWrapper(reason); - }); -}); - -on('sdk:loader:destroy', function onunload({ subject, data: reason }) { - // If this loader is unload then `subject.wrappedJSObject` will be - // `destructor`. - if (subject.wrappedJSObject === unloadSubject) { - off('sdk:loader:destroy', onunload); - unload(reason); - } -// Note that we use strong reference to listener here to make sure it's not -// GC-ed, which may happen otherwise since nothing keeps reference to `onunolad` -// function. -}, true); diff --git a/tools/addon-sdk-1.12/lib/sdk/system/xul-app.js b/tools/addon-sdk-1.12/lib/sdk/system/xul-app.js deleted file mode 100644 index 6e8b4e8..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/system/xul-app.js +++ /dev/null @@ -1,67 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "experimental" -}; - -const {Cc, Ci} = require("chrome"); - -var appInfo = Cc["@mozilla.org/xre/app-info;1"] - .getService(Ci.nsIXULAppInfo); - -var ID = exports.ID = appInfo.ID; -var name = exports.name = appInfo.name; -var version = exports.version = appInfo.version; -var platformVersion = exports.platformVersion = appInfo.platformVersion; - -// The following mapping of application names to GUIDs was taken from: -// -// https://addons.mozilla.org/en-US/firefox/pages/appversions -// -// Using the GUID instead of the app's name is preferable because sometimes -// re-branded versions of a product have different names: for instance, -// Firefox, Minefield, Iceweasel, and Shiretoko all have the same -// GUID. -// This mapping is duplicated in `app-extensions/bootstrap.js`. They should keep -// in sync, so if you change one, change the other too! - -var ids = exports.ids = { - Firefox: "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}", - Mozilla: "{86c18b42-e466-45a9-ae7a-9b95ba6f5640}", - Sunbird: "{718e30fb-e89b-41dd-9da7-e25a45638b28}", - SeaMonkey: "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}", - Fennec: "{aa3c5121-dab2-40e2-81ca-7ea25febc110}", - Thunderbird: "{3550f703-e582-4d05-9a08-453d09bdfdc6}" -}; - -var is = exports.is = function is(name) { - if (!(name in ids)) - throw new Error("Unkown Mozilla Application: " + name); - return ID == ids[name]; -}; - -var isOneOf = exports.isOneOf = function isOneOf(names) { - for (var i = 0; i < names.length; i++) - if (is(names[i])) - return true; - return false; -}; - -/** - * Use this to check whether the given version (e.g. xulApp.platformVersion) - * is in the given range. Versions must be in version comparator-compatible - * format. See MDC for details: - * https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIVersionComparator - */ -var versionInRange = exports.versionInRange = -function versionInRange(version, lowInclusive, highExclusive) { - var vc = Cc["@mozilla.org/xpcom/version-comparator;1"] - .getService(Ci.nsIVersionComparator); - return (vc.compare(version, lowInclusive) >= 0) && - (vc.compare(version, highExclusive) < 0); -} - diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs.js b/tools/addon-sdk-1.12/lib/sdk/tabs.js deleted file mode 100644 index f006073..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs.js +++ /dev/null @@ -1,10 +0,0 @@ -/* 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'; - -module.metadata = { - 'stability': 'stable' -}; - -module.exports = require('./tabs/tabs'); diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/common.js b/tools/addon-sdk-1.12/lib/sdk/tabs/common.js deleted file mode 100644 index dd5bb22..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/common.js +++ /dev/null @@ -1,23 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { validateOptions } = require('../deprecated/api-utils'); - -function Options(options) { - if ('string' === typeof options) - options = { url: options }; - - return validateOptions(options, { - url: { is: ["string"] }, - inBackground: { is: ["undefined", "boolean"] }, - isPinned: { is: ["undefined", "boolean"] }, - onOpen: { is: ["undefined", "function"] }, - onClose: { is: ["undefined", "function"] }, - onReady: { is: ["undefined", "function"] }, - onActivate: { is: ["undefined", "function"] }, - onDeactivate: { is: ["undefined", "function"] } - }); -} -exports.Options = Options; diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/events.js b/tools/addon-sdk-1.12/lib/sdk/tabs/events.js deleted file mode 100644 index eb99f40..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/events.js +++ /dev/null @@ -1,30 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const ON_PREFIX = "on"; -const TAB_PREFIX = "Tab"; - -const EVENTS = { - ready: "DOMContentLoaded", - open: "TabOpen", - close: "TabClose", - activate: "TabSelect", - deactivate: null, - pinned: "TabPinned", - unpinned: "TabUnpinned" -} -exports.EVENTS = EVENTS; - -Object.keys(EVENTS).forEach(function(name) { - EVENTS[name] = { - name: name, - listener: ON_PREFIX + name.charAt(0).toUpperCase() + name.substr(1), - dom: EVENTS[name] - } -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/helpers.js b/tools/addon-sdk-1.12/lib/sdk/tabs/helpers.js deleted file mode 100644 index aaff5cb..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/helpers.js +++ /dev/null @@ -1,17 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { getTabForContentWindow } = require('./utils'); -const { Tab } = require('./tab'); - -function getTabForWindow(win) { - let tab = getTabForContentWindow(win); - // We were unable to find the related tab! - if (!tab) - return null; - - return Tab({ tab: tab }); -} -exports.getTabForWindow = getTabForWindow; diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/namespace.js b/tools/addon-sdk-1.12/lib/sdk/tabs/namespace.js deleted file mode 100644 index d359cee..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/namespace.js +++ /dev/null @@ -1,9 +0,0 @@ -/* 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'; - -let { ns } = require('../core/namespace'); - -exports.tabsNS = ns(); -exports.tabNS = ns(); diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/observer.js b/tools/addon-sdk-1.12/lib/sdk/tabs/observer.js deleted file mode 100644 index 19a048c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/observer.js +++ /dev/null @@ -1,99 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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'; - -module.metadata = { - "stability": "unstable" -}; - -const { EventEmitterTrait: EventEmitter } = require("../deprecated/events"); -const { DOMEventAssembler } = require("../deprecated/events/assembler"); -const { Trait } = require("../deprecated/light-traits"); -const { getActiveTab, getTabs, getTabContainer } = require("./utils"); -const { browserWindowIterator } = require("../deprecated/window-utils"); -const { isBrowser } = require('../window/utils'); -const { observer: windowObserver } = require("../windows/observer"); - -const EVENTS = { - "TabOpen": "open", - "TabClose": "close", - "TabSelect": "select", - "TabMove": "move", - "TabPinned": "pinned", - "TabUnpinned": "unpinned" -}; - - -// Event emitter objects used to register listeners and emit events on them -// when they occur. -const observer = Trait.compose(DOMEventAssembler, EventEmitter).create({ - /** - * Method is implemented by `EventEmitter` and is used just for emitting - * events on registered listeners. - */ - _emit: Trait.required, - /** - * Events that are supported and emitted by the module. - */ - supportedEventsTypes: Object.keys(EVENTS), - /** - * Function handles all the supported events on all the windows that are - * observed. Method is used to proxy events to the listeners registered on - * this event emitter. - * @param {Event} event - * Keyboard event being emitted. - */ - handleEvent: function handleEvent(event) { - this._emit(EVENTS[event.type], event.target, event); - } -}); - -// Currently Gecko does not dispatch any event on the previously selected -// tab before / after "TabSelect" is dispatched. In order to work around this -// limitation we keep track of selected tab and emit "deactivate" event with -// that before emitting "activate" on selected tab. -var selectedTab = null; -function onTabSelect(tab) { - if (selectedTab !== tab) { - if (selectedTab) observer._emit('deactivate', selectedTab); - if (tab) observer._emit('activate', selectedTab = tab); - } -}; -observer.on('select', onTabSelect); - -// We also observe opening / closing windows in order to add / remove it's -// containers to the observed list. -function onWindowOpen(chromeWindow) { - if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window. - observer.observe(getTabContainer(chromeWindow)); -} -windowObserver.on("open", onWindowOpen); - -function onWindowClose(chromeWindow) { - if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window. - // Bug 751546: Emit `deactivate` event on window close immediatly - // Otherwise we are going to face "dead object" exception on `select` event - if (getActiveTab(chromeWindow) == selectedTab) { - observer._emit("deactivate", selectedTab); - selectedTab = null; - } - observer.ignore(getTabContainer(chromeWindow)); -} -windowObserver.on("close", onWindowClose); - - -// Currently gecko does not dispatches "TabSelect" events when different -// window gets activated. To work around this limitation we emulate "select" -// event for this case. -windowObserver.on("activate", function onWindowActivate(chromeWindow) { - if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window. - observer._emit("select", getActiveTab(chromeWindow)); -}); - -// We should synchronize state, since probably we already have at least one -// window open. -for each (let window in browserWindowIterator()) onWindowOpen(window); - -exports.observer = observer; diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/tab-fennec.js b/tools/addon-sdk-1.12/lib/sdk/tabs/tab-fennec.js deleted file mode 100644 index 42bf91b..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/tab-fennec.js +++ /dev/null @@ -1,141 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Cc, Ci } = require('chrome'); -const { Class } = require('../core/heritage'); -const { tabNS } = require('./namespace'); -const { EventTarget } = require('../event/target'); -const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, - setTabURL, getOwnerWindow, getTabContentType } = require('./utils'); -const { emit } = require('../event/core'); -const { when: unload } = require('../system/unload'); - -const { EVENTS } = require('./events'); -const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec'; - -const Tab = Class({ - extends: EventTarget, - initialize: function initialize(options) { - options = options.tab ? options : { tab: options }; - - EventTarget.prototype.initialize.call(this, options); - let tabInternals = tabNS(this); - - tabInternals.window = options.window || getOwnerWindow(options.tab); - tabInternals.tab = options.tab; - }, - - /** - * The title of the page currently loaded in the tab. - * Changing this property changes an actual title. - * @type {String} - */ - get title() getTabTitle(tabNS(this).tab), - set title(title) setTabTitle(tabNS(this).tab, title), - - /** - * Location of the page currently loaded in this tab. - * Changing this property will loads page under under the specified location. - * @type {String} - */ - get url() getTabURL(tabNS(this).tab), - set url(url) setTabURL(tabNS(this).tab, url), - - /** - * URI of the favicon for the page currently loaded in this tab. - * @type {String} - */ - get favicon() { - // TODO: provide the real favicon when it is available - console.error(ERR_FENNEC_MSG); - - // return 16x16 blank default - return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAEklEQVQ4jWNgGAWjYBSMAggAAAQQAAF/TXiOAAAAAElFTkSuQmCC'; - }, - - getThumbnail: function() { - // TODO: implement! - console.error(ERR_FENNEC_MSG); - - // return 80x45 blank default - return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAtCAYAAAA5reyyAAAAJElEQVRoge3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAADXBjhtAAGQ0AF/AAAAAElFTkSuQmCC'; - }, - - /** - * The index of the tab relative to other tabs in the application window. - * Changing this property will change order of the actual position of the tab. - * @type {Number} - */ - get index() { - let tabs = tabNS(this).window.BrowserApp.tabs; - let tab = tabNS(this).tab; - for (var i = tabs.length; i >= 0; i--) { - if (tabs[i] === tab) - return i; - } - return null; - }, - set index(value) { - console.error(ERR_FENNEC_MSG); // TODO - }, - - /** - * Whether or not tab is pinned (Is an app-tab). - * @type {Boolean} - */ - get isPinned() { - console.error(ERR_FENNEC_MSG); // TODO - return false; // TODO - }, - pin: function pin() { - console.error(ERR_FENNEC_MSG); // TODO - }, - unpin: function unpin() { - console.error(ERR_FENNEC_MSG); // TODO - }, - - /** - * Returns the MIME type that the document loaded in the tab is being - * rendered as. - * @type {String} - */ - get contentType() getTabContentType(tabNS(this).tab), - - /** - * Create a worker for this tab, first argument is options given to Worker. - * @type {Worker} - */ - attach: function attach(options) { - // BUG 792946 https://bugzilla.mozilla.org/show_bug.cgi?id=792946 - // TODO: fix this circular dependency - let { Worker } = require('./worker'); - return Worker(options, tabNS(this).tab.browser.contentWindow); - }, - - /** - * Make this tab active. - */ - activate: function activate() { - activateTab(tabNS(this).tab, tabNS(this).window); - }, - - /** - * Close the tab - */ - close: function close(callback) { - if (callback) - this.once(EVENTS.close.name, callback); - - closeTab(tabNS(this).tab); - }, - - /** - * Reload the tab - */ - reload: function reload() { - tabNS(this).tab.browser.reload(); - } -}); -exports.Tab = Tab; diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/tab-firefox.js b/tools/addon-sdk-1.12/lib/sdk/tabs/tab-firefox.js deleted file mode 100644 index 5a36302..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/tab-firefox.js +++ /dev/null @@ -1,201 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Trait } = require("../deprecated/traits"); -const { EventEmitter } = require("../deprecated/events"); -const { defer } = require("../lang/functional"); -const { EVENTS } = require("./events"); -const { getThumbnailURIForWindow } = require("../content/thumbnail"); -const { getFaviconURIForLocation } = require("../io/data"); -const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, setTabTitle, - getTabURL, setTabURL, getTabContentType } = require('./utils'); - -// Array of the inner instances of all the wrapped tabs. -const TABS = []; - -/** - * Trait used to create tab wrappers. - */ -const TabTrait = Trait.compose(EventEmitter, { - on: Trait.required, - _emit: Trait.required, - /** - * Tab DOM element that is being wrapped. - */ - _tab: null, - /** - * Window wrapper whose tab this object represents. - */ - window: null, - constructor: function Tab(options) { - this._onReady = this._onReady.bind(this); - this._tab = options.tab; - let window = this.window = options.window || getOwnerWindow(this._tab); - - // Setting event listener if was passed. - for each (let type in EVENTS) { - let listener = options[type.listener]; - if (listener) - this.on(type.name, options[type.listener]); - if ('ready' != type.name) // window spreads this event. - window.tabs.on(type.name, this._onEvent.bind(this, type.name)); - } - - this.on(EVENTS.close.name, this.destroy.bind(this)); - this._browser.addEventListener(EVENTS.ready.dom, this._onReady, true); - - if (options.isPinned) - this.pin(); - - // Since we will have to identify tabs by a DOM elements facade function - // is used as constructor that collects all the instances and makes sure - // that they more then one wrapper is not created per tab. - return this; - }, - destroy: function destroy() { - this._removeAllListeners(); - this._browser.removeEventListener(EVENTS.ready.dom, this._onReady, true); - }, - - /** - * Internal listener that emits public event 'ready' when the page of this - * tab is loaded. - */ - _onReady: function _onReady(event) { - // IFrames events will bubble so we need to ignore those. - if (event.target == this._contentDocument) - this._emit(EVENTS.ready.name, this._public); - }, - /** - * Internal tab event router. Window will emit tab related events for all it's - * tabs, this listener will propagate all the events for this tab to it's - * listeners. - */ - _onEvent: function _onEvent(type, tab) { - if (tab == this._public) - this._emit(type, tab); - }, - /** - * Browser DOM element where page of this tab is currently loaded. - */ - get _browser() getBrowserForTab(this._tab), - /** - * Window DOM element containing this tab. - */ - get _window() getOwnerWindow(this._tab), - /** - * Document object of the page that is currently loaded in this tab. - */ - get _contentDocument() this._browser.contentDocument, - /** - * Window object of the page that is currently loaded in this tab. - */ - get _contentWindow() this._browser.contentWindow, - - /** - * The title of the page currently loaded in the tab. - * Changing this property changes an actual title. - * @type {String} - */ - get title() getTabTitle(this._tab), - set title(title) setTabTitle(this._tab, title), - - /** - * Returns the MIME type that the document loaded in the tab is being - * rendered as. - * @type {String} - */ - get contentType() getTabContentType(this._tab), - - /** - * Location of the page currently loaded in this tab. - * Changing this property will loads page under under the specified location. - * @type {String} - */ - get url() getTabURL(this._tab), - set url(url) setTabURL(this._tab, url), - /** - * URI of the favicon for the page currently loaded in this tab. - * @type {String} - */ - get favicon() getFaviconURIForLocation(this.url), - /** - * The CSS style for the tab - */ - get style() null, // TODO - /** - * The index of the tab relative to other tabs in the application window. - * Changing this property will change order of the actual position of the tab. - * @type {Number} - */ - get index() - this._window.gBrowser.getBrowserIndexForDocument(this._contentDocument), - set index(value) this._window.gBrowser.moveTabTo(this._tab, value), - /** - * Thumbnail data URI of the page currently loaded in this tab. - * @type {String} - */ - getThumbnail: function getThumbnail() - getThumbnailURIForWindow(this._contentWindow), - /** - * Whether or not tab is pinned (Is an app-tab). - * @type {Boolean} - */ - get isPinned() this._tab.pinned, - pin: function pin() { - this._window.gBrowser.pinTab(this._tab); - }, - unpin: function unpin() { - this._window.gBrowser.unpinTab(this._tab); - }, - - /** - * Create a worker for this tab, first argument is options given to Worker. - * @type {Worker} - */ - attach: function attach(options) { - // BUG 792946 https://bugzilla.mozilla.org/show_bug.cgi?id=792946 - // TODO: fix this circular dependency - let { Worker } = require('./worker'); - return Worker(options, this._contentWindow); - }, - - /** - * Make this tab active. - * Please note: That this function is called asynchronous since in E10S that - * will be the case. Besides this function is called from a constructor where - * we would like to return instance before firing a 'TabActivated' event. - */ - activate: defer(function activate() { - activateTab(this._tab); - }), - /** - * Close the tab - */ - close: function close(callback) { - if (callback) - this.once(EVENTS.close.name, callback); - this._window.gBrowser.removeTab(this._tab); - }, - /** - * Reload the tab - */ - reload: function reload() { - this._window.gBrowser.reloadTab(this._tab); - } -}); - -function Tab(options) { - let chromeTab = options.tab; - for each (let tab in TABS) { - if (chromeTab == tab._tab) - return tab._public; - } - let tab = TabTrait(options); - TABS.push(tab); - return tab._public; -} -Tab.prototype = TabTrait.prototype; -exports.Tab = Tab; diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/tab.js b/tools/addon-sdk-1.12/lib/sdk/tabs/tab.js deleted file mode 100644 index 420baee..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/tab.js +++ /dev/null @@ -1,21 +0,0 @@ -/* 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'; - -module.metadata = { - 'stability': 'unstable' -}; - -if (require('../system/xul-app').is('Firefox')) { - module.exports = require('./tab-firefox'); -} -else if (require('../system/xul-app').is('Fennec')) { - module.exports = require('./tab-fennec'); -} -else { - throw new Error([ - 'The tabs module currently supports only Firefox & Fennec. In the future', - ' we would like it to support other applications, however.' - ].join('')); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/tabs-firefox.js b/tools/addon-sdk-1.12/lib/sdk/tabs/tabs-firefox.js deleted file mode 100644 index 92834c2..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/tabs-firefox.js +++ /dev/null @@ -1,26 +0,0 @@ -/* 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'; - -// TODO: BUG 792670 - remove dependency below -const { browserWindows } = require('../windows'); -const { tabs } = require('../windows/tabs-firefox'); - -Object.defineProperties(tabs, { - open: { value: function open(options) { - if (options.inNewWindow) - // `tabs` option is under review and may be removed. - return browserWindows.open({ tabs: [ options ] }); - // Open in active window if new window was not required. - return browserWindows.activeWindow.tabs.open(options); - }} -}); - -// Workaround for bug 674195. Freezing objects from other compartments fail, -// so we use `Object.freeze` from the same component as objects -// `hasOwnProperty`. Since `hasOwnProperty` here will be from other component -// we override it to support our workaround. -module.exports = Object.create(tabs, { - isPrototypeOf: { value: Object.prototype.isPrototypeOf } -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/tabs.js b/tools/addon-sdk-1.12/lib/sdk/tabs/tabs.js deleted file mode 100644 index fcb3868..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/tabs.js +++ /dev/null @@ -1,21 +0,0 @@ -/* 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'; - -module.metadata = { - 'stability': 'unstable' -}; - -if (require('../system/xul-app').is('Firefox')) { - module.exports = require('./tabs-firefox'); -} -else if (require('../system/xul-app').is('Fennec')) { - module.exports = require('../windows/tabs-fennec').tabs; -} -else { - throw new Error([ - 'The tabs module currently supports only Firefox & Fennec. In the future', - ' we would like it to support other applications, however.' - ].join('')); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/utils.js b/tools/addon-sdk-1.12/lib/sdk/tabs/utils.js deleted file mode 100644 index d923e4b..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/utils.js +++ /dev/null @@ -1,222 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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'; - -module.metadata = { - 'stability': 'unstable' -}; - -const { defer } = require("../lang/functional"); -const { windows } = require('../window/utils'); -const { Ci } = require('chrome'); - -function activateTab(tab, window) { - let gBrowser = getTabBrowserForTab(tab); - - // normal case - if (gBrowser) { - gBrowser.selectedTab = tab; - } - // fennec ? - else if (window && window.BrowserApp) { - window.BrowserApp.selectTab(tab); - } - return null; -} -exports.activateTab = activateTab; - -function getTabBrowser(window) { - return window.gBrowser; -} -exports.getTabBrowser = getTabBrowser; - -function getTabContainer(window) { - return getTabBrowser(window).tabContainer; -} -exports.getTabContainer = getTabContainer; - -function getTabs(window) { - // fennec - if (window.BrowserApp) - return window.BrowserApp.tabs; - - // firefox - default - return Array.slice(getTabContainer(window).children); -} -exports.getTabs = getTabs; - -function getActiveTab(window) { - return window.gBrowser.selectedTab; -} -exports.getActiveTab = getActiveTab; - -function getOwnerWindow(tab) { - // normal case - if (tab.ownerDocument) - return tab.ownerDocument.defaultView; - - // try fennec case - return getWindowHoldingTab(tab); -} -exports.getOwnerWindow = getOwnerWindow; - -// fennec -function getWindowHoldingTab(rawTab) { - for each (let window in windows()) { - // this function may be called when not using fennec - if (!window.BrowserApp) - continue; - - for each (let tab in window.BrowserApp.tabs) { - if (tab === rawTab) - return window; - } - } - - return null; -} - -function openTab(window, url, options) { - options = options || {}; - - // fennec? - if (window.BrowserApp) { - return window.BrowserApp.addTab(url, { - selected: options.inBackground ? false : true, - pinned: options.isPinned || false - }); - } - return window.gBrowser.addTab(url); -}; -exports.openTab = openTab; - -function isTabOpen(tab) { - // try normal case then fennec case - return !!((tab.linkedBrowser) || getWindowHoldingTab(tab)); -} -exports.isTabOpen = isTabOpen; - -function closeTab(tab) { - let gBrowser = getTabBrowserForTab(tab); - // normal case? - if (gBrowser) - return gBrowser.removeTab(tab); - - let window = getWindowHoldingTab(tab); - // fennec? - if (window && window.BrowserApp) - return window.BrowserApp.closeTab(tab); - return null; -} -exports.closeTab = closeTab; - -function getURI(tab) { - if (tab.browser) // fennec - return tab.browser.currentURI.spec; - return tab.linkedBrowser.currentURI.spec; -} -exports.getURI = getURI; - -function getTabBrowserForTab(tab) { - let outerWin = getOwnerWindow(tab); - if (outerWin) - return getOwnerWindow(tab).gBrowser; - return null; -} -exports.getTabBrowserForTab = getTabBrowserForTab; - -function getBrowserForTab(tab) { - if (tab.browser) // fennec - return tab.browser; - - return tab.linkedBrowser; -} -exports.getBrowserForTab = getBrowserForTab; - -function getTabTitle(tab) { - return getBrowserForTab(tab).contentDocument.title || tab.label || ""; -} -exports.getTabTitle = getTabTitle; - -function setTabTitle(tab, title) { - title = String(title); - if (tab.browser) - tab.browser.contentDocument.title = title; - tab.label = String(title); -} -exports.setTabTitle = setTabTitle; - -function getTabContentWindow(tab) { - return getBrowserForTab(tab).contentWindow; -} -exports.getTabContentWindow = getTabContentWindow; - -function getTabForContentWindow(window) { - // Retrieve the topmost frame container. It can be either <xul:browser>, - // <xul:iframe/> or <html:iframe/>. But in our case, it should be xul:browser. - let browser = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .chromeEventHandler; - // Is null for toplevel documents - if (!browser) - return false; - // Retrieve the owner window, should be browser.xul one - let chromeWindow = browser.ownerDocument.defaultView; - - // Ensure that it is top-level browser window. - // We need extra checks because of Mac hidden window that has a broken - // `gBrowser` global attribute. - if ('gBrowser' in chromeWindow && chromeWindow.gBrowser && - 'browsers' in chromeWindow.gBrowser) { - // Looks like we are on Firefox Desktop - // Then search for the position in tabbrowser in order to get the tab object - let browsers = chromeWindow.gBrowser.browsers; - let i = browsers.indexOf(browser); - if (i !== -1) - return chromeWindow.gBrowser.tabs[i]; - return null; - } - else if ('BrowserApp' in chromeWindow) { - // Looks like we are on Firefox Mobile - return chromeWindow.BrowserApp.getTabForWindow(window) - } - - return null; -} -exports.getTabForContentWindow = getTabForContentWindow; - -function getTabURL(tab) { - if (tab.browser) // fennec - return String(tab.browser.currentURI.spec); - return String(getBrowserForTab(tab).currentURI.spec); -} -exports.getTabURL = getTabURL; - -function setTabURL(tab, url) { - url = String(url); - if (tab.browser) - return tab.browser.loadURI(url); - return getBrowserForTab(tab).loadURI(url); -} -// "TabOpen" event is fired when it's still "about:blank" is loaded in the -// changing `location` property of the `contentDocument` has no effect since -// seems to be either ignored or overridden by internal listener, there for -// location change is enqueued for the next turn of event loop. -exports.setTabURL = defer(setTabURL); - -function getTabContentType(tab) { - return getBrowserForTab(tab).contentDocument.contentType; -} -exports.getTabContentType = getTabContentType; - -function getSelectedTab(window) { - if (window.BrowserApp) // fennec? - return window.BrowserApp.selectedTab; - if (window.gBrowser) - return window.gBrowser.selectedTab; - return null; -} -exports.getSelectedTab = getSelectedTab; diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/worker.js b/tools/addon-sdk-1.12/lib/sdk/tabs/worker.js deleted file mode 100644 index d2ba336..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/tabs/worker.js +++ /dev/null @@ -1,17 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const ContentWorker = require('../content/worker').Worker; - -function Worker(options, window) { - options.window = window; - - let worker = ContentWorker(options); - worker.once("detach", function detach() { - worker.destroy(); - }); - return worker; -} -exports.Worker = Worker;
\ No newline at end of file diff --git a/tools/addon-sdk-1.12/lib/sdk/test.js b/tools/addon-sdk-1.12/lib/sdk/test.js deleted file mode 100644 index 0a322d6..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/test.js +++ /dev/null @@ -1,112 +0,0 @@ -/* vim:ts=2:sts=2:sw=2: - * 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"; - -module.metadata = { - "stability": "unstable" -}; - -const BaseAssert = require("./test/assert").Assert; -const { isFunction, isObject } = require("./lang/type"); - -function extend(target) { - let descriptor = {} - Array.slice(arguments, 1).forEach(function(source) { - Object.getOwnPropertyNames(source).forEach(function onEach(name) { - descriptor[name] = Object.getOwnPropertyDescriptor(source, name); - }); - }); - return Object.create(target, descriptor); -} - -/** - * Function takes test `suite` object in CommonJS format and defines all of the - * tests from that suite and nested suites in a jetpack format on a given - * `target` object. Optionally third argument `prefix` can be passed to prefix - * all the test names. - */ -function defineTestSuite(target, suite, prefix) { - prefix = prefix || ""; - // If suite defines `Assert` that's what `assert` object have to be created - // from and passed to a test function (This allows custom assertion functions) - // See for details: http://wiki.commonjs.org/wiki/Unit_Testing/1.1 - let Assert = suite.Assert || BaseAssert; - // Going through each item in the test suite and wrapping it into a - // Jetpack test format. - Object.keys(suite).forEach(function(key) { - // If name starts with test then it's a test function or suite. - if (key.indexOf("test") === 0) { - let test = suite[key]; - - // For each test function so we create a wrapper test function in a - // jetpack format and copy that to a `target` exports. - if (isFunction(test)) { - - // Since names of the test may match across suites we use full object - // path as a name to avoid overriding same function. - target[prefix + key] = function(options) { - - // Creating `assert` functions for this test. - let assert = Assert(options); - - // If CommonJS test function expects more than one argument - // it means that test is async and second argument is a callback - // to notify that test is finished. - if (1 < test.length) { - - // Letting test runner know that test is executed async and - // creating a callback function that CommonJS tests will call - // once it's done. - options.waitUntilDone(); - test(assert, function() { - options.done(); - }); - } - - // Otherwise CommonJS test is synchronous so we call it only with - // one argument. - else { - test(assert); - } - } - } - - // If it's an object then it's a test suite containing test function - // and / or nested test suites. In that case we just extend prefix used - // and call this function to copy and wrap tests from nested suite. - else if (isObject(test)) { - // We need to clone `tests` instead of modifying it, since it's very - // likely that it is frozen (usually test suites imported modules). - test = extend(Object.prototype, test, { - Assert: test.Assert || Assert - }); - defineTestSuite(target, test, prefix + key + "."); - } - } - }); -} - -/** - * This function is a CommonJS test runner function, but since Jetpack test - * runner and test format is different from CommonJS this function shims given - * `exports` with all its tests into a Jetpack test format so that the built-in - * test runner will be able to run CommonJS test without manual changes. - */ -exports.run = function run(exports) { - - // We can't leave old properties on exports since those are test in a CommonJS - // format that why we move everything to a new `suite` object. - let suite = {}; - Object.keys(exports).forEach(function(key) { - suite[key] = exports[key]; - delete exports[key]; - }); - - // Now we wrap all the CommonJS tests to a Jetpack format and define - // those to a given `exports` object since that where jetpack test runner - // will look for them. - defineTestSuite(exports, suite); -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/test/assert.js b/tools/addon-sdk-1.12/lib/sdk/test/assert.js deleted file mode 100644 index 5eca7d4..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/test/assert.js +++ /dev/null @@ -1,349 +0,0 @@ -/* vim:ts=2:sts=2:sw=2: - * 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { isFunction, isNull, isObject, isString, - isRegExp, isArray, isDate, isPrimitive, - isUndefined, instanceOf, source } = require("../lang/type"); - -/** - * The `AssertionError` is defined in assert. - * @extends Error - * @example - * new assert.AssertionError({ - * message: message, - * actual: actual, - * expected: expected - * }) - */ -function AssertionError(options) { - let assertionError = Object.create(AssertionError.prototype); - - if (isString(options)) - options = { message: options }; - if ("actual" in options) - assertionError.actual = options.actual; - if ("expected" in options) - assertionError.expected = options.expected; - if ("operator" in options) - assertionError.operator = options.operator; - - assertionError.message = options.message; - assertionError.stack = new Error().stack; - return assertionError; -} -AssertionError.prototype = Object.create(Error.prototype, { - constructor: { value: AssertionError }, - name: { value: "AssertionError", enumerable: true }, - toString: { value: function toString() { - let value; - if (this.message) { - value = this.name + " : " + this.message; - } - else { - value = [ - this.name + " : ", - source(this.expected), - this.operator, - source(this.actual) - ].join(" "); - } - return value; - }} -}); -exports.AssertionError = AssertionError; - -function Assert(logger) { - return Object.create(Assert.prototype, { _log: { value: logger }}); -} -Assert.prototype = { - fail: function fail(e) { - if (!e || typeof(e) !== 'object') { - this._log.fail(e); - return; - } - let message = e.message; - if ('operator' in e) { - message += [ - " -", - source(e.expected), - e.operator, - source(e.actual) - ].join(" "); - } - this._log.fail(message); - }, - pass: function pass(message) { - this._log.pass(message); - }, - error: function error(e) { - this._log.exception(e); - }, - ok: function ok(value, message) { - if (!!!value) { - this.fail({ - actual: value, - expected: true, - message: message, - operator: "==" - }); - } - else { - this.pass(message); - } - }, - - /** - * The equality assertion tests shallow, coercive equality with `==`. - * @example - * assert.equal(1, 1, "one is one"); - */ - equal: function equal(actual, expected, message) { - if (actual == expected) { - this.pass(message); - } - else { - this.fail({ - actual: actual, - expected: expected, - message: message, - operator: "==" - }); - } - }, - - /** - * The non-equality assertion tests for whether two objects are not equal - * with `!=`. - * @example - * assert.notEqual(1, 2, "one is not two"); - */ - notEqual: function notEqual(actual, expected, message) { - if (actual != expected) { - this.pass(message); - } - else { - this.fail({ - actual: actual, - expected: expected, - message: message, - operator: "!=", - }); - } - }, - - /** - * The equivalence assertion tests a deep (with `===`) equality relation. - * @example - * assert.deepEqual({ a: "foo" }, { a: "foo" }, "equivalent objects") - */ - deepEqual: function deepEqual(actual, expected, message) { - if (isDeepEqual(actual, expected)) { - this.pass(message); - } - else { - this.fail({ - actual: actual, - expected: expected, - message: message, - operator: "deepEqual" - }); - } - }, - - /** - * The non-equivalence assertion tests for any deep (with `===`) inequality. - * @example - * assert.notDeepEqual({ a: "foo" }, Object.create({ a: "foo" }), - * "object's inherit from different prototypes"); - */ - notDeepEqual: function notDeepEqual(actual, expected, message) { - if (!isDeepEqual(actual, expected)) { - this.pass(message); - } - else { - this.fail({ - actual: actual, - expected: expected, - message: message, - operator: "notDeepEqual" - }); - } - }, - - /** - * The strict equality assertion tests strict equality, as determined by - * `===`. - * @example - * assert.strictEqual(null, null, "`null` is `null`") - */ - strictEqual: function strictEqual(actual, expected, message) { - if (actual === expected) { - this.pass(message); - } - else { - this.fail({ - actual: actual, - expected: expected, - message: message, - operator: "===" - }); - } - }, - - /** - * The strict non-equality assertion tests for strict inequality, as - * determined by `!==`. - * @example - * assert.notStrictEqual(null, undefined, "`null` is not `undefined`"); - */ - notStrictEqual: function notStrictEqual(actual, expected, message) { - if (actual !== expected) { - this.pass(message); - } - else { - this.fail({ - actual: actual, - expected: expected, - message: message, - operator: "!==" - }) - } - }, - - /** - * The assertion whether or not given `block` throws an exception. If optional - * `Error` argument is provided and it's type of function thrown error is - * asserted to be an instance of it, if type of `Error` is string then message - * of throw exception is asserted to contain it. - * @param {Function} block - * Function that is expected to throw. - * @param {Error|RegExp} [Error] - * Error constructor that is expected to be thrown or a string that - * must be contained by a message of the thrown exception, or a RegExp - * matching a message of the thrown exception. - * @param {String} message - * Description message - * - * @examples - * - * assert.throws(function block() { - * doSomething(4) - * }, "Object is expected", "Incorrect argument is passed"); - * - * assert.throws(function block() { - * Object.create(5) - * }, TypeError, "TypeError is thrown"); - */ - throws: function throws(block, Error, message) { - let threw = false; - let exception = null; - - // If third argument is not provided and second argument is a string it - // means that optional `Error` argument was not passed, so we shift - // arguments. - if (isString(Error) && isUndefined(message)) { - message = Error; - Error = undefined; - } - - // Executing given `block`. - try { - block(); - } - catch (e) { - threw = true; - exception = e; - } - - // If exception was thrown and `Error` argument was not passed assert is - // passed. - if (threw && (isUndefined(Error) || - // If passed `Error` is RegExp using it's test method to - // assert thrown exception message. - (isRegExp(Error) && Error.test(exception.message)) || - // If passed `Error` is a constructor function testing if - // thrown exception is an instance of it. - (isFunction(Error) && instanceOf(exception, Error)))) - { - this.pass(message); - } - - // Otherwise we report assertion failure. - else { - let failure = { - message: message, - operator: "throws" - }; - - if (exception) - failure.actual = exception; - - if (Error) - failure.expected = Error; - - this.fail(failure); - } - } -}; -exports.Assert = Assert; - -function isDeepEqual(actual, expected) { - - // 7.1. All identical values are equivalent, as determined by ===. - if (actual === expected) { - return true; - } - - // 7.2. If the expected value is a Date object, the actual value is - // equivalent if it is also a Date object that refers to the same time. - else if (isDate(actual) && isDate(expected)) { - return actual.getTime() === expected.getTime(); - } - - // XXX specification bug: this should be specified - else if (isPrimitive(actual) || isPrimitive(expected)) { - return expected === actual; - } - - // 7.3. Other pairs that do not both pass typeof value == "object", - // equivalence is determined by ==. - else if (!isObject(actual) && !isObject(expected)) { - return actual == expected; - } - - // 7.4. For all other Object pairs, including Array objects, equivalence is - // determined by having the same number of owned properties (as verified - // with Object.prototype.hasOwnProperty.call), the same set of keys - // (although not necessarily the same order), equivalent values for every - // corresponding key, and an identical "prototype" property. Note: this - // accounts for both named and indexed properties on Arrays. - else { - return actual.prototype === expected.prototype && - isEquivalent(actual, expected); - } -} - -function isEquivalent(a, b, stack) { - let aKeys = Object.keys(a); - let bKeys = Object.keys(b); - - return aKeys.length === bKeys.length && - isArrayEquivalent(aKeys.sort(), bKeys.sort()) && - aKeys.every(function(key) { - return isDeepEqual(a[key], b[key], stack) - }); -} - -function isArrayEquivalent(a, b, stack) { - return isArray(a) && isArray(b) && - a.every(function(value, index) { - return isDeepEqual(value, b[index]); - }); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/test/harness.js b/tools/addon-sdk-1.12/lib/sdk/test/harness.js deleted file mode 100644 index 46584ca..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/test/harness.js +++ /dev/null @@ -1,327 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { Cc,Ci } = require("chrome"); -const { Loader } = require('./loader'); -const { setTimeout } = require('../timers'); -const memory = require('../deprecated/memory'); -const { PlainTextConsole } = require("../console/plain-text"); -const { when: unload } = require("../system/unload"); -const { format } = require("../console/traceback"); -const system = require("../system"); - -// Trick manifest builder to make it think we need these modules ? -const unit = require("../deprecated/unit-test"); -const test = require("../test"); -const url = require("../url"); - -var cService = Cc['@mozilla.org/consoleservice;1'].getService() - .QueryInterface(Ci.nsIConsoleService); - -// Cuddlefish loader in which we load and execute tests. -var loader; - -// Function to call when we're done running tests. -var onDone; - -// Function to print text to a console, w/o CR at the end. -var print; - -// How many more times to run all tests. -var iterationsLeft; - -// Whether to report memory profiling information. -var profileMemory; - -// Whether we should stop as soon as a test reports a failure. -var stopOnError; - -// Function to call to retrieve a list of tests to execute -var findAndRunTests; - -// Combined information from all test runs. -var results = { - passed: 0, - failed: 0, - testRuns: [] -}; - -// JSON serialization of last memory usage stats; we keep it stringified -// so we don't actually change the memory usage stats (in terms of objects) -// of the JSRuntime we're profiling. -var lastMemoryUsage; - -function analyzeRawProfilingData(data) { - var graph = data.graph; - var shapes = {}; - - // Convert keys in the graph from strings to ints. - // TODO: Can we get rid of this ridiculousness? - var newGraph = {}; - for (id in graph) { - newGraph[parseInt(id)] = graph[id]; - } - graph = newGraph; - - var modules = 0; - var moduleIds = []; - var moduleObjs = {UNKNOWN: 0}; - for (let name in data.namedObjects) { - moduleObjs[name] = 0; - moduleIds[data.namedObjects[name]] = name; - modules++; - } - - var count = 0; - for (id in graph) { - var parent = graph[id].parent; - while (parent) { - if (parent in moduleIds) { - var name = moduleIds[parent]; - moduleObjs[name]++; - break; - } - if (!(parent in graph)) { - moduleObjs.UNKNOWN++; - break; - } - parent = graph[parent].parent; - } - count++; - } - - print("\nobject count is " + count + " in " + modules + " modules" + - " (" + data.totalObjectCount + " across entire JS runtime)\n"); - if (lastMemoryUsage) { - var last = JSON.parse(lastMemoryUsage); - var diff = { - moduleObjs: dictDiff(last.moduleObjs, moduleObjs), - totalObjectClasses: dictDiff(last.totalObjectClasses, - data.totalObjectClasses) - }; - - for (let name in diff.moduleObjs) - print(" " + diff.moduleObjs[name] + " in " + name + "\n"); - for (let name in diff.totalObjectClasses) - print(" " + diff.totalObjectClasses[name] + " instances of " + - name + "\n"); - } - lastMemoryUsage = JSON.stringify( - {moduleObjs: moduleObjs, - totalObjectClasses: data.totalObjectClasses} - ); -} - -function dictDiff(last, curr) { - var diff = {}; - - for (let name in last) { - var result = (curr[name] || 0) - last[name]; - if (result) - diff[name] = (result > 0 ? "+" : "") + result; - } - for (let name in curr) { - var result = curr[name] - (last[name] || 0); - if (result) - diff[name] = (result > 0 ? "+" : "") + result; - } - return diff; -} - -function reportMemoryUsage() { - memory.gc(); - - var mgr = Cc["@mozilla.org/memory-reporter-manager;1"] - .getService(Ci.nsIMemoryReporterManager); - var reporters = mgr.enumerateReporters(); - if (reporters.hasMoreElements()) - print("\n"); - while (reporters.hasMoreElements()) { - var reporter = reporters.getNext(); - reporter.QueryInterface(Ci.nsIMemoryReporter); - print(reporter.description + ": " + reporter.memoryUsed + "\n"); - } - - var weakrefs = [info.weakref.get() - for each (info in memory.getObjects())]; - weakrefs = [weakref for each (weakref in weakrefs) if (weakref)]; - print("Tracked memory objects in testing sandbox: " + - weakrefs.length + "\n"); -} - -var gWeakrefInfo; - -function showResults() { - memory.gc(); - - if (gWeakrefInfo) { - gWeakrefInfo.forEach( - function(info) { - var ref = info.weakref.get(); - if (ref !== null) { - var data = ref.__url__ ? ref.__url__ : ref; - var warning = data == "[object Object]" - ? "[object " + data.constructor.name + "(" + - [p for (p in data)].join(", ") + ")]" - : data; - console.warn("LEAK", warning, info.bin); - } - } - ); - } - - onDone(results); -} - -function cleanup() { - try { - for (let name in loader.modules) - memory.track(loader.modules[name], - "module global scope: " + name); - memory.track(loader, "Cuddlefish Loader"); - - if (profileMemory) { - gWeakrefInfo = [{ weakref: info.weakref, bin: info.bin } - for each (info in memory.getObjects())]; - } - - loader.unload(); - - if (loader.globals.console.errorsLogged && !results.failed) { - results.failed++; - console.error("warnings and/or errors were logged."); - } - - if (consoleListener.errorsLogged && !results.failed) { - console.warn(consoleListener.errorsLogged + " " + - "warnings or errors were logged to the " + - "platform's nsIConsoleService, which could " + - "be of no consequence; however, they could also " + - "be indicative of aberrant behavior."); - } - - consoleListener.errorsLogged = 0; - loader = null; - - memory.gc(); - } catch (e) { - results.failed++; - console.error("unload.send() threw an exception."); - console.exception(e); - }; - - setTimeout(showResults, 1); -} - -function nextIteration(tests) { - if (tests) { - results.passed += tests.passed; - results.failed += tests.failed; - - if (profileMemory) - reportMemoryUsage(); - - let testRun = []; - for each (let test in tests.testRunSummary) { - let testCopy = {}; - for (let info in test) { - testCopy[info] = test[info]; - } - testRun.push(testCopy); - } - - results.testRuns.push(testRun); - iterationsLeft--; - } - - if (iterationsLeft && (!stopOnError || results.failed == 0)) { - // Pass the loader which has a hooked console that doesn't dispatch - // errors to the JS console and avoid firing false alarm in our - // console listener - findAndRunTests(loader, nextIteration); - } - else { - setTimeout(cleanup, 0); - } -} - -var POINTLESS_ERRORS = [ - 'Invalid chrome URI:', - 'OpenGL LayerManager Initialized Succesfully.', - '[JavaScript Error: "TelemetryStopwatch:', - '[JavaScript Warning: "ReferenceErrorL reference to undefined property', - '[Javascript Warning: "Error: Failed to preserve wrapper of wrapped ' + - 'native weak map key', - '[JavaScript Warning: "Duplicate resource declaration for' -]; - -var consoleListener = { - errorsLogged: 0, - observe: function(object) { - if (!(object instanceof Ci.nsIScriptError)) - return; - this.errorsLogged++; - var message = object.QueryInterface(Ci.nsIConsoleMessage).message; - var pointless = [err for each (err in POINTLESS_ERRORS) - if (message.indexOf(err) == 0)]; - if (pointless.length == 0 && message) - print("console: " + message + "\n"); - } -}; - -function TestRunnerConsole(base, options) { - this.__proto__ = { - errorsLogged: 0, - warn: function warn() { - this.errorsLogged++; - base.warn.apply(base, arguments); - }, - error: function error() { - this.errorsLogged++; - base.error.apply(base, arguments); - }, - info: function info(first) { - if (options.verbose) - base.info.apply(base, arguments); - else - if (first == "pass:") - print("."); - }, - __proto__: base - }; -} - -var runTests = exports.runTests = function runTests(options) { - iterationsLeft = options.iterations; - profileMemory = options.profileMemory; - stopOnError = options.stopOnError; - onDone = options.onDone; - print = options.print; - findAndRunTests = options.findAndRunTests; - - try { - cService.registerListener(consoleListener); - print("Running tests on " + system.name + " " + system.version + - "/Gecko " + system.platformVersion + " (" + - system.id + ") under " + - system.platform + "/" + system.architecture + ".\n"); - - - loader = Loader(module, { - console: new TestRunnerConsole(new PlainTextConsole(print), options) - }); - - nextIteration(); - } catch (e) { - print(format(e) + "\n" + e + "\n"); - onDone({passed: 0, failed: 1}); - } -}; - -unload(function() { - cService.unregisterListener(consoleListener); -}); - diff --git a/tools/addon-sdk-1.12/lib/sdk/test/httpd.js b/tools/addon-sdk-1.12/lib/sdk/test/httpd.js deleted file mode 100644 index 48d3516..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/test/httpd.js +++ /dev/null @@ -1,5174 +0,0 @@ -/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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/. */ - -/* -* An implementation of an HTTP server both as a loadable script and as an XPCOM -* component. See the accompanying README file for user documentation on -* httpd.js. -*/ - -module.metadata = { - "stability": "experimental" -}; - -const { components, CC, Cc, Ci, Cr, Cu } = require("chrome"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - - -const PR_UINT32_MAX = Math.pow(2, 32) - 1; - -/** True if debugging output is enabled, false otherwise. */ -var DEBUG = false; // non-const *only* so tweakable in server tests - -/** True if debugging output should be timestamped. */ -var DEBUG_TIMESTAMP = false; // non-const so tweakable in server tests - -var gGlobalObject = Cc["@mozilla.org/systemprincipal;1"].createInstance(); - -/** -* Asserts that the given condition holds. If it doesn't, the given message is -* dumped, a stack trace is printed, and an exception is thrown to attempt to -* stop execution (which unfortunately must rely upon the exception not being -* accidentally swallowed by the code that uses it). -*/ -function NS_ASSERT(cond, msg) -{ - if (DEBUG && !cond) - { - dumpn("###!!!"); - dumpn("###!!! ASSERTION" + (msg ? ": " + msg : "!")); - dumpn("###!!! Stack follows:"); - - var stack = new Error().stack.split(/\n/); - dumpn(stack.map(function(val) { return "###!!! " + val; }).join("\n")); - - throw Cr.NS_ERROR_ABORT; - } -} - -/** Constructs an HTTP error object. */ -function HttpError(code, description) -{ - this.code = code; - this.description = description; -} -HttpError.prototype = -{ - toString: function() - { - return this.code + " " + this.description; - } -}; - -/** -* Errors thrown to trigger specific HTTP server responses. -*/ -const HTTP_400 = new HttpError(400, "Bad Request"); -const HTTP_401 = new HttpError(401, "Unauthorized"); -const HTTP_402 = new HttpError(402, "Payment Required"); -const HTTP_403 = new HttpError(403, "Forbidden"); -const HTTP_404 = new HttpError(404, "Not Found"); -const HTTP_405 = new HttpError(405, "Method Not Allowed"); -const HTTP_406 = new HttpError(406, "Not Acceptable"); -const HTTP_407 = new HttpError(407, "Proxy Authentication Required"); -const HTTP_408 = new HttpError(408, "Request Timeout"); -const HTTP_409 = new HttpError(409, "Conflict"); -const HTTP_410 = new HttpError(410, "Gone"); -const HTTP_411 = new HttpError(411, "Length Required"); -const HTTP_412 = new HttpError(412, "Precondition Failed"); -const HTTP_413 = new HttpError(413, "Request Entity Too Large"); -const HTTP_414 = new HttpError(414, "Request-URI Too Long"); -const HTTP_415 = new HttpError(415, "Unsupported Media Type"); -const HTTP_417 = new HttpError(417, "Expectation Failed"); - -const HTTP_500 = new HttpError(500, "Internal Server Error"); -const HTTP_501 = new HttpError(501, "Not Implemented"); -const HTTP_502 = new HttpError(502, "Bad Gateway"); -const HTTP_503 = new HttpError(503, "Service Unavailable"); -const HTTP_504 = new HttpError(504, "Gateway Timeout"); -const HTTP_505 = new HttpError(505, "HTTP Version Not Supported"); - -/** Creates a hash with fields corresponding to the values in arr. */ -function array2obj(arr) -{ - var obj = {}; - for (var i = 0; i < arr.length; i++) - obj[arr[i]] = arr[i]; - return obj; -} - -/** Returns an array of the integers x through y, inclusive. */ -function range(x, y) -{ - var arr = []; - for (var i = x; i <= y; i++) - arr.push(i); - return arr; -} - -/** An object (hash) whose fields are the numbers of all HTTP error codes. */ -const HTTP_ERROR_CODES = array2obj(range(400, 417).concat(range(500, 505))); - - -/** -* The character used to distinguish hidden files from non-hidden files, a la -* the leading dot in Apache. Since that mechanism also hides files from -* easy display in LXR, ls output, etc. however, we choose instead to use a -* suffix character. If a requested file ends with it, we append another -* when getting the file on the server. If it doesn't, we just look up that -* file. Therefore, any file whose name ends with exactly one of the character -* is "hidden" and available for use by the server. -*/ -const HIDDEN_CHAR = "^"; - -/** -* The file name suffix indicating the file containing overridden headers for -* a requested file. -*/ -const HEADERS_SUFFIX = HIDDEN_CHAR + "headers" + HIDDEN_CHAR; - -/** Type used to denote SJS scripts for CGI-like functionality. */ -const SJS_TYPE = "sjs"; - -/** Base for relative timestamps produced by dumpn(). */ -var firstStamp = 0; - -/** dump(str) with a trailing "\n" -- only outputs if DEBUG. */ -function dumpn(str) -{ - if (DEBUG) - { - var prefix = "HTTPD-INFO | "; - if (DEBUG_TIMESTAMP) - { - if (firstStamp === 0) - firstStamp = Date.now(); - - var elapsed = Date.now() - firstStamp; // milliseconds - var min = Math.floor(elapsed / 60000); - var sec = (elapsed % 60000) / 1000; - - if (sec < 10) - prefix += min + ":0" + sec.toFixed(3) + " | "; - else - prefix += min + ":" + sec.toFixed(3) + " | "; - } - - dump(prefix + str + "\n"); - } -} - -/** Dumps the current JS stack if DEBUG. */ -function dumpStack() -{ - // peel off the frames for dumpStack() and Error() - var stack = new Error().stack.split(/\n/).slice(2); - stack.forEach(dumpn); -} - - -/** The XPCOM thread manager. */ -var gThreadManager = null; - -/** The XPCOM prefs service. */ -var gRootPrefBranch = null; -function getRootPrefBranch() -{ - if (!gRootPrefBranch) - { - gRootPrefBranch = Cc["@mozilla.org/preferences-service;1"] - .getService(Ci.nsIPrefBranch); - } - return gRootPrefBranch; -} - -/** -* JavaScript constructors for commonly-used classes; precreating these is a -* speedup over doing the same from base principles. See the docs at -* http://developer.mozilla.org/en/docs/components.Constructor for details. -*/ -const ServerSocket = CC("@mozilla.org/network/server-socket;1", - "nsIServerSocket", - "init"); -const ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1", - "nsIScriptableInputStream", - "init"); -const Pipe = CC("@mozilla.org/pipe;1", - "nsIPipe", - "init"); -const FileInputStream = CC("@mozilla.org/network/file-input-stream;1", - "nsIFileInputStream", - "init"); -const ConverterInputStream = CC("@mozilla.org/intl/converter-input-stream;1", - "nsIConverterInputStream", - "init"); -const WritablePropertyBag = CC("@mozilla.org/hash-property-bag;1", - "nsIWritablePropertyBag2"); -const SupportsString = CC("@mozilla.org/supports-string;1", - "nsISupportsString"); - -/* These two are non-const only so a test can overwrite them. */ -var BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream"); -var BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1", - "nsIBinaryOutputStream", - "setOutputStream"); - -/** -* Returns the RFC 822/1123 representation of a date. -* -* @param date : Number -* the date, in milliseconds from midnight (00:00:00), January 1, 1970 GMT -* @returns string -* the representation of the given date -*/ -function toDateString(date) -{ - // - // rfc1123-date = wkday "," SP date1 SP time SP "GMT" - // date1 = 2DIGIT SP month SP 4DIGIT - // ; day month year (e.g., 02 Jun 1982) - // time = 2DIGIT ":" 2DIGIT ":" 2DIGIT - // ; 00:00:00 - 23:59:59 - // wkday = "Mon" | "Tue" | "Wed" - // | "Thu" | "Fri" | "Sat" | "Sun" - // month = "Jan" | "Feb" | "Mar" | "Apr" - // | "May" | "Jun" | "Jul" | "Aug" - // | "Sep" | "Oct" | "Nov" | "Dec" - // - - const wkdayStrings = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - const monthStrings = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; - - /** -* Processes a date and returns the encoded UTC time as a string according to -* the format specified in RFC 2616. -* -* @param date : Date -* the date to process -* @returns string -* a string of the form "HH:MM:SS", ranging from "00:00:00" to "23:59:59" -*/ - function toTime(date) - { - var hrs = date.getUTCHours(); - var rv = (hrs < 10) ? "0" + hrs : hrs; - - var mins = date.getUTCMinutes(); - rv += ":"; - rv += (mins < 10) ? "0" + mins : mins; - - var secs = date.getUTCSeconds(); - rv += ":"; - rv += (secs < 10) ? "0" + secs : secs; - - return rv; - } - - /** -* Processes a date and returns the encoded UTC date as a string according to -* the date1 format specified in RFC 2616. -* -* @param date : Date -* the date to process -* @returns string -* a string of the form "HH:MM:SS", ranging from "00:00:00" to "23:59:59" -*/ - function toDate1(date) - { - var day = date.getUTCDate(); - var month = date.getUTCMonth(); - var year = date.getUTCFullYear(); - - var rv = (day < 10) ? "0" + day : day; - rv += " " + monthStrings[month]; - rv += " " + year; - - return rv; - } - - date = new Date(date); - - const fmtString = "%wkday%, %date1% %time% GMT"; - var rv = fmtString.replace("%wkday%", wkdayStrings[date.getUTCDay()]); - rv = rv.replace("%time%", toTime(date)); - return rv.replace("%date1%", toDate1(date)); -} - -/** -* Prints out a human-readable representation of the object o and its fields, -* omitting those whose names begin with "_" if showMembers != true (to ignore -* "private" properties exposed via getters/setters). -*/ -function printObj(o, showMembers) -{ - var s = "******************************\n"; - s += "o = {\n"; - for (var i in o) - { - if (typeof(i) != "string" || - (showMembers || (i.length > 0 && i[0] != "_"))) - s+= " " + i + ": " + o[i] + ",\n"; - } - s += " };\n"; - s += "******************************"; - dumpn(s); -} - -/** -* Instantiates a new HTTP server. -*/ -function nsHttpServer() -{ - if (!gThreadManager) - gThreadManager = Cc["@mozilla.org/thread-manager;1"].getService(); - - /** The port on which this server listens. */ - this._port = undefined; - - /** The socket associated with this. */ - this._socket = null; - - /** The handler used to process requests to this server. */ - this._handler = new ServerHandler(this); - - /** Naming information for this server. */ - this._identity = new ServerIdentity(); - - /** -* Indicates when the server is to be shut down at the end of the request. -*/ - this._doQuit = false; - - /** -* True if the socket in this is closed (and closure notifications have been -* sent and processed if the socket was ever opened), false otherwise. -*/ - this._socketClosed = true; - - /** -* Used for tracking existing connections and ensuring that all connections -* are properly cleaned up before server shutdown; increases by 1 for every -* new incoming connection. -*/ - this._connectionGen = 0; - - /** -* Hash of all open connections, indexed by connection number at time of -* creation. -*/ - this._connections = {}; -} -nsHttpServer.prototype = -{ - classID: components.ID("{54ef6f81-30af-4b1d-ac55-8ba811293e41}"), - - // NSISERVERSOCKETLISTENER - - /** -* Processes an incoming request coming in on the given socket and contained -* in the given transport. -* -* @param socket : nsIServerSocket -* the socket through which the request was served -* @param trans : nsISocketTransport -* the transport for the request/response -* @see nsIServerSocketListener.onSocketAccepted -*/ - onSocketAccepted: function(socket, trans) - { - dumpn("*** onSocketAccepted(socket=" + socket + ", trans=" + trans + ")"); - - dumpn(">>> new connection on " + trans.host + ":" + trans.port); - - const SEGMENT_SIZE = 8192; - const SEGMENT_COUNT = 1024; - try - { - var input = trans.openInputStream(0, SEGMENT_SIZE, SEGMENT_COUNT) - .QueryInterface(Ci.nsIAsyncInputStream); - var output = trans.openOutputStream(0, 0, 0); - } - catch (e) - { - dumpn("*** error opening transport streams: " + e); - trans.close(Cr.NS_BINDING_ABORTED); - return; - } - - var connectionNumber = ++this._connectionGen; - - try - { - var conn = new Connection(input, output, this, socket.port, trans.port, - connectionNumber); - var reader = new RequestReader(conn); - - // XXX add request timeout functionality here! - - // Note: must use main thread here, or we might get a GC that will cause - // threadsafety assertions. We really need to fix XPConnect so that - // you can actually do things in multi-threaded JS. :-( - input.asyncWait(reader, 0, 0, gThreadManager.mainThread); - } - catch (e) - { - // Assume this connection can't be salvaged and bail on it completely; - // don't attempt to close it so that we can assert that any connection - // being closed is in this._connections. - dumpn("*** error in initial request-processing stages: " + e); - trans.close(Cr.NS_BINDING_ABORTED); - return; - } - - this._connections[connectionNumber] = conn; - dumpn("*** starting connection " + connectionNumber); - }, - - /** -* Called when the socket associated with this is closed. -* -* @param socket : nsIServerSocket -* the socket being closed -* @param status : nsresult -* the reason the socket stopped listening (NS_BINDING_ABORTED if the server -* was stopped using nsIHttpServer.stop) -* @see nsIServerSocketListener.onStopListening -*/ - onStopListening: function(socket, status) - { - dumpn(">>> shutting down server on port " + socket.port); - this._socketClosed = true; - if (!this._hasOpenConnections()) - { - dumpn("*** no open connections, notifying async from onStopListening"); - - // Notify asynchronously so that any pending teardown in stop() has a - // chance to run first. - var self = this; - var stopEvent = - { - run: function() - { - dumpn("*** _notifyStopped async callback"); - self._notifyStopped(); - } - }; - gThreadManager.currentThread - .dispatch(stopEvent, Ci.nsIThread.DISPATCH_NORMAL); - } - }, - - // NSIHTTPSERVER - - // - // see nsIHttpServer.start - // - start: function(port) - { - this._start(port, "localhost") - }, - - _start: function(port, host) - { - if (this._socket) - throw Cr.NS_ERROR_ALREADY_INITIALIZED; - - this._port = port; - this._doQuit = this._socketClosed = false; - - this._host = host; - - // The listen queue needs to be long enough to handle - // network.http.max-persistent-connections-per-server concurrent connections, - // plus a safety margin in case some other process is talking to - // the server as well. - var prefs = getRootPrefBranch(); - var maxConnections; - try { - // Bug 776860: The original pref was removed in favor of this new one: - maxConnections = prefs.getIntPref("network.http.max-persistent-connections-per-server") + 5; - } - catch(e) { - maxConnections = prefs.getIntPref("network.http.max-connections-per-server") + 5; - } - - try - { - var loopback = true; - if (this._host != "127.0.0.1" && this._host != "localhost") { - var loopback = false; - } - - var socket = new ServerSocket(this._port, - loopback, // true = localhost, false = everybody - maxConnections); - dumpn(">>> listening on port " + socket.port + ", " + maxConnections + - " pending connections"); - socket.asyncListen(this); - this._identity._initialize(port, host, true); - this._socket = socket; - } - catch (e) - { - dumpn("!!! could not start server on port " + port + ": " + e); - throw Cr.NS_ERROR_NOT_AVAILABLE; - } - }, - - // - // see nsIHttpServer.stop - // - stop: function(callback) - { - if (!callback) - throw Cr.NS_ERROR_NULL_POINTER; - if (!this._socket) - throw Cr.NS_ERROR_UNEXPECTED; - - this._stopCallback = typeof callback === "function" - ? callback - : function() { callback.onStopped(); }; - - dumpn(">>> stopping listening on port " + this._socket.port); - this._socket.close(); - this._socket = null; - - // We can't have this identity any more, and the port on which we're running - // this server now could be meaningless the next time around. - this._identity._teardown(); - - this._doQuit = false; - - // socket-close notification and pending request completion happen async - }, - - // - // see nsIHttpServer.registerFile - // - registerFile: function(path, file) - { - if (file && (!file.exists() || file.isDirectory())) - throw Cr.NS_ERROR_INVALID_ARG; - - this._handler.registerFile(path, file); - }, - - // - // see nsIHttpServer.registerDirectory - // - registerDirectory: function(path, directory) - { - // XXX true path validation! - if (path.charAt(0) != "/" || - path.charAt(path.length - 1) != "/" || - (directory && - (!directory.exists() || !directory.isDirectory()))) - throw Cr.NS_ERROR_INVALID_ARG; - - // XXX determine behavior of nonexistent /foo/bar when a /foo/bar/ mapping - // exists! - - this._handler.registerDirectory(path, directory); - }, - - // - // see nsIHttpServer.registerPathHandler - // - registerPathHandler: function(path, handler) - { - this._handler.registerPathHandler(path, handler); - }, - - // - // see nsIHttpServer.registerErrorHandler - // - registerErrorHandler: function(code, handler) - { - this._handler.registerErrorHandler(code, handler); - }, - - // - // see nsIHttpServer.setIndexHandler - // - setIndexHandler: function(handler) - { - this._handler.setIndexHandler(handler); - }, - - // - // see nsIHttpServer.registerContentType - // - registerContentType: function(ext, type) - { - this._handler.registerContentType(ext, type); - }, - - // - // see nsIHttpServer.serverIdentity - // - get identity() - { - return this._identity; - }, - - // - // see nsIHttpServer.getState - // - getState: function(path, k) - { - return this._handler._getState(path, k); - }, - - // - // see nsIHttpServer.setState - // - setState: function(path, k, v) - { - return this._handler._setState(path, k, v); - }, - - // - // see nsIHttpServer.getSharedState - // - getSharedState: function(k) - { - return this._handler._getSharedState(k); - }, - - // - // see nsIHttpServer.setSharedState - // - setSharedState: function(k, v) - { - return this._handler._setSharedState(k, v); - }, - - // - // see nsIHttpServer.getObjectState - // - getObjectState: function(k) - { - return this._handler._getObjectState(k); - }, - - // - // see nsIHttpServer.setObjectState - // - setObjectState: function(k, v) - { - return this._handler._setObjectState(k, v); - }, - - - // NSISUPPORTS - - // - // see nsISupports.QueryInterface - // - QueryInterface: function(iid) - { - if (iid.equals(Ci.nsIServerSocketListener) || iid.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - - // NON-XPCOM PUBLIC API - - /** -* Returns true iff this server is not running (and is not in the process of -* serving any requests still to be processed when the server was last -* stopped after being run). -*/ - isStopped: function() - { - return this._socketClosed && !this._hasOpenConnections(); - }, - - // PRIVATE IMPLEMENTATION - - /** True if this server has any open connections to it, false otherwise. */ - _hasOpenConnections: function() - { - // - // If we have any open connections, they're tracked as numeric properties on - // |this._connections|. The non-standard __count__ property could be used - // to check whether there are any properties, but standard-wise, even - // looking forward to ES5, there's no less ugly yet still O(1) way to do - // this. - // - for (var n in this._connections) - return true; - return false; - }, - - /** Calls the server-stopped callback provided when stop() was called. */ - _notifyStopped: function() - { - NS_ASSERT(this._stopCallback !== null, "double-notifying?"); - NS_ASSERT(!this._hasOpenConnections(), "should be done serving by now"); - - // - // NB: We have to grab this now, null out the member, *then* call the - // callback here, or otherwise the callback could (indirectly) futz with - // this._stopCallback by starting and immediately stopping this, at - // which point we'd be nulling out a field we no longer have a right to - // modify. - // - var callback = this._stopCallback; - this._stopCallback = null; - try - { - callback(); - } - catch (e) - { - // not throwing because this is specified as being usually (but not - // always) asynchronous - dump("!!! error running onStopped callback: " + e + "\n"); - } - }, - - /** -* Notifies this server that the given connection has been closed. -* -* @param connection : Connection -* the connection that was closed -*/ - _connectionClosed: function(connection) - { - NS_ASSERT(connection.number in this._connections, - "closing a connection " + this + " that we never added to the " + - "set of open connections?"); - NS_ASSERT(this._connections[connection.number] === connection, - "connection number mismatch? " + - this._connections[connection.number]); - delete this._connections[connection.number]; - - // Fire a pending server-stopped notification if it's our responsibility. - if (!this._hasOpenConnections() && this._socketClosed) - this._notifyStopped(); - }, - - /** -* Requests that the server be shut down when possible. -*/ - _requestQuit: function() - { - dumpn(">>> requesting a quit"); - dumpStack(); - this._doQuit = true; - } -}; - - -// -// RFC 2396 section 3.2.2: -// -// host = hostname | IPv4address -// hostname = *( domainlabel "." ) toplabel [ "." ] -// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum -// toplabel = alpha | alpha *( alphanum | "-" ) alphanum -// IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit -// - -const HOST_REGEX = - new RegExp("^(?:" + - // *( domainlabel "." ) - "(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)*" + - // toplabel - "[a-z](?:[a-z0-9-]*[a-z0-9])?" + - "|" + - // IPv4 address - "\\d+\\.\\d+\\.\\d+\\.\\d+" + - ")$", - "i"); - - -/** -* Represents the identity of a server. An identity consists of a set of -* (scheme, host, port) tuples denoted as locations (allowing a single server to -* serve multiple sites or to be used behind both HTTP and HTTPS proxies for any -* host/port). Any incoming request must be to one of these locations, or it -* will be rejected with an HTTP 400 error. One location, denoted as the -* primary location, is the location assigned in contexts where a location -* cannot otherwise be endogenously derived, such as for HTTP/1.0 requests. -* -* A single identity may contain at most one location per unique host/port pair; -* other than that, no restrictions are placed upon what locations may -* constitute an identity. -*/ -function ServerIdentity() -{ - /** The scheme of the primary location. */ - this._primaryScheme = "http"; - - /** The hostname of the primary location. */ - this._primaryHost = "127.0.0.1" - - /** The port number of the primary location. */ - this._primaryPort = -1; - - /** -* The current port number for the corresponding server, stored so that a new -* primary location can always be set if the current one is removed. -*/ - this._defaultPort = -1; - - /** -* Maps hosts to maps of ports to schemes, e.g. the following would represent -* https://example.com:789/ and http://example.org/: -* -* { -* "xexample.com": { 789: "https" }, -* "xexample.org": { 80: "http" } -* } -* -* Note the "x" prefix on hostnames, which prevents collisions with special -* JS names like "prototype". -*/ - this._locations = { "xlocalhost": {} }; -} -ServerIdentity.prototype = -{ - // NSIHTTPSERVERIDENTITY - - // - // see nsIHttpServerIdentity.primaryScheme - // - get primaryScheme() - { - if (this._primaryPort === -1) - throw Cr.NS_ERROR_NOT_INITIALIZED; - return this._primaryScheme; - }, - - // - // see nsIHttpServerIdentity.primaryHost - // - get primaryHost() - { - if (this._primaryPort === -1) - throw Cr.NS_ERROR_NOT_INITIALIZED; - return this._primaryHost; - }, - - // - // see nsIHttpServerIdentity.primaryPort - // - get primaryPort() - { - if (this._primaryPort === -1) - throw Cr.NS_ERROR_NOT_INITIALIZED; - return this._primaryPort; - }, - - // - // see nsIHttpServerIdentity.add - // - add: function(scheme, host, port) - { - this._validate(scheme, host, port); - - var entry = this._locations["x" + host]; - if (!entry) - this._locations["x" + host] = entry = {}; - - entry[port] = scheme; - }, - - // - // see nsIHttpServerIdentity.remove - // - remove: function(scheme, host, port) - { - this._validate(scheme, host, port); - - var entry = this._locations["x" + host]; - if (!entry) - return false; - - var present = port in entry; - delete entry[port]; - - if (this._primaryScheme == scheme && - this._primaryHost == host && - this._primaryPort == port && - this._defaultPort !== -1) - { - // Always keep at least one identity in existence at any time, unless - // we're in the process of shutting down (the last condition above). - this._primaryPort = -1; - this._initialize(this._defaultPort, host, false); - } - - return present; - }, - - // - // see nsIHttpServerIdentity.has - // - has: function(scheme, host, port) - { - this._validate(scheme, host, port); - - return "x" + host in this._locations && - scheme === this._locations["x" + host][port]; - }, - - // - // see nsIHttpServerIdentity.has - // - getScheme: function(host, port) - { - this._validate("http", host, port); - - var entry = this._locations["x" + host]; - if (!entry) - return ""; - - return entry[port] || ""; - }, - - // - // see nsIHttpServerIdentity.setPrimary - // - setPrimary: function(scheme, host, port) - { - this._validate(scheme, host, port); - - this.add(scheme, host, port); - - this._primaryScheme = scheme; - this._primaryHost = host; - this._primaryPort = port; - }, - - - // NSISUPPORTS - - // - // see nsISupports.QueryInterface - // - QueryInterface: function(iid) - { - if (iid.equals(Ci.nsIHttpServerIdentity) || iid.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - - // PRIVATE IMPLEMENTATION - - /** -* Initializes the primary name for the corresponding server, based on the -* provided port number. -*/ - _initialize: function(port, host, addSecondaryDefault) - { - this._host = host; - if (this._primaryPort !== -1) - this.add("http", host, port); - else - this.setPrimary("http", "localhost", port); - this._defaultPort = port; - - // Only add this if we're being called at server startup - if (addSecondaryDefault && host != "127.0.0.1") - this.add("http", "127.0.0.1", port); - }, - - /** -* Called at server shutdown time, unsets the primary location only if it was -* the default-assigned location and removes the default location from the -* set of locations used. -*/ - _teardown: function() - { - if (this._host != "127.0.0.1") { - // Not the default primary location, nothing special to do here - this.remove("http", "127.0.0.1", this._defaultPort); - } - - // This is a *very* tricky bit of reasoning here; make absolutely sure the - // tests for this code pass before you commit changes to it. - if (this._primaryScheme == "http" && - this._primaryHost == this._host && - this._primaryPort == this._defaultPort) - { - // Make sure we don't trigger the readding logic in .remove(), then remove - // the default location. - var port = this._defaultPort; - this._defaultPort = -1; - this.remove("http", this._host, port); - - // Ensure a server start triggers the setPrimary() path in ._initialize() - this._primaryPort = -1; - } - else - { - // No reason not to remove directly as it's not our primary location - this.remove("http", this._host, this._defaultPort); - } - }, - - /** -* Ensures scheme, host, and port are all valid with respect to RFC 2396. -* -* @throws NS_ERROR_ILLEGAL_VALUE -* if any argument doesn't match the corresponding production -*/ - _validate: function(scheme, host, port) - { - if (scheme !== "http" && scheme !== "https") - { - dumpn("*** server only supports http/https schemes: '" + scheme + "'"); - dumpStack(); - throw Cr.NS_ERROR_ILLEGAL_VALUE; - } - if (!HOST_REGEX.test(host)) - { - dumpn("*** unexpected host: '" + host + "'"); - throw Cr.NS_ERROR_ILLEGAL_VALUE; - } - if (port < 0 || port > 65535) - { - dumpn("*** unexpected port: '" + port + "'"); - throw Cr.NS_ERROR_ILLEGAL_VALUE; - } - } -}; - - -/** -* Represents a connection to the server (and possibly in the future the thread -* on which the connection is processed). -* -* @param input : nsIInputStream -* stream from which incoming data on the connection is read -* @param output : nsIOutputStream -* stream to write data out the connection -* @param server : nsHttpServer -* the server handling the connection -* @param port : int -* the port on which the server is running -* @param outgoingPort : int -* the outgoing port used by this connection -* @param number : uint -* a serial number used to uniquely identify this connection -*/ -function Connection(input, output, server, port, outgoingPort, number) -{ - dumpn("*** opening new connection " + number + " on port " + outgoingPort); - - /** Stream of incoming data. */ - this.input = input; - - /** Stream for outgoing data. */ - this.output = output; - - /** The server associated with this request. */ - this.server = server; - - /** The port on which the server is running. */ - this.port = port; - - /** The outgoing poort used by this connection. */ - this._outgoingPort = outgoingPort; - - /** The serial number of this connection. */ - this.number = number; - - /** -* The request for which a response is being generated, null if the -* incoming request has not been fully received or if it had errors. -*/ - this.request = null; - - /** State variables for debugging. */ - this._closed = this._processed = false; -} -Connection.prototype = -{ - /** Closes this connection's input/output streams. */ - close: function() - { - dumpn("*** closing connection " + this.number + - " on port " + this._outgoingPort); - - this.input.close(); - this.output.close(); - this._closed = true; - - var server = this.server; - server._connectionClosed(this); - - // If an error triggered a server shutdown, act on it now - if (server._doQuit) - server.stop(function() { /* not like we can do anything better */ }); - }, - - /** -* Initiates processing of this connection, using the data in the given -* request. -* -* @param request : Request -* the request which should be processed -*/ - process: function(request) - { - NS_ASSERT(!this._closed && !this._processed); - - this._processed = true; - - this.request = request; - this.server._handler.handleResponse(this); - }, - - /** -* Initiates processing of this connection, generating a response with the -* given HTTP error code. -* -* @param code : uint -* an HTTP code, so in the range [0, 1000) -* @param request : Request -* incomplete data about the incoming request (since there were errors -* during its processing -*/ - processError: function(code, request) - { - NS_ASSERT(!this._closed && !this._processed); - - this._processed = true; - this.request = request; - this.server._handler.handleError(code, this); - }, - - /** Converts this to a string for debugging purposes. */ - toString: function() - { - return "<Connection(" + this.number + - (this.request ? ", " + this.request.path : "") +"): " + - (this._closed ? "closed" : "open") + ">"; - } -}; - - - -/** Returns an array of count bytes from the given input stream. */ -function readBytes(inputStream, count) -{ - return new BinaryInputStream(inputStream).readByteArray(count); -} - - - -/** Request reader processing states; see RequestReader for details. */ -const READER_IN_REQUEST_LINE = 0; -const READER_IN_HEADERS = 1; -const READER_IN_BODY = 2; -const READER_FINISHED = 3; - - -/** -* Reads incoming request data asynchronously, does any necessary preprocessing, -* and forwards it to the request handler. Processing occurs in three states: -* -* READER_IN_REQUEST_LINE Reading the request's status line -* READER_IN_HEADERS Reading headers in the request -* READER_IN_BODY Reading the body of the request -* READER_FINISHED Entire request has been read and processed -* -* During the first two stages, initial metadata about the request is gathered -* into a Request object. Once the status line and headers have been processed, -* we start processing the body of the request into the Request. Finally, when -* the entire body has been read, we create a Response and hand it off to the -* ServerHandler to be given to the appropriate request handler. -* -* @param connection : Connection -* the connection for the request being read -*/ -function RequestReader(connection) -{ - /** Connection metadata for this request. */ - this._connection = connection; - - /** -* A container providing line-by-line access to the raw bytes that make up the -* data which has been read from the connection but has not yet been acted -* upon (by passing it to the request handler or by extracting request -* metadata from it). -*/ - this._data = new LineData(); - - /** -* The amount of data remaining to be read from the body of this request. -* After all headers in the request have been read this is the value in the -* Content-Length header, but as the body is read its value decreases to zero. -*/ - this._contentLength = 0; - - /** The current state of parsing the incoming request. */ - this._state = READER_IN_REQUEST_LINE; - - /** Metadata constructed from the incoming request for the request handler. */ - this._metadata = new Request(connection.port); - - /** -* Used to preserve state if we run out of line data midway through a -* multi-line header. _lastHeaderName stores the name of the header, while -* _lastHeaderValue stores the value we've seen so far for the header. -* -* These fields are always either both undefined or both strings. -*/ - this._lastHeaderName = this._lastHeaderValue = undefined; -} -RequestReader.prototype = -{ - // NSIINPUTSTREAMCALLBACK - - /** -* Called when more data from the incoming request is available. This method -* then reads the available data from input and deals with that data as -* necessary, depending upon the syntax of already-downloaded data. -* -* @param input : nsIAsyncInputStream -* the stream of incoming data from the connection -*/ - onInputStreamReady: function(input) - { - dumpn("*** onInputStreamReady(input=" + input + ") on thread " + - gThreadManager.currentThread + " (main is " + - gThreadManager.mainThread + ")"); - dumpn("*** this._state == " + this._state); - - // Handle cases where we get more data after a request error has been - // discovered but *before* we can close the connection. - var data = this._data; - if (!data) - return; - - try - { - data.appendBytes(readBytes(input, input.available())); - } - catch (e) - { - if (streamClosed(e)) - { - dumpn("*** WARNING: unexpected error when reading from socket; will " + - "be treated as if the input stream had been closed"); - dumpn("*** WARNING: actual error was: " + e); - } - - // We've lost a race -- input has been closed, but we're still expecting - // to read more data. available() will throw in this case, and since - // we're dead in the water now, destroy the connection. - dumpn("*** onInputStreamReady called on a closed input, destroying " + - "connection"); - this._connection.close(); - return; - } - - switch (this._state) - { - default: - NS_ASSERT(false, "invalid state: " + this._state); - break; - - case READER_IN_REQUEST_LINE: - if (!this._processRequestLine()) - break; - /* fall through */ - - case READER_IN_HEADERS: - if (!this._processHeaders()) - break; - /* fall through */ - - case READER_IN_BODY: - this._processBody(); - } - - if (this._state != READER_FINISHED) - input.asyncWait(this, 0, 0, gThreadManager.currentThread); - }, - - // - // see nsISupports.QueryInterface - // - QueryInterface: function(aIID) - { - if (aIID.equals(Ci.nsIInputStreamCallback) || - aIID.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - - // PRIVATE API - - /** -* Processes unprocessed, downloaded data as a request line. -* -* @returns boolean -* true iff the request line has been fully processed -*/ - _processRequestLine: function() - { - NS_ASSERT(this._state == READER_IN_REQUEST_LINE); - - // Servers SHOULD ignore any empty line(s) received where a Request-Line - // is expected (section 4.1). - var data = this._data; - var line = {}; - var readSuccess; - while ((readSuccess = data.readLine(line)) && line.value == "") - dumpn("*** ignoring beginning blank line..."); - - // if we don't have a full line, wait until we do - if (!readSuccess) - return false; - - // we have the first non-blank line - try - { - this._parseRequestLine(line.value); - this._state = READER_IN_HEADERS; - return true; - } - catch (e) - { - this._handleError(e); - return false; - } - }, - - /** -* Processes stored data, assuming it is either at the beginning or in -* the middle of processing request headers. -* -* @returns boolean -* true iff header data in the request has been fully processed -*/ - _processHeaders: function() - { - NS_ASSERT(this._state == READER_IN_HEADERS); - - // XXX things to fix here: - // - // - need to support RFC 2047-encoded non-US-ASCII characters - - try - { - var done = this._parseHeaders(); - if (done) - { - var request = this._metadata; - - // XXX this is wrong for requests with transfer-encodings applied to - // them, particularly chunked (which by its nature can have no - // meaningful Content-Length header)! - this._contentLength = request.hasHeader("Content-Length") - ? parseInt(request.getHeader("Content-Length"), 10) - : 0; - dumpn("_processHeaders, Content-length=" + this._contentLength); - - this._state = READER_IN_BODY; - } - return done; - } - catch (e) - { - this._handleError(e); - return false; - } - }, - - /** -* Processes stored data, assuming it is either at the beginning or in -* the middle of processing the request body. -* -* @returns boolean -* true iff the request body has been fully processed -*/ - _processBody: function() - { - NS_ASSERT(this._state == READER_IN_BODY); - - // XXX handle chunked transfer-coding request bodies! - - try - { - if (this._contentLength > 0) - { - var data = this._data.purge(); - var count = Math.min(data.length, this._contentLength); - dumpn("*** loading data=" + data + " len=" + data.length + - " excess=" + (data.length - count)); - - var bos = new BinaryOutputStream(this._metadata._bodyOutputStream); - bos.writeByteArray(data, count); - this._contentLength -= count; - } - - dumpn("*** remaining body data len=" + this._contentLength); - if (this._contentLength == 0) - { - this._validateRequest(); - this._state = READER_FINISHED; - this._handleResponse(); - return true; - } - - return false; - } - catch (e) - { - this._handleError(e); - return false; - } - }, - - /** -* Does various post-header checks on the data in this request. -* -* @throws : HttpError -* if the request was malformed in some way -*/ - _validateRequest: function() - { - NS_ASSERT(this._state == READER_IN_BODY); - - dumpn("*** _validateRequest"); - - var metadata = this._metadata; - var headers = metadata._headers; - - // 19.6.1.1 -- servers MUST report 400 to HTTP/1.1 requests w/o Host header - var identity = this._connection.server.identity; - if (metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_1)) - { - if (!headers.hasHeader("Host")) - { - dumpn("*** malformed HTTP/1.1 or greater request with no Host header!"); - throw HTTP_400; - } - - // If the Request-URI wasn't absolute, then we need to determine our host. - // We have to determine what scheme was used to access us based on the - // server identity data at this point, because the request just doesn't - // contain enough data on its own to do this, sadly. - if (!metadata._host) - { - var host, port; - var hostPort = headers.getHeader("Host"); - var colon = hostPort.indexOf(":"); - if (colon < 0) - { - host = hostPort; - port = ""; - } - else - { - host = hostPort.substring(0, colon); - port = hostPort.substring(colon + 1); - } - - // NB: We allow an empty port here because, oddly, a colon may be - // present even without a port number, e.g. "example.com:"; in this - // case the default port applies. - if (!HOST_REGEX.test(host) || !/^\d*$/.test(port)) - { - dumpn("*** malformed hostname (" + hostPort + ") in Host " + - "header, 400 time"); - throw HTTP_400; - } - - // If we're not given a port, we're stuck, because we don't know what - // scheme to use to look up the correct port here, in general. Since - // the HTTPS case requires a tunnel/proxy and thus requires that the - // requested URI be absolute (and thus contain the necessary - // information), let's assume HTTP will prevail and use that. - port = +port || 80; - - var scheme = identity.getScheme(host, port); - if (!scheme) - { - dumpn("*** unrecognized hostname (" + hostPort + ") in Host " + - "header, 400 time"); - throw HTTP_400; - } - - metadata._scheme = scheme; - metadata._host = host; - metadata._port = port; - } - } - else - { - NS_ASSERT(metadata._host === undefined, - "HTTP/1.0 doesn't allow absolute paths in the request line!"); - - metadata._scheme = identity.primaryScheme; - metadata._host = identity.primaryHost; - metadata._port = identity.primaryPort; - } - - NS_ASSERT(identity.has(metadata._scheme, metadata._host, metadata._port), - "must have a location we recognize by now!"); - }, - - /** -* Handles responses in case of error, either in the server or in the request. -* -* @param e -* the specific error encountered, which is an HttpError in the case where -* the request is in some way invalid or cannot be fulfilled; if this isn't -* an HttpError we're going to be paranoid and shut down, because that -* shouldn't happen, ever -*/ - _handleError: function(e) - { - // Don't fall back into normal processing! - this._state = READER_FINISHED; - - var server = this._connection.server; - if (e instanceof HttpError) - { - var code = e.code; - } - else - { - dumpn("!!! UNEXPECTED ERROR: " + e + - (e.lineNumber ? ", line " + e.lineNumber : "")); - - // no idea what happened -- be paranoid and shut down - code = 500; - server._requestQuit(); - } - - // make attempted reuse of data an error - this._data = null; - - this._connection.processError(code, this._metadata); - }, - - /** -* Now that we've read the request line and headers, we can actually hand off -* the request to be handled. -* -* This method is called once per request, after the request line and all -* headers and the body, if any, have been received. -*/ - _handleResponse: function() - { - NS_ASSERT(this._state == READER_FINISHED); - - // We don't need the line-based data any more, so make attempted reuse an - // error. - this._data = null; - - this._connection.process(this._metadata); - }, - - - // PARSING - - /** -* Parses the request line for the HTTP request associated with this. -* -* @param line : string -* the request line -*/ - _parseRequestLine: function(line) - { - NS_ASSERT(this._state == READER_IN_REQUEST_LINE); - - dumpn("*** _parseRequestLine('" + line + "')"); - - var metadata = this._metadata; - - // clients and servers SHOULD accept any amount of SP or HT characters - // between fields, even though only a single SP is required (section 19.3) - var request = line.split(/[ \t]+/); - if (!request || request.length != 3) - throw HTTP_400; - - metadata._method = request[0]; - - // get the HTTP version - var ver = request[2]; - var match = ver.match(/^HTTP\/(\d+\.\d+)$/); - if (!match) - throw HTTP_400; - - // determine HTTP version - try - { - metadata._httpVersion = new nsHttpVersion(match[1]); - if (!metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_0)) - throw "unsupported HTTP version"; - } - catch (e) - { - // we support HTTP/1.0 and HTTP/1.1 only - throw HTTP_501; - } - - - var fullPath = request[1]; - var serverIdentity = this._connection.server.identity; - - var scheme, host, port; - - if (fullPath.charAt(0) != "/") - { - // No absolute paths in the request line in HTTP prior to 1.1 - if (!metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_1)) - throw HTTP_400; - - try - { - var uri = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService) - .newURI(fullPath, null, null); - fullPath = uri.path; - scheme = uri.scheme; - host = metadata._host = uri.asciiHost; - port = uri.port; - if (port === -1) - { - if (scheme === "http") - port = 80; - else if (scheme === "https") - port = 443; - else - throw HTTP_400; - } - } - catch (e) - { - // If the host is not a valid host on the server, the response MUST be a - // 400 (Bad Request) error message (section 5.2). Alternately, the URI - // is malformed. - throw HTTP_400; - } - - if (!serverIdentity.has(scheme, host, port) || fullPath.charAt(0) != "/") - throw HTTP_400; - } - - var splitter = fullPath.indexOf("?"); - if (splitter < 0) - { - // _queryString already set in ctor - metadata._path = fullPath; - } - else - { - metadata._path = fullPath.substring(0, splitter); - metadata._queryString = fullPath.substring(splitter + 1); - } - - metadata._scheme = scheme; - metadata._host = host; - metadata._port = port; - }, - - /** -* Parses all available HTTP headers in this until the header-ending CRLFCRLF, -* adding them to the store of headers in the request. -* -* @throws -* HTTP_400 if the headers are malformed -* @returns boolean -* true if all headers have now been processed, false otherwise -*/ - _parseHeaders: function() - { - NS_ASSERT(this._state == READER_IN_HEADERS); - - dumpn("*** _parseHeaders"); - - var data = this._data; - - var headers = this._metadata._headers; - var lastName = this._lastHeaderName; - var lastVal = this._lastHeaderValue; - - var line = {}; - while (true) - { - NS_ASSERT(!((lastVal === undefined) ^ (lastName === undefined)), - lastName === undefined ? - "lastVal without lastName? lastVal: '" + lastVal + "'" : - "lastName without lastVal? lastName: '" + lastName + "'"); - - if (!data.readLine(line)) - { - // save any data we have from the header we might still be processing - this._lastHeaderName = lastName; - this._lastHeaderValue = lastVal; - return false; - } - - var lineText = line.value; - var firstChar = lineText.charAt(0); - - // blank line means end of headers - if (lineText == "") - { - // we're finished with the previous header - if (lastName) - { - try - { - headers.setHeader(lastName, lastVal, true); - } - catch (e) - { - dumpn("*** e == " + e); - throw HTTP_400; - } - } - else - { - // no headers in request -- valid for HTTP/1.0 requests - } - - // either way, we're done processing headers - this._state = READER_IN_BODY; - return true; - } - else if (firstChar == " " || firstChar == "\t") - { - // multi-line header if we've already seen a header line - if (!lastName) - { - // we don't have a header to continue! - throw HTTP_400; - } - - // append this line's text to the value; starts with SP/HT, so no need - // for separating whitespace - lastVal += lineText; - } - else - { - // we have a new header, so set the old one (if one existed) - if (lastName) - { - try - { - headers.setHeader(lastName, lastVal, true); - } - catch (e) - { - dumpn("*** e == " + e); - throw HTTP_400; - } - } - - var colon = lineText.indexOf(":"); // first colon must be splitter - if (colon < 1) - { - // no colon or missing header field-name - throw HTTP_400; - } - - // set header name, value (to be set in the next loop, usually) - lastName = lineText.substring(0, colon); - lastVal = lineText.substring(colon + 1); - } // empty, continuation, start of header - } // while (true) - } -}; - - -/** The character codes for CR and LF. */ -const CR = 0x0D, LF = 0x0A; - -/** -* Calculates the number of characters before the first CRLF pair in array, or -* -1 if the array contains no CRLF pair. -* -* @param array : Array -* an array of numbers in the range [0, 256), each representing a single -* character; the first CRLF is the lowest index i where -* |array[i] == "\r".charCodeAt(0)| and |array[i+1] == "\n".charCodeAt(0)|, -* if such an |i| exists, and -1 otherwise -* @returns int -* the index of the first CRLF if any were present, -1 otherwise -*/ -function findCRLF(array) -{ - for (var i = array.indexOf(CR); i >= 0; i = array.indexOf(CR, i + 1)) - { - if (array[i + 1] == LF) - return i; - } - return -1; -} - - -/** -* A container which provides line-by-line access to the arrays of bytes with -* which it is seeded. -*/ -function LineData() -{ - /** An array of queued bytes from which to get line-based characters. */ - this._data = []; -} -LineData.prototype = -{ - /** -* Appends the bytes in the given array to the internal data cache maintained -* by this. -*/ - appendBytes: function(bytes) - { - Array.prototype.push.apply(this._data, bytes); - }, - - /** -* Removes and returns a line of data, delimited by CRLF, from this. -* -* @param out -* an object whose "value" property will be set to the first line of text -* present in this, sans CRLF, if this contains a full CRLF-delimited line -* of text; if this doesn't contain enough data, the value of the property -* is undefined -* @returns boolean -* true if a full line of data could be read from the data in this, false -* otherwise -*/ - readLine: function(out) - { - var data = this._data; - var length = findCRLF(data); - if (length < 0) - return false; - - // - // We have the index of the CR, so remove all the characters, including - // CRLF, from the array with splice, and convert the removed array into the - // corresponding string, from which we then strip the trailing CRLF. - // - // Getting the line in this matter acknowledges that substring is an O(1) - // operation in SpiderMonkey because strings are immutable, whereas two - // splices, both from the beginning of the data, are less likely to be as - // cheap as a single splice plus two extra character conversions. - // - var line = String.fromCharCode.apply(null, data.splice(0, length + 2)); - out.value = line.substring(0, length); - - return true; - }, - - /** -* Removes the bytes currently within this and returns them in an array. -* -* @returns Array -* the bytes within this when this method is called -*/ - purge: function() - { - var data = this._data; - this._data = []; - return data; - } -}; - - - -/** -* Creates a request-handling function for an nsIHttpRequestHandler object. -*/ -function createHandlerFunc(handler) -{ - return function(metadata, response) { handler.handle(metadata, response); }; -} - - -/** -* The default handler for directories; writes an HTML response containing a -* slightly-formatted directory listing. -*/ -function defaultIndexHandler(metadata, response) -{ - response.setHeader("Content-Type", "text/html", false); - - var path = htmlEscape(decodeURI(metadata.path)); - - // - // Just do a very basic bit of directory listings -- no need for too much - // fanciness, especially since we don't have a style sheet in which we can - // stick rules (don't want to pollute the default path-space). - // - - var body = '<html>\ -<head>\ -<title>' + path + '</title>\ -</head>\ -<body>\ -<h1>' + path + '</h1>\ -<ol style="list-style-type: none">'; - - var directory = metadata.getProperty("directory").QueryInterface(Ci.nsILocalFile); - NS_ASSERT(directory && directory.isDirectory()); - - var fileList = []; - var files = directory.directoryEntries; - while (files.hasMoreElements()) - { - var f = files.getNext().QueryInterface(Ci.nsIFile); - var name = f.leafName; - if (!f.isHidden() && - (name.charAt(name.length - 1) != HIDDEN_CHAR || - name.charAt(name.length - 2) == HIDDEN_CHAR)) - fileList.push(f); - } - - fileList.sort(fileSort); - - for (var i = 0; i < fileList.length; i++) - { - var file = fileList[i]; - try - { - var name = file.leafName; - if (name.charAt(name.length - 1) == HIDDEN_CHAR) - name = name.substring(0, name.length - 1); - var sep = file.isDirectory() ? "/" : ""; - - // Note: using " to delimit the attribute here because encodeURIComponent - // passes through '. - var item = '<li><a href="' + encodeURIComponent(name) + sep + '">' + - htmlEscape(name) + sep + - '</a></li>'; - - body += item; - } - catch (e) { /* some file system error, ignore the file */ } - } - - body += ' </ol>\ -</body>\ -</html>'; - - response.bodyOutputStream.write(body, body.length); -} - -/** -* Sorts a and b (nsIFile objects) into an aesthetically pleasing order. -*/ -function fileSort(a, b) -{ - var dira = a.isDirectory(), dirb = b.isDirectory(); - - if (dira && !dirb) - return -1; - if (dirb && !dira) - return 1; - - var namea = a.leafName.toLowerCase(), nameb = b.leafName.toLowerCase(); - return nameb > namea ? -1 : 1; -} - - -/** -* Converts an externally-provided path into an internal path for use in -* determining file mappings. -* -* @param path -* the path to convert -* @param encoded -* true if the given path should be passed through decodeURI prior to -* conversion -* @throws URIError -* if path is incorrectly encoded -*/ -function toInternalPath(path, encoded) -{ - if (encoded) - path = decodeURI(path); - - var comps = path.split("/"); - for (var i = 0, sz = comps.length; i < sz; i++) - { - var comp = comps[i]; - if (comp.charAt(comp.length - 1) == HIDDEN_CHAR) - comps[i] = comp + HIDDEN_CHAR; - } - return comps.join("/"); -} - - -/** -* Adds custom-specified headers for the given file to the given response, if -* any such headers are specified. -* -* @param file -* the file on the disk which is to be written -* @param metadata -* metadata about the incoming request -* @param response -* the Response to which any specified headers/data should be written -* @throws HTTP_500 -* if an error occurred while processing custom-specified headers -*/ -function maybeAddHeaders(file, metadata, response) -{ - var name = file.leafName; - if (name.charAt(name.length - 1) == HIDDEN_CHAR) - name = name.substring(0, name.length - 1); - - var headerFile = file.parent; - headerFile.append(name + HEADERS_SUFFIX); - - if (!headerFile.exists()) - return; - - const PR_RDONLY = 0x01; - var fis = new FileInputStream(headerFile, PR_RDONLY, parseInt("444", 8), - Ci.nsIFileInputStream.CLOSE_ON_EOF); - - try - { - var lis = new ConverterInputStream(fis, "UTF-8", 1024, 0x0); - lis.QueryInterface(Ci.nsIUnicharLineInputStream); - - var line = {value: ""}; - var more = lis.readLine(line); - - if (!more && line.value == "") - return; - - - // request line - - var status = line.value; - if (status.indexOf("HTTP ") == 0) - { - status = status.substring(5); - var space = status.indexOf(" "); - var code, description; - if (space < 0) - { - code = status; - description = ""; - } - else - { - code = status.substring(0, space); - description = status.substring(space + 1, status.length); - } - - response.setStatusLine(metadata.httpVersion, parseInt(code, 10), description); - - line.value = ""; - more = lis.readLine(line); - } - - // headers - while (more || line.value != "") - { - var header = line.value; - var colon = header.indexOf(":"); - - response.setHeader(header.substring(0, colon), - header.substring(colon + 1, header.length), - false); // allow overriding server-set headers - - line.value = ""; - more = lis.readLine(line); - } - } - catch (e) - { - dumpn("WARNING: error in headers for " + metadata.path + ": " + e); - throw HTTP_500; - } - finally - { - fis.close(); - } -} - - -/** -* An object which handles requests for a server, executing default and -* overridden behaviors as instructed by the code which uses and manipulates it. -* Default behavior includes the paths / and /trace (diagnostics), with some -* support for HTTP error pages for various codes and fallback to HTTP 500 if -* those codes fail for any reason. -* -* @param server : nsHttpServer -* the server in which this handler is being used -*/ -function ServerHandler(server) -{ - // FIELDS - - /** -* The nsHttpServer instance associated with this handler. -*/ - this._server = server; - - /** -* A FileMap object containing the set of path->nsILocalFile mappings for -* all directory mappings set in the server (e.g., "/" for /var/www/html/, -* "/foo/bar/" for /local/path/, and "/foo/bar/baz/" for /local/path2). -* -* Note carefully: the leading and trailing "/" in each path (not file) are -* removed before insertion to simplify the code which uses this. You have -* been warned! -*/ - this._pathDirectoryMap = new FileMap(); - - /** -* Custom request handlers for the server in which this resides. Path-handler -* pairs are stored as property-value pairs in this property. -* -* @see ServerHandler.prototype._defaultPaths -*/ - this._overridePaths = {}; - - /** -* Custom request handlers for the error handlers in the server in which this -* resides. Path-handler pairs are stored as property-value pairs in this -* property. -* -* @see ServerHandler.prototype._defaultErrors -*/ - this._overrideErrors = {}; - - /** -* Maps file extensions to their MIME types in the server, overriding any -* mapping that might or might not exist in the MIME service. -*/ - this._mimeMappings = {}; - - /** -* The default handler for requests for directories, used to serve directories -* when no index file is present. -*/ - this._indexHandler = defaultIndexHandler; - - /** Per-path state storage for the server. */ - this._state = {}; - - /** Entire-server state storage. */ - this._sharedState = {}; - - /** Entire-server state storage for nsISupports values. */ - this._objectState = {}; -} -ServerHandler.prototype = -{ - // PUBLIC API - - /** -* Handles a request to this server, responding to the request appropriately -* and initiating server shutdown if necessary. -* -* This method never throws an exception. -* -* @param connection : Connection -* the connection for this request -*/ - handleResponse: function(connection) - { - var request = connection.request; - var response = new Response(connection); - - var path = request.path; - dumpn("*** path == " + path); - - try - { - try - { - if (path in this._overridePaths) - { - // explicit paths first, then files based on existing directory mappings, - // then (if the file doesn't exist) built-in server default paths - dumpn("calling override for " + path); - this._overridePaths[path](request, response); - } - else - { - this._handleDefault(request, response); - } - } - catch (e) - { - if (response.partiallySent()) - { - response.abort(e); - return; - } - - if (!(e instanceof HttpError)) - { - dumpn("*** unexpected error: e == " + e); - throw HTTP_500; - } - if (e.code !== 404) - throw e; - - dumpn("*** default: " + (path in this._defaultPaths)); - - response = new Response(connection); - if (path in this._defaultPaths) - this._defaultPaths[path](request, response); - else - throw HTTP_404; - } - } - catch (e) - { - if (response.partiallySent()) - { - response.abort(e); - return; - } - - var errorCode = "internal"; - - try - { - if (!(e instanceof HttpError)) - throw e; - - errorCode = e.code; - dumpn("*** errorCode == " + errorCode); - - response = new Response(connection); - if (e.customErrorHandling) - e.customErrorHandling(response); - this._handleError(errorCode, request, response); - return; - } - catch (e2) - { - dumpn("*** error handling " + errorCode + " error: " + - "e2 == " + e2 + ", shutting down server"); - - connection.server._requestQuit(); - response.abort(e2); - return; - } - } - - response.complete(); - }, - - // - // see nsIHttpServer.registerFile - // - registerFile: function(path, file) - { - if (!file) - { - dumpn("*** unregistering '" + path + "' mapping"); - delete this._overridePaths[path]; - return; - } - - dumpn("*** registering '" + path + "' as mapping to " + file.path); - file = file.clone(); - - var self = this; - this._overridePaths[path] = - function(request, response) - { - if (!file.exists()) - throw HTTP_404; - - response.setStatusLine(request.httpVersion, 200, "OK"); - self._writeFileResponse(request, file, response, 0, file.fileSize); - }; - }, - - // - // see nsIHttpServer.registerPathHandler - // - registerPathHandler: function(path, handler) - { - // XXX true path validation! - if (path.charAt(0) != "/") - throw Cr.NS_ERROR_INVALID_ARG; - - this._handlerToField(handler, this._overridePaths, path); - }, - - // - // see nsIHttpServer.registerDirectory - // - registerDirectory: function(path, directory) - { - // strip off leading and trailing '/' so that we can use lastIndexOf when - // determining exactly how a path maps onto a mapped directory -- - // conditional is required here to deal with "/".substring(1, 0) being - // converted to "/".substring(0, 1) per the JS specification - var key = path.length == 1 ? "" : path.substring(1, path.length - 1); - - // the path-to-directory mapping code requires that the first character not - // be "/", or it will go into an infinite loop - if (key.charAt(0) == "/") - throw Cr.NS_ERROR_INVALID_ARG; - - key = toInternalPath(key, false); - - if (directory) - { - dumpn("*** mapping '" + path + "' to the location " + directory.path); - this._pathDirectoryMap.put(key, directory); - } - else - { - dumpn("*** removing mapping for '" + path + "'"); - this._pathDirectoryMap.put(key, null); - } - }, - - // - // see nsIHttpServer.registerErrorHandler - // - registerErrorHandler: function(err, handler) - { - if (!(err in HTTP_ERROR_CODES)) - dumpn("*** WARNING: registering non-HTTP/1.1 error code " + - "(" + err + ") handler -- was this intentional?"); - - this._handlerToField(handler, this._overrideErrors, err); - }, - - // - // see nsIHttpServer.setIndexHandler - // - setIndexHandler: function(handler) - { - if (!handler) - handler = defaultIndexHandler; - else if (typeof(handler) != "function") - handler = createHandlerFunc(handler); - - this._indexHandler = handler; - }, - - // - // see nsIHttpServer.registerContentType - // - registerContentType: function(ext, type) - { - if (!type) - delete this._mimeMappings[ext]; - else - this._mimeMappings[ext] = headerUtils.normalizeFieldValue(type); - }, - - // PRIVATE API - - /** -* Sets or remove (if handler is null) a handler in an object with a key. -* -* @param handler -* a handler, either function or an nsIHttpRequestHandler -* @param dict -* The object to attach the handler to. -* @param key -* The field name of the handler. -*/ - _handlerToField: function(handler, dict, key) - { - // for convenience, handler can be a function if this is run from xpcshell - if (typeof(handler) == "function") - dict[key] = handler; - else if (handler) - dict[key] = createHandlerFunc(handler); - else - delete dict[key]; - }, - - /** -* Handles a request which maps to a file in the local filesystem (if a base -* path has already been set; otherwise the 404 error is thrown). -* -* @param metadata : Request -* metadata for the incoming request -* @param response : Response -* an uninitialized Response to the given request, to be initialized by a -* request handler -* @throws HTTP_### -* if an HTTP error occurred (usually HTTP_404); note that in this case the -* calling code must handle post-processing of the response -*/ - _handleDefault: function(metadata, response) - { - dumpn("*** _handleDefault()"); - - response.setStatusLine(metadata.httpVersion, 200, "OK"); - - var path = metadata.path; - NS_ASSERT(path.charAt(0) == "/", "invalid path: <" + path + ">"); - - // determine the actual on-disk file; this requires finding the deepest - // path-to-directory mapping in the requested URL - var file = this._getFileForPath(path); - - // the "file" might be a directory, in which case we either serve the - // contained index.html or make the index handler write the response - if (file.exists() && file.isDirectory()) - { - file.append("index.html"); // make configurable? - if (!file.exists() || file.isDirectory()) - { - metadata._ensurePropertyBag(); - metadata._bag.setPropertyAsInterface("directory", file.parent); - this._indexHandler(metadata, response); - return; - } - } - - // alternately, the file might not exist - if (!file.exists()) - throw HTTP_404; - - var start, end; - if (metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_1) && - metadata.hasHeader("Range") && - this._getTypeFromFile(file) !== SJS_TYPE) - { - var rangeMatch = metadata.getHeader("Range").match(/^bytes=(\d+)?-(\d+)?$/); - if (!rangeMatch) - throw HTTP_400; - - if (rangeMatch[1] !== undefined) - start = parseInt(rangeMatch[1], 10); - - if (rangeMatch[2] !== undefined) - end = parseInt(rangeMatch[2], 10); - - if (start === undefined && end === undefined) - throw HTTP_400; - - // No start given, so the end is really the count of bytes from the - // end of the file. - if (start === undefined) - { - start = Math.max(0, file.fileSize - end); - end = file.fileSize - 1; - } - - // start and end are inclusive - if (end === undefined || end >= file.fileSize) - end = file.fileSize - 1; - - if (start !== undefined && start >= file.fileSize) { - var HTTP_416 = new HttpError(416, "Requested Range Not Satisfiable"); - HTTP_416.customErrorHandling = function(errorResponse) - { - maybeAddHeaders(file, metadata, errorResponse); - }; - throw HTTP_416; - } - - if (end < start) - { - response.setStatusLine(metadata.httpVersion, 200, "OK"); - start = 0; - end = file.fileSize - 1; - } - else - { - response.setStatusLine(metadata.httpVersion, 206, "Partial Content"); - var contentRange = "bytes " + start + "-" + end + "/" + file.fileSize; - response.setHeader("Content-Range", contentRange); - } - } - else - { - start = 0; - end = file.fileSize - 1; - } - - // finally... - dumpn("*** handling '" + path + "' as mapping to " + file.path + " from " + - start + " to " + end + " inclusive"); - this._writeFileResponse(metadata, file, response, start, end - start + 1); - }, - - /** -* Writes an HTTP response for the given file, including setting headers for -* file metadata. -* -* @param metadata : Request -* the Request for which a response is being generated -* @param file : nsILocalFile -* the file which is to be sent in the response -* @param response : Response -* the response to which the file should be written -* @param offset: uint -* the byte offset to skip to when writing -* @param count: uint -* the number of bytes to write -*/ - _writeFileResponse: function(metadata, file, response, offset, count) - { - const PR_RDONLY = 0x01; - - var type = this._getTypeFromFile(file); - if (type === SJS_TYPE) - { - var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8), - Ci.nsIFileInputStream.CLOSE_ON_EOF); - - try - { - var sis = new ScriptableInputStream(fis); - var s = Cu.Sandbox(gGlobalObject); - s.importFunction(dump, "dump"); - - // Define a basic key-value state-preservation API across requests, with - // keys initially corresponding to the empty string. - var self = this; - var path = metadata.path; - s.importFunction(function getState(k) - { - return self._getState(path, k); - }); - s.importFunction(function setState(k, v) - { - self._setState(path, k, v); - }); - s.importFunction(function getSharedState(k) - { - return self._getSharedState(k); - }); - s.importFunction(function setSharedState(k, v) - { - self._setSharedState(k, v); - }); - s.importFunction(function getObjectState(k, callback) - { - callback(self._getObjectState(k)); - }); - s.importFunction(function setObjectState(k, v) - { - self._setObjectState(k, v); - }); - s.importFunction(function registerPathHandler(p, h) - { - self.registerPathHandler(p, h); - }); - - // Make it possible for sjs files to access their location - this._setState(path, "__LOCATION__", file.path); - - try - { - // Alas, the line number in errors dumped to console when calling the - // request handler is simply an offset from where we load the SJS file. - // Work around this in a reasonably non-fragile way by dynamically - // getting the line number where we evaluate the SJS file. Don't - // separate these two lines! - var line = new Error().lineNumber; - Cu.evalInSandbox(sis.read(file.fileSize), s); - } - catch (e) - { - dumpn("*** syntax error in SJS at " + file.path + ": " + e); - throw HTTP_500; - } - - try - { - s.handleRequest(metadata, response); - } - catch (e) - { - dump("*** error running SJS at " + file.path + ": " + - e + " on line " + - (e instanceof Error - ? e.lineNumber + " in httpd.js" - : (e.lineNumber - line)) + "\n"); - throw HTTP_500; - } - } - finally - { - fis.close(); - } - } - else - { - try - { - response.setHeader("Last-Modified", - toDateString(file.lastModifiedTime), - false); - } - catch (e) { /* lastModifiedTime threw, ignore */ } - - response.setHeader("Content-Type", type, false); - maybeAddHeaders(file, metadata, response); - response.setHeader("Content-Length", "" + count, false); - - var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8), - Ci.nsIFileInputStream.CLOSE_ON_EOF); - - offset = offset || 0; - count = count || file.fileSize; - NS_ASSERT(offset === 0 || offset < file.fileSize, "bad offset"); - NS_ASSERT(count >= 0, "bad count"); - NS_ASSERT(offset + count <= file.fileSize, "bad total data size"); - - try - { - if (offset !== 0) - { - // Seek (or read, if seeking isn't supported) to the correct offset so - // the data sent to the client matches the requested range. - if (fis instanceof Ci.nsISeekableStream) - fis.seek(Ci.nsISeekableStream.NS_SEEK_SET, offset); - else - new ScriptableInputStream(fis).read(offset); - } - } - catch (e) - { - fis.close(); - throw e; - } - - function writeMore() - { - gThreadManager.currentThread - .dispatch(writeData, Ci.nsIThread.DISPATCH_NORMAL); - } - - var input = new BinaryInputStream(fis); - var output = new BinaryOutputStream(response.bodyOutputStream); - var writeData = - { - run: function() - { - var chunkSize = Math.min(65536, count); - count -= chunkSize; - NS_ASSERT(count >= 0, "underflow"); - - try - { - var data = input.readByteArray(chunkSize); - NS_ASSERT(data.length === chunkSize, - "incorrect data returned? got " + data.length + - ", expected " + chunkSize); - output.writeByteArray(data, data.length); - if (count === 0) - { - fis.close(); - response.finish(); - } - else - { - writeMore(); - } - } - catch (e) - { - try - { - fis.close(); - } - finally - { - response.finish(); - } - throw e; - } - } - }; - - writeMore(); - - // Now that we know copying will start, flag the response as async. - response.processAsync(); - } - }, - - /** -* Get the value corresponding to a given key for the given path for SJS state -* preservation across requests. -* -* @param path : string -* the path from which the given state is to be retrieved -* @param k : string -* the key whose corresponding value is to be returned -* @returns string -* the corresponding value, which is initially the empty string -*/ - _getState: function(path, k) - { - var state = this._state; - if (path in state && k in state[path]) - return state[path][k]; - return ""; - }, - - /** -* Set the value corresponding to a given key for the given path for SJS state -* preservation across requests. -* -* @param path : string -* the path from which the given state is to be retrieved -* @param k : string -* the key whose corresponding value is to be set -* @param v : string -* the value to be set -*/ - _setState: function(path, k, v) - { - if (typeof v !== "string") - throw new Error("non-string value passed"); - var state = this._state; - if (!(path in state)) - state[path] = {}; - state[path][k] = v; - }, - - /** -* Get the value corresponding to a given key for SJS state preservation -* across requests. -* -* @param k : string -* the key whose corresponding value is to be returned -* @returns string -* the corresponding value, which is initially the empty string -*/ - _getSharedState: function(k) - { - var state = this._sharedState; - if (k in state) - return state[k]; - return ""; - }, - - /** -* Set the value corresponding to a given key for SJS state preservation -* across requests. -* -* @param k : string -* the key whose corresponding value is to be set -* @param v : string -* the value to be set -*/ - _setSharedState: function(k, v) - { - if (typeof v !== "string") - throw new Error("non-string value passed"); - this._sharedState[k] = v; - }, - - /** -* Returns the object associated with the given key in the server for SJS -* state preservation across requests. -* -* @param k : string -* the key whose corresponding object is to be returned -* @returns nsISupports -* the corresponding object, or null if none was present -*/ - _getObjectState: function(k) - { - if (typeof k !== "string") - throw new Error("non-string key passed"); - return this._objectState[k] || null; - }, - - /** -* Sets the object associated with the given key in the server for SJS -* state preservation across requests. -* -* @param k : string -* the key whose corresponding object is to be set -* @param v : nsISupports -* the object to be associated with the given key; may be null -*/ - _setObjectState: function(k, v) - { - if (typeof k !== "string") - throw new Error("non-string key passed"); - if (typeof v !== "object") - throw new Error("non-object value passed"); - if (v && !("QueryInterface" in v)) - { - throw new Error("must pass an nsISupports; use wrappedJSObject to ease " + - "pain when using the server from JS"); - } - - this._objectState[k] = v; - }, - - /** -* Gets a content-type for the given file, first by checking for any custom -* MIME-types registered with this handler for the file's extension, second by -* asking the global MIME service for a content-type, and finally by failing -* over to application/octet-stream. -* -* @param file : nsIFile -* the nsIFile for which to get a file type -* @returns string -* the best content-type which can be determined for the file -*/ - _getTypeFromFile: function(file) - { - try - { - var name = file.leafName; - var dot = name.lastIndexOf("."); - if (dot > 0) - { - var ext = name.slice(dot + 1); - if (ext in this._mimeMappings) - return this._mimeMappings[ext]; - } - return Cc["@mozilla.org/uriloader/external-helper-app-service;1"] - .getService(Ci.nsIMIMEService) - .getTypeFromFile(file); - } - catch (e) - { - return "application/octet-stream"; - } - }, - - /** -* Returns the nsILocalFile which corresponds to the path, as determined using -* all registered path->directory mappings and any paths which are explicitly -* overridden. -* -* @param path : string -* the server path for which a file should be retrieved, e.g. "/foo/bar" -* @throws HttpError -* when the correct action is the corresponding HTTP error (i.e., because no -* mapping was found for a directory in path, the referenced file doesn't -* exist, etc.) -* @returns nsILocalFile -* the file to be sent as the response to a request for the path -*/ - _getFileForPath: function(path) - { - // decode and add underscores as necessary - try - { - path = toInternalPath(path, true); - } - catch (e) - { - throw HTTP_400; // malformed path - } - - // next, get the directory which contains this path - var pathMap = this._pathDirectoryMap; - - // An example progression of tmp for a path "/foo/bar/baz/" might be: - // "foo/bar/baz/", "foo/bar/baz", "foo/bar", "foo", "" - var tmp = path.substring(1); - while (true) - { - // do we have a match for current head of the path? - var file = pathMap.get(tmp); - if (file) - { - // XXX hack; basically disable showing mapping for /foo/bar/ when the - // requested path was /foo/bar, because relative links on the page - // will all be incorrect -- we really need the ability to easily - // redirect here instead - if (tmp == path.substring(1) && - tmp.length != 0 && - tmp.charAt(tmp.length - 1) != "/") - file = null; - else - break; - } - - // if we've finished trying all prefixes, exit - if (tmp == "") - break; - - tmp = tmp.substring(0, tmp.lastIndexOf("/")); - } - - // no mapping applies, so 404 - if (!file) - throw HTTP_404; - - - // last, get the file for the path within the determined directory - var parentFolder = file.parent; - var dirIsRoot = (parentFolder == null); - - // Strategy here is to append components individually, making sure we - // never move above the given directory; this allows paths such as - // "<file>/foo/../bar" but prevents paths such as "<file>/../base-sibling"; - // this component-wise approach also means the code works even on platforms - // which don't use "/" as the directory separator, such as Windows - var leafPath = path.substring(tmp.length + 1); - var comps = leafPath.split("/"); - for (var i = 0, sz = comps.length; i < sz; i++) - { - var comp = comps[i]; - - if (comp == "..") - file = file.parent; - else if (comp == "." || comp == "") - continue; - else - file.append(comp); - - if (!dirIsRoot && file.equals(parentFolder)) - throw HTTP_403; - } - - return file; - }, - - /** -* Writes the error page for the given HTTP error code over the given -* connection. -* -* @param errorCode : uint -* the HTTP error code to be used -* @param connection : Connection -* the connection on which the error occurred -*/ - handleError: function(errorCode, connection) - { - var response = new Response(connection); - - dumpn("*** error in request: " + errorCode); - - this._handleError(errorCode, new Request(connection.port), response); - }, - - /** -* Handles a request which generates the given error code, using the -* user-defined error handler if one has been set, gracefully falling back to -* the x00 status code if the code has no handler, and failing to status code -* 500 if all else fails. -* -* @param errorCode : uint -* the HTTP error which is to be returned -* @param metadata : Request -* metadata for the request, which will often be incomplete since this is an -* error -* @param response : Response -* an uninitialized Response should be initialized when this method -* completes with information which represents the desired error code in the -* ideal case or a fallback code in abnormal circumstances (i.e., 500 is a -* fallback for 505, per HTTP specs) -*/ - _handleError: function(errorCode, metadata, response) - { - if (!metadata) - throw Cr.NS_ERROR_NULL_POINTER; - - var errorX00 = errorCode - (errorCode % 100); - - try - { - if (!(errorCode in HTTP_ERROR_CODES)) - dumpn("*** WARNING: requested invalid error: " + errorCode); - - // RFC 2616 says that we should try to handle an error by its class if we - // can't otherwise handle it -- if that fails, we revert to handling it as - // a 500 internal server error, and if that fails we throw and shut down - // the server - - // actually handle the error - try - { - if (errorCode in this._overrideErrors) - this._overrideErrors[errorCode](metadata, response); - else - this._defaultErrors[errorCode](metadata, response); - } - catch (e) - { - if (response.partiallySent()) - { - response.abort(e); - return; - } - - // don't retry the handler that threw - if (errorX00 == errorCode) - throw HTTP_500; - - dumpn("*** error in handling for error code " + errorCode + ", " + - "falling back to " + errorX00 + "..."); - response = new Response(response._connection); - if (errorX00 in this._overrideErrors) - this._overrideErrors[errorX00](metadata, response); - else if (errorX00 in this._defaultErrors) - this._defaultErrors[errorX00](metadata, response); - else - throw HTTP_500; - } - } - catch (e) - { - if (response.partiallySent()) - { - response.abort(); - return; - } - - // we've tried everything possible for a meaningful error -- now try 500 - dumpn("*** error in handling for error code " + errorX00 + ", falling " + - "back to 500..."); - - try - { - response = new Response(response._connection); - if (500 in this._overrideErrors) - this._overrideErrors[500](metadata, response); - else - this._defaultErrors[500](metadata, response); - } - catch (e2) - { - dumpn("*** multiple errors in default error handlers!"); - dumpn("*** e == " + e + ", e2 == " + e2); - response.abort(e2); - return; - } - } - - response.complete(); - }, - - // FIELDS - - /** -* This object contains the default handlers for the various HTTP error codes. -*/ - _defaultErrors: - { - 400: function(metadata, response) - { - // none of the data in metadata is reliable, so hard-code everything here - response.setStatusLine("1.1", 400, "Bad Request"); - response.setHeader("Content-Type", "text/plain", false); - - var body = "Bad request\n"; - response.bodyOutputStream.write(body, body.length); - }, - 403: function(metadata, response) - { - response.setStatusLine(metadata.httpVersion, 403, "Forbidden"); - response.setHeader("Content-Type", "text/html", false); - - var body = "<html>\ -<head><title>403 Forbidden</title></head>\ -<body>\ -<h1>403 Forbidden</h1>\ -</body>\ -</html>"; - response.bodyOutputStream.write(body, body.length); - }, - 404: function(metadata, response) - { - response.setStatusLine(metadata.httpVersion, 404, "Not Found"); - response.setHeader("Content-Type", "text/html", false); - - var body = "<html>\ -<head><title>404 Not Found</title></head>\ -<body>\ -<h1>404 Not Found</h1>\ -<p>\ -<span style='font-family: monospace;'>" + - htmlEscape(metadata.path) + - "</span> was not found.\ -</p>\ -</body>\ -</html>"; - response.bodyOutputStream.write(body, body.length); - }, - 416: function(metadata, response) - { - response.setStatusLine(metadata.httpVersion, - 416, - "Requested Range Not Satisfiable"); - response.setHeader("Content-Type", "text/html", false); - - var body = "<html>\ -<head>\ -<title>416 Requested Range Not Satisfiable</title></head>\ -<body>\ -<h1>416 Requested Range Not Satisfiable</h1>\ -<p>The byte range was not valid for the\ -requested resource.\ -</p>\ -</body>\ -</html>"; - response.bodyOutputStream.write(body, body.length); - }, - 500: function(metadata, response) - { - response.setStatusLine(metadata.httpVersion, - 500, - "Internal Server Error"); - response.setHeader("Content-Type", "text/html", false); - - var body = "<html>\ -<head><title>500 Internal Server Error</title></head>\ -<body>\ -<h1>500 Internal Server Error</h1>\ -<p>Something's broken in this server and\ -needs to be fixed.</p>\ -</body>\ -</html>"; - response.bodyOutputStream.write(body, body.length); - }, - 501: function(metadata, response) - { - response.setStatusLine(metadata.httpVersion, 501, "Not Implemented"); - response.setHeader("Content-Type", "text/html", false); - - var body = "<html>\ -<head><title>501 Not Implemented</title></head>\ -<body>\ -<h1>501 Not Implemented</h1>\ -<p>This server is not (yet) Apache.</p>\ -</body>\ -</html>"; - response.bodyOutputStream.write(body, body.length); - }, - 505: function(metadata, response) - { - response.setStatusLine("1.1", 505, "HTTP Version Not Supported"); - response.setHeader("Content-Type", "text/html", false); - - var body = "<html>\ -<head><title>505 HTTP Version Not Supported</title></head>\ -<body>\ -<h1>505 HTTP Version Not Supported</h1>\ -<p>This server only supports HTTP/1.0 and HTTP/1.1\ -connections.</p>\ -</body>\ -</html>"; - response.bodyOutputStream.write(body, body.length); - } - }, - - /** -* Contains handlers for the default set of URIs contained in this server. -*/ - _defaultPaths: - { - "/": function(metadata, response) - { - response.setStatusLine(metadata.httpVersion, 200, "OK"); - response.setHeader("Content-Type", "text/html", false); - - var body = "<html>\ -<head><title>httpd.js</title></head>\ -<body>\ -<h1>httpd.js</h1>\ -<p>If you're seeing this page, httpd.js is up and\ -serving requests! Now set a base path and serve some\ -files!</p>\ -</body>\ -</html>"; - - response.bodyOutputStream.write(body, body.length); - }, - - "/trace": function(metadata, response) - { - response.setStatusLine(metadata.httpVersion, 200, "OK"); - response.setHeader("Content-Type", "text/plain", false); - - var body = "Request-URI: " + - metadata.scheme + "://" + metadata.host + ":" + metadata.port + - metadata.path + "\n\n"; - body += "Request (semantically equivalent, slightly reformatted):\n\n"; - body += metadata.method + " " + metadata.path; - - if (metadata.queryString) - body += "?" + metadata.queryString; - - body += " HTTP/" + metadata.httpVersion + "\r\n"; - - var headEnum = metadata.headers; - while (headEnum.hasMoreElements()) - { - var fieldName = headEnum.getNext() - .QueryInterface(Ci.nsISupportsString) - .data; - body += fieldName + ": " + metadata.getHeader(fieldName) + "\r\n"; - } - - response.bodyOutputStream.write(body, body.length); - } - } -}; - - -/** -* Maps absolute paths to files on the local file system (as nsILocalFiles). -*/ -function FileMap() -{ - /** Hash which will map paths to nsILocalFiles. */ - this._map = {}; -} -FileMap.prototype = -{ - // PUBLIC API - - /** -* Maps key to a clone of the nsILocalFile value if value is non-null; -* otherwise, removes any extant mapping for key. -* -* @param key : string -* string to which a clone of value is mapped -* @param value : nsILocalFile -* the file to map to key, or null to remove a mapping -*/ - put: function(key, value) - { - if (value) - this._map[key] = value.clone(); - else - delete this._map[key]; - }, - - /** -* Returns a clone of the nsILocalFile mapped to key, or null if no such -* mapping exists. -* -* @param key : string -* key to which the returned file maps -* @returns nsILocalFile -* a clone of the mapped file, or null if no mapping exists -*/ - get: function(key) - { - var val = this._map[key]; - return val ? val.clone() : null; - } -}; - - -// Response CONSTANTS - -// token = *<any CHAR except CTLs or separators> -// CHAR = <any US-ASCII character (0-127)> -// CTL = <any US-ASCII control character (0-31) and DEL (127)> -// separators = "(" | ")" | "<" | ">" | "@" -// | "," | ";" | ":" | "\" | <"> -// | "/" | "[" | "]" | "?" | "=" -// | "{" | "}" | SP | HT -const IS_TOKEN_ARRAY = - [0, 0, 0, 0, 0, 0, 0, 0, // 0 - 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 0, 0, 0, 0, 0, 0, 0, 0, // 24 - - 0, 1, 0, 1, 1, 1, 1, 1, // 32 - 0, 0, 1, 1, 0, 1, 1, 0, // 40 - 1, 1, 1, 1, 1, 1, 1, 1, // 48 - 1, 1, 0, 0, 0, 0, 0, 0, // 56 - - 0, 1, 1, 1, 1, 1, 1, 1, // 64 - 1, 1, 1, 1, 1, 1, 1, 1, // 72 - 1, 1, 1, 1, 1, 1, 1, 1, // 80 - 1, 1, 1, 0, 0, 0, 1, 1, // 88 - - 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 1, 1, 1, 1, 1, 1, 1, 1, // 104 - 1, 1, 1, 1, 1, 1, 1, 1, // 112 - 1, 1, 1, 0, 1, 0, 1]; // 120 - - -/** -* Determines whether the given character code is a CTL. -* -* @param code : uint -* the character code -* @returns boolean -* true if code is a CTL, false otherwise -*/ -function isCTL(code) -{ - return (code >= 0 && code <= 31) || (code == 127); -} - -/** -* Represents a response to an HTTP request, encapsulating all details of that -* response. This includes all headers, the HTTP version, status code and -* explanation, and the entity itself. -* -* @param connection : Connection -* the connection over which this response is to be written -*/ -function Response(connection) -{ - /** The connection over which this response will be written. */ - this._connection = connection; - - /** -* The HTTP version of this response; defaults to 1.1 if not set by the -* handler. -*/ - this._httpVersion = nsHttpVersion.HTTP_1_1; - - /** -* The HTTP code of this response; defaults to 200. -*/ - this._httpCode = 200; - - /** -* The description of the HTTP code in this response; defaults to "OK". -*/ - this._httpDescription = "OK"; - - /** -* An nsIHttpHeaders object in which the headers in this response should be -* stored. This property is null after the status line and headers have been -* written to the network, and it may be modified up until it is cleared, -* except if this._finished is set first (in which case headers are written -* asynchronously in response to a finish() call not preceded by -* flushHeaders()). -*/ - this._headers = new nsHttpHeaders(); - - /** -* Set to true when this response is ended (completely constructed if possible -* and the connection closed); further actions on this will then fail. -*/ - this._ended = false; - - /** -* A stream used to hold data written to the body of this response. -*/ - this._bodyOutputStream = null; - - /** -* A stream containing all data that has been written to the body of this -* response so far. (Async handlers make the data contained in this -* unreliable as a way of determining content length in general, but auxiliary -* saved information can sometimes be used to guarantee reliability.) -*/ - this._bodyInputStream = null; - - /** -* A stream copier which copies data to the network. It is initially null -* until replaced with a copier for response headers; when headers have been -* fully sent it is replaced with a copier for the response body, remaining -* so for the duration of response processing. -*/ - this._asyncCopier = null; - - /** -* True if this response has been designated as being processed -* asynchronously rather than for the duration of a single call to -* nsIHttpRequestHandler.handle. -*/ - this._processAsync = false; - - /** -* True iff finish() has been called on this, signaling that no more changes -* to this may be made. -*/ - this._finished = false; - - /** -* True iff powerSeized() has been called on this, signaling that this -* response is to be handled manually by the response handler (which may then -* send arbitrary data in response, even non-HTTP responses). -*/ - this._powerSeized = false; -} -Response.prototype = -{ - // PUBLIC CONSTRUCTION API - - // - // see nsIHttpResponse.bodyOutputStream - // - get bodyOutputStream() - { - if (this._finished) - throw Cr.NS_ERROR_NOT_AVAILABLE; - - if (!this._bodyOutputStream) - { - var pipe = new Pipe(true, false, Response.SEGMENT_SIZE, PR_UINT32_MAX, - null); - this._bodyOutputStream = pipe.outputStream; - this._bodyInputStream = pipe.inputStream; - if (this._processAsync || this._powerSeized) - this._startAsyncProcessor(); - } - - return this._bodyOutputStream; - }, - - // - // see nsIHttpResponse.write - // - write: function(data) - { - if (this._finished) - throw Cr.NS_ERROR_NOT_AVAILABLE; - - var dataAsString = String(data); - this.bodyOutputStream.write(dataAsString, dataAsString.length); - }, - - // - // see nsIHttpResponse.setStatusLine - // - setStatusLine: function(httpVersion, code, description) - { - if (!this._headers || this._finished || this._powerSeized) - throw Cr.NS_ERROR_NOT_AVAILABLE; - this._ensureAlive(); - - if (!(code >= 0 && code < 1000)) - throw Cr.NS_ERROR_INVALID_ARG; - - try - { - var httpVer; - // avoid version construction for the most common cases - if (!httpVersion || httpVersion == "1.1") - httpVer = nsHttpVersion.HTTP_1_1; - else if (httpVersion == "1.0") - httpVer = nsHttpVersion.HTTP_1_0; - else - httpVer = new nsHttpVersion(httpVersion); - } - catch (e) - { - throw Cr.NS_ERROR_INVALID_ARG; - } - - // Reason-Phrase = *<TEXT, excluding CR, LF> - // TEXT = <any OCTET except CTLs, but including LWS> - // - // XXX this ends up disallowing octets which aren't Unicode, I think -- not - // much to do if description is IDL'd as string - if (!description) - description = ""; - for (var i = 0; i < description.length; i++) - if (isCTL(description.charCodeAt(i)) && description.charAt(i) != "\t") - throw Cr.NS_ERROR_INVALID_ARG; - - // set the values only after validation to preserve atomicity - this._httpDescription = description; - this._httpCode = code; - this._httpVersion = httpVer; - }, - - // - // see nsIHttpResponse.setHeader - // - setHeader: function(name, value, merge) - { - if (!this._headers || this._finished || this._powerSeized) - throw Cr.NS_ERROR_NOT_AVAILABLE; - this._ensureAlive(); - - this._headers.setHeader(name, value, merge); - }, - - // - // see nsIHttpResponse.processAsync - // - processAsync: function() - { - if (this._finished) - throw Cr.NS_ERROR_UNEXPECTED; - if (this._powerSeized) - throw Cr.NS_ERROR_NOT_AVAILABLE; - if (this._processAsync) - return; - this._ensureAlive(); - - dumpn("*** processing connection " + this._connection.number + " async"); - this._processAsync = true; - - /* -* Either the bodyOutputStream getter or this method is responsible for -* starting the asynchronous processor and catching writes of data to the -* response body of async responses as they happen, for the purpose of -* forwarding those writes to the actual connection's output stream. -* If bodyOutputStream is accessed first, calling this method will create -* the processor (when it first is clear that body data is to be written -* immediately, not buffered). If this method is called first, accessing -* bodyOutputStream will create the processor. If only this method is -* called, we'll write nothing, neither headers nor the nonexistent body, -* until finish() is called. Since that delay is easily avoided by simply -* getting bodyOutputStream or calling write(""), we don't worry about it. -*/ - if (this._bodyOutputStream && !this._asyncCopier) - this._startAsyncProcessor(); - }, - - // - // see nsIHttpResponse.seizePower - // - seizePower: function() - { - if (this._processAsync) - throw Cr.NS_ERROR_NOT_AVAILABLE; - if (this._finished) - throw Cr.NS_ERROR_UNEXPECTED; - if (this._powerSeized) - return; - this._ensureAlive(); - - dumpn("*** forcefully seizing power over connection " + - this._connection.number + "..."); - - // Purge any already-written data without sending it. We could as easily - // swap out the streams entirely, but that makes it possible to acquire and - // unknowingly use a stale reference, so we require there only be one of - // each stream ever for any response to avoid this complication. - if (this._asyncCopier) - this._asyncCopier.cancel(Cr.NS_BINDING_ABORTED); - this._asyncCopier = null; - if (this._bodyOutputStream) - { - var input = new BinaryInputStream(this._bodyInputStream); - var avail; - while ((avail = input.available()) > 0) - input.readByteArray(avail); - } - - this._powerSeized = true; - if (this._bodyOutputStream) - this._startAsyncProcessor(); - }, - - // - // see nsIHttpResponse.finish - // - finish: function() - { - if (!this._processAsync && !this._powerSeized) - throw Cr.NS_ERROR_UNEXPECTED; - if (this._finished) - return; - - dumpn("*** finishing connection " + this._connection.number); - this._startAsyncProcessor(); // in case bodyOutputStream was never accessed - if (this._bodyOutputStream) - this._bodyOutputStream.close(); - this._finished = true; - }, - - - // NSISUPPORTS - - // - // see nsISupports.QueryInterface - // - QueryInterface: function(iid) - { - if (iid.equals(Ci.nsIHttpResponse) || iid.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - - // POST-CONSTRUCTION API (not exposed externally) - - /** -* The HTTP version number of this, as a string (e.g. "1.1"). -*/ - get httpVersion() - { - this._ensureAlive(); - return this._httpVersion.toString(); - }, - - /** -* The HTTP status code of this response, as a string of three characters per -* RFC 2616. -*/ - get httpCode() - { - this._ensureAlive(); - - var codeString = (this._httpCode < 10 ? "0" : "") + - (this._httpCode < 100 ? "0" : "") + - this._httpCode; - return codeString; - }, - - /** -* The description of the HTTP status code of this response, or "" if none is -* set. -*/ - get httpDescription() - { - this._ensureAlive(); - - return this._httpDescription; - }, - - /** -* The headers in this response, as an nsHttpHeaders object. -*/ - get headers() - { - this._ensureAlive(); - - return this._headers; - }, - - // - // see nsHttpHeaders.getHeader - // - getHeader: function(name) - { - this._ensureAlive(); - - return this._headers.getHeader(name); - }, - - /** -* Determines whether this response may be abandoned in favor of a newly -* constructed response. A response may be abandoned only if it is not being -* sent asynchronously and if raw control over it has not been taken from the -* server. -* -* @returns boolean -* true iff no data has been written to the network -*/ - partiallySent: function() - { - dumpn("*** partiallySent()"); - return this._processAsync || this._powerSeized; - }, - - /** -* If necessary, kicks off the remaining request processing needed to be done -* after a request handler performs its initial work upon this response. -*/ - complete: function() - { - dumpn("*** complete()"); - if (this._processAsync || this._powerSeized) - { - NS_ASSERT(this._processAsync ^ this._powerSeized, - "can't both send async and relinquish power"); - return; - } - - NS_ASSERT(!this.partiallySent(), "completing a partially-sent response?"); - - this._startAsyncProcessor(); - - // Now make sure we finish processing this request! - if (this._bodyOutputStream) - this._bodyOutputStream.close(); - }, - - /** -* Abruptly ends processing of this response, usually due to an error in an -* incoming request but potentially due to a bad error handler. Since we -* cannot handle the error in the usual way (giving an HTTP error page in -* response) because data may already have been sent (or because the response -* might be expected to have been generated asynchronously or completely from -* scratch by the handler), we stop processing this response and abruptly -* close the connection. -* -* @param e : Error -* the exception which precipitated this abort, or null if no such exception -* was generated -*/ - abort: function(e) - { - dumpn("*** abort(<" + e + ">)"); - - // This response will be ended by the processor if one was created. - var copier = this._asyncCopier; - if (copier) - { - // We dispatch asynchronously here so that any pending writes of data to - // the connection will be deterministically written. This makes it easier - // to specify exact behavior, and it makes observable behavior more - // predictable for clients. Note that the correctness of this depends on - // callbacks in response to _waitToReadData in WriteThroughCopier - // happening asynchronously with respect to the actual writing of data to - // bodyOutputStream, as they currently do; if they happened synchronously, - // an event which ran before this one could write more data to the - // response body before we get around to canceling the copier. We have - // tests for this in test_seizepower.js, however, and I can't think of a - // way to handle both cases without removing bodyOutputStream access and - // moving its effective write(data, length) method onto Response, which - // would be slower and require more code than this anyway. - gThreadManager.currentThread.dispatch({ - run: function() - { - dumpn("*** canceling copy asynchronously..."); - copier.cancel(Cr.NS_ERROR_UNEXPECTED); - } - }, Ci.nsIThread.DISPATCH_NORMAL); - } - else - { - this.end(); - } - }, - - /** -* Closes this response's network connection, marks the response as finished, -* and notifies the server handler that the request is done being processed. -*/ - end: function() - { - NS_ASSERT(!this._ended, "ending this response twice?!?!"); - - this._connection.close(); - if (this._bodyOutputStream) - this._bodyOutputStream.close(); - - this._finished = true; - this._ended = true; - }, - - // PRIVATE IMPLEMENTATION - - /** -* Sends the status line and headers of this response if they haven't been -* sent and initiates the process of copying data written to this response's -* body to the network. -*/ - _startAsyncProcessor: function() - { - dumpn("*** _startAsyncProcessor()"); - - // Handle cases where we're being called a second time. The former case - // happens when this is triggered both by complete() and by processAsync(), - // while the latter happens when processAsync() in conjunction with sent - // data causes abort() to be called. - if (this._asyncCopier || this._ended) - { - dumpn("*** ignoring second call to _startAsyncProcessor"); - return; - } - - // Send headers if they haven't been sent already and should be sent, then - // asynchronously continue to send the body. - if (this._headers && !this._powerSeized) - { - this._sendHeaders(); - return; - } - - this._headers = null; - this._sendBody(); - }, - - /** -* Signals that all modifications to the response status line and headers are -* complete and then sends that data over the network to the client. Once -* this method completes, a different response to the request that resulted -* in this response cannot be sent -- the only possible action in case of -* error is to abort the response and close the connection. -*/ - _sendHeaders: function() - { - dumpn("*** _sendHeaders()"); - - NS_ASSERT(this._headers); - NS_ASSERT(!this._powerSeized); - - // request-line - var statusLine = "HTTP/" + this.httpVersion + " " + - this.httpCode + " " + - this.httpDescription + "\r\n"; - - // header post-processing - - var headers = this._headers; - headers.setHeader("Connection", "close", false); - headers.setHeader("Server", "httpd.js", false); - if (!headers.hasHeader("Date")) - headers.setHeader("Date", toDateString(Date.now()), false); - - // Any response not being processed asynchronously must have an associated - // Content-Length header for reasons of backwards compatibility with the - // initial server, which fully buffered every response before sending it. - // Beyond that, however, it's good to do this anyway because otherwise it's - // impossible to test behaviors that depend on the presence or absence of a - // Content-Length header. - if (!this._processAsync) - { - dumpn("*** non-async response, set Content-Length"); - - var bodyStream = this._bodyInputStream; - var avail = bodyStream ? bodyStream.available() : 0; - - // XXX assumes stream will always report the full amount of data available - headers.setHeader("Content-Length", "" + avail, false); - } - - - // construct and send response - dumpn("*** header post-processing completed, sending response head..."); - - // request-line - var preambleData = [statusLine]; - - // headers - var headEnum = headers.enumerator; - while (headEnum.hasMoreElements()) - { - var fieldName = headEnum.getNext() - .QueryInterface(Ci.nsISupportsString) - .data; - var values = headers.getHeaderValues(fieldName); - for (var i = 0, sz = values.length; i < sz; i++) - preambleData.push(fieldName + ": " + values[i] + "\r\n"); - } - - // end request-line/headers - preambleData.push("\r\n"); - - var preamble = preambleData.join(""); - - var responseHeadPipe = new Pipe(true, false, 0, PR_UINT32_MAX, null); - responseHeadPipe.outputStream.write(preamble, preamble.length); - - var response = this; - var copyObserver = - { - onStartRequest: function(request, cx) - { - dumpn("*** preamble copying started"); - }, - - onStopRequest: function(request, cx, statusCode) - { - dumpn("*** preamble copying complete " + - "[status=0x" + statusCode.toString(16) + "]"); - - if (!components.isSuccessCode(statusCode)) - { - dumpn("!!! header copying problems: non-success statusCode, " + - "ending response"); - - response.end(); - } - else - { - response._sendBody(); - } - }, - - QueryInterface: function(aIID) - { - if (aIID.equals(Ci.nsIRequestObserver) || aIID.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - } - }; - - var headerCopier = this._asyncCopier = - new WriteThroughCopier(responseHeadPipe.inputStream, - this._connection.output, - copyObserver, null); - - responseHeadPipe.outputStream.close(); - - // Forbid setting any more headers or modifying the request line. - this._headers = null; - }, - - /** -* Asynchronously writes the body of the response (or the entire response, if -* seizePower() has been called) to the network. -*/ - _sendBody: function() - { - dumpn("*** _sendBody"); - - NS_ASSERT(!this._headers, "still have headers around but sending body?"); - - // If no body data was written, we're done - if (!this._bodyInputStream) - { - dumpn("*** empty body, response finished"); - this.end(); - return; - } - - var response = this; - var copyObserver = - { - onStartRequest: function(request, context) - { - dumpn("*** onStartRequest"); - }, - - onStopRequest: function(request, cx, statusCode) - { - dumpn("*** onStopRequest [status=0x" + statusCode.toString(16) + "]"); - - if (statusCode === Cr.NS_BINDING_ABORTED) - { - dumpn("*** terminating copy observer without ending the response"); - } - else - { - if (!components.isSuccessCode(statusCode)) - dumpn("*** WARNING: non-success statusCode in onStopRequest"); - - response.end(); - } - }, - - QueryInterface: function(aIID) - { - if (aIID.equals(Ci.nsIRequestObserver) || aIID.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - } - }; - - dumpn("*** starting async copier of body data..."); - this._asyncCopier = - new WriteThroughCopier(this._bodyInputStream, this._connection.output, - copyObserver, null); - }, - - /** Ensures that this hasn't been ended. */ - _ensureAlive: function() - { - NS_ASSERT(!this._ended, "not handling response lifetime correctly"); - } -}; - -/** -* Size of the segments in the buffer used in storing response data and writing -* it to the socket. -*/ -Response.SEGMENT_SIZE = 8192; - -/** Serves double duty in WriteThroughCopier implementation. */ -function notImplemented() -{ - throw Cr.NS_ERROR_NOT_IMPLEMENTED; -} - -/** Returns true iff the given exception represents stream closure. */ -function streamClosed(e) -{ - return e === Cr.NS_BASE_STREAM_CLOSED || - (typeof e === "object" && e.result === Cr.NS_BASE_STREAM_CLOSED); -} - -/** Returns true iff the given exception represents a blocked stream. */ -function wouldBlock(e) -{ - return e === Cr.NS_BASE_STREAM_WOULD_BLOCK || - (typeof e === "object" && e.result === Cr.NS_BASE_STREAM_WOULD_BLOCK); -} - -/** -* Copies data from source to sink as it becomes available, when that data can -* be written to sink without blocking. -* -* @param source : nsIAsyncInputStream -* the stream from which data is to be read -* @param sink : nsIAsyncOutputStream -* the stream to which data is to be copied -* @param observer : nsIRequestObserver -* an observer which will be notified when the copy starts and finishes -* @param context : nsISupports -* context passed to observer when notified of start/stop -* @throws NS_ERROR_NULL_POINTER -* if source, sink, or observer are null -*/ -function WriteThroughCopier(source, sink, observer, context) -{ - if (!source || !sink || !observer) - throw Cr.NS_ERROR_NULL_POINTER; - - /** Stream from which data is being read. */ - this._source = source; - - /** Stream to which data is being written. */ - this._sink = sink; - - /** Observer watching this copy. */ - this._observer = observer; - - /** Context for the observer watching this. */ - this._context = context; - - /** -* True iff this is currently being canceled (cancel has been called, the -* callback may not yet have been made). -*/ - this._canceled = false; - - /** -* False until all data has been read from input and written to output, at -* which point this copy is completed and cancel() is asynchronously called. -*/ - this._completed = false; - - /** Required by nsIRequest, meaningless. */ - this.loadFlags = 0; - /** Required by nsIRequest, meaningless. */ - this.loadGroup = null; - /** Required by nsIRequest, meaningless. */ - this.name = "response-body-copy"; - - /** Status of this request. */ - this.status = Cr.NS_OK; - - /** Arrays of byte strings waiting to be written to output. */ - this._pendingData = []; - - // start copying - try - { - observer.onStartRequest(this, context); - this._waitToReadData(); - this._waitForSinkClosure(); - } - catch (e) - { - dumpn("!!! error starting copy: " + e + - ("lineNumber" in e ? ", line " + e.lineNumber : "")); - dumpn(e.stack); - this.cancel(Cr.NS_ERROR_UNEXPECTED); - } -} -WriteThroughCopier.prototype = -{ - /* nsISupports implementation */ - - QueryInterface: function(iid) - { - if (iid.equals(Ci.nsIInputStreamCallback) || - iid.equals(Ci.nsIOutputStreamCallback) || - iid.equals(Ci.nsIRequest) || - iid.equals(Ci.nsISupports)) - { - return this; - } - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - - // NSIINPUTSTREAMCALLBACK - - /** -* Receives a more-data-in-input notification and writes the corresponding -* data to the output. -* -* @param input : nsIAsyncInputStream -* the input stream on whose data we have been waiting -*/ - onInputStreamReady: function(input) - { - if (this._source === null) - return; - - dumpn("*** onInputStreamReady"); - - // - // Ordinarily we'll read a non-zero amount of data from input, queue it up - // to be written and then wait for further callbacks. The complications in - // this method are the cases where we deviate from that behavior when errors - // occur or when copying is drawing to a finish. - // - // The edge cases when reading data are: - // - // Zero data is read - // If zero data was read, we're at the end of available data, so we can - // should stop reading and move on to writing out what we have (or, if - // we've already done that, onto notifying of completion). - // A stream-closed exception is thrown - // This is effectively a less kind version of zero data being read; the - // only difference is that we notify of completion with that result - // rather than with NS_OK. - // Some other exception is thrown - // This is the least kind result. We don't know what happened, so we - // act as though the stream closed except that we notify of completion - // with the result NS_ERROR_UNEXPECTED. - // - - var bytesWanted = 0, bytesConsumed = -1; - try - { - input = new BinaryInputStream(input); - - bytesWanted = Math.min(input.available(), Response.SEGMENT_SIZE); - dumpn("*** input wanted: " + bytesWanted); - - if (bytesWanted > 0) - { - var data = input.readByteArray(bytesWanted); - bytesConsumed = data.length; - this._pendingData.push(String.fromCharCode.apply(String, data)); - } - - dumpn("*** " + bytesConsumed + " bytes read"); - - // Handle the zero-data edge case in the same place as all other edge - // cases are handled. - if (bytesWanted === 0) - throw Cr.NS_BASE_STREAM_CLOSED; - } - catch (e) - { - if (streamClosed(e)) - { - dumpn("*** input stream closed"); - e = bytesWanted === 0 ? Cr.NS_OK : Cr.NS_ERROR_UNEXPECTED; - } - else - { - dumpn("!!! unexpected error reading from input, canceling: " + e); - e = Cr.NS_ERROR_UNEXPECTED; - } - - this._doneReadingSource(e); - return; - } - - var pendingData = this._pendingData; - - NS_ASSERT(bytesConsumed > 0); - NS_ASSERT(pendingData.length > 0, "no pending data somehow?"); - NS_ASSERT(pendingData[pendingData.length - 1].length > 0, - "buffered zero bytes of data?"); - - NS_ASSERT(this._source !== null); - - // Reading has gone great, and we've gotten data to write now. What if we - // don't have a place to write that data, because output went away just - // before this read? Drop everything on the floor, including new data, and - // cancel at this point. - if (this._sink === null) - { - pendingData.length = 0; - this._doneReadingSource(Cr.NS_ERROR_UNEXPECTED); - return; - } - - // Okay, we've read the data, and we know we have a place to write it. We - // need to queue up the data to be written, but *only* if none is queued - // already -- if data's already queued, the code that actually writes the - // data will make sure to wait on unconsumed pending data. - try - { - if (pendingData.length === 1) - this._waitToWriteData(); - } - catch (e) - { - dumpn("!!! error waiting to write data just read, swallowing and " + - "writing only what we already have: " + e); - this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED); - return; - } - - // Whee! We successfully read some data, and it's successfully queued up to - // be written. All that remains now is to wait for more data to read. - try - { - this._waitToReadData(); - } - catch (e) - { - dumpn("!!! error waiting to read more data: " + e); - this._doneReadingSource(Cr.NS_ERROR_UNEXPECTED); - } - }, - - - // NSIOUTPUTSTREAMCALLBACK - - /** -* Callback when data may be written to the output stream without blocking, or -* when the output stream has been closed. -* -* @param output : nsIAsyncOutputStream -* the output stream on whose writability we've been waiting, also known as -* this._sink -*/ - onOutputStreamReady: function(output) - { - if (this._sink === null) - return; - - dumpn("*** onOutputStreamReady"); - - var pendingData = this._pendingData; - if (pendingData.length === 0) - { - // There's no pending data to write. The only way this can happen is if - // we're waiting on the output stream's closure, so we can respond to a - // copying failure as quickly as possible (rather than waiting for data to - // be available to read and then fail to be copied). Therefore, we must - // be done now -- don't bother to attempt to write anything and wrap - // things up. - dumpn("!!! output stream closed prematurely, ending copy"); - - this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED); - return; - } - - - NS_ASSERT(pendingData[0].length > 0, "queued up an empty quantum?"); - - // - // Write out the first pending quantum of data. The possible errors here - // are: - // - // The write might fail because we can't write that much data - // Okay, we've written what we can now, so re-queue what's left and - // finish writing it out later. - // The write failed because the stream was closed - // Discard pending data that we can no longer write, stop reading, and - // signal that copying finished. - // Some other error occurred. - // Same as if the stream were closed, but notify with the status - // NS_ERROR_UNEXPECTED so the observer knows something was wonky. - // - - try - { - var quantum = pendingData[0]; - - // XXX |quantum| isn't guaranteed to be ASCII, so we're relying on - // undefined behavior! We're only using this because writeByteArray - // is unusably broken for asynchronous output streams; see bug 532834 - // for details. - var bytesWritten = output.write(quantum, quantum.length); - if (bytesWritten === quantum.length) - pendingData.shift(); - else - pendingData[0] = quantum.substring(bytesWritten); - - dumpn("*** wrote " + bytesWritten + " bytes of data"); - } - catch (e) - { - if (wouldBlock(e)) - { - NS_ASSERT(pendingData.length > 0, - "stream-blocking exception with no data to write?"); - NS_ASSERT(pendingData[0].length > 0, - "stream-blocking exception with empty quantum?"); - this._waitToWriteData(); - return; - } - - if (streamClosed(e)) - dumpn("!!! output stream prematurely closed, signaling error..."); - else - dumpn("!!! unknown error: " + e + ", quantum=" + quantum); - - this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED); - return; - } - - // The day is ours! Quantum written, now let's see if we have more data - // still to write. - try - { - if (pendingData.length > 0) - { - this._waitToWriteData(); - return; - } - } - catch (e) - { - dumpn("!!! unexpected error waiting to write pending data: " + e); - this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED); - return; - } - - // Okay, we have no more pending data to write -- but might we get more in - // the future? - if (this._source !== null) - { - /* -* If we might, then wait for the output stream to be closed. (We wait -* only for closure because we have no data to write -- and if we waited -* for a specific amount of data, we would get repeatedly notified for no -* reason if over time the output stream permitted more and more data to -* be written to it without blocking.) -*/ - this._waitForSinkClosure(); - } - else - { - /* -* On the other hand, if we can't have more data because the input -* stream's gone away, then it's time to notify of copy completion. -* Victory! -*/ - this._sink = null; - this._cancelOrDispatchCancelCallback(Cr.NS_OK); - } - }, - - - // NSIREQUEST - - /** Returns true if the cancel observer hasn't been notified yet. */ - isPending: function() - { - return !this._completed; - }, - - /** Not implemented, don't use! */ - suspend: notImplemented, - /** Not implemented, don't use! */ - resume: notImplemented, - - /** -* Cancels data reading from input, asynchronously writes out any pending -* data, and causes the observer to be notified with the given error code when -* all writing has finished. -* -* @param status : nsresult -* the status to pass to the observer when data copying has been canceled -*/ - cancel: function(status) - { - dumpn("*** cancel(" + status.toString(16) + ")"); - - if (this._canceled) - { - dumpn("*** suppressing a late cancel"); - return; - } - - this._canceled = true; - this.status = status; - - // We could be in the middle of absolutely anything at this point. Both - // input and output might still be around, we might have pending data to - // write, and in general we know nothing about the state of the world. We - // therefore must assume everything's in progress and take everything to its - // final steady state (or so far as it can go before we need to finish - // writing out remaining data). - - this._doneReadingSource(status); - }, - - - // PRIVATE IMPLEMENTATION - - /** -* Stop reading input if we haven't already done so, passing e as the status -* when closing the stream, and kick off a copy-completion notice if no more -* data remains to be written. -* -* @param e : nsresult -* the status to be used when closing the input stream -*/ - _doneReadingSource: function(e) - { - dumpn("*** _doneReadingSource(0x" + e.toString(16) + ")"); - - this._finishSource(e); - if (this._pendingData.length === 0) - this._sink = null; - else - NS_ASSERT(this._sink !== null, "null output?"); - - // If we've written out all data read up to this point, then it's time to - // signal completion. - if (this._sink === null) - { - NS_ASSERT(this._pendingData.length === 0, "pending data still?"); - this._cancelOrDispatchCancelCallback(e); - } - }, - - /** -* Stop writing output if we haven't already done so, discard any data that -* remained to be sent, close off input if it wasn't already closed, and kick -* off a copy-completion notice. -* -* @param e : nsresult -* the status to be used when closing input if it wasn't already closed -*/ - _doneWritingToSink: function(e) - { - dumpn("*** _doneWritingToSink(0x" + e.toString(16) + ")"); - - this._pendingData.length = 0; - this._sink = null; - this._doneReadingSource(e); - }, - - /** -* Completes processing of this copy: either by canceling the copy if it -* hasn't already been canceled using the provided status, or by dispatching -* the cancel callback event (with the originally provided status, of course) -* if it already has been canceled. -* -* @param status : nsresult -* the status code to use to cancel this, if this hasn't already been -* canceled -*/ - _cancelOrDispatchCancelCallback: function(status) - { - dumpn("*** _cancelOrDispatchCancelCallback(" + status + ")"); - - NS_ASSERT(this._source === null, "should have finished input"); - NS_ASSERT(this._sink === null, "should have finished output"); - NS_ASSERT(this._pendingData.length === 0, "should have no pending data"); - - if (!this._canceled) - { - this.cancel(status); - return; - } - - var self = this; - var event = - { - run: function() - { - dumpn("*** onStopRequest async callback"); - - self._completed = true; - try - { - self._observer.onStopRequest(self, self._context, self.status); - } - catch (e) - { - NS_ASSERT(false, - "how are we throwing an exception here? we control " + - "all the callers! " + e); - } - } - }; - - gThreadManager.currentThread.dispatch(event, Ci.nsIThread.DISPATCH_NORMAL); - }, - - /** -* Kicks off another wait for more data to be available from the input stream. -*/ - _waitToReadData: function() - { - dumpn("*** _waitToReadData"); - this._source.asyncWait(this, 0, Response.SEGMENT_SIZE, - gThreadManager.mainThread); - }, - - /** -* Kicks off another wait until data can be written to the output stream. -*/ - _waitToWriteData: function() - { - dumpn("*** _waitToWriteData"); - - var pendingData = this._pendingData; - NS_ASSERT(pendingData.length > 0, "no pending data to write?"); - NS_ASSERT(pendingData[0].length > 0, "buffered an empty write?"); - - this._sink.asyncWait(this, 0, pendingData[0].length, - gThreadManager.mainThread); - }, - - /** -* Kicks off a wait for the sink to which data is being copied to be closed. -* We wait for stream closure when we don't have any data to be copied, rather -* than waiting to write a specific amount of data. We can't wait to write -* data because the sink might be infinitely writable, and if no data appears -* in the source for a long time we might have to spin quite a bit waiting to -* write, waiting to write again, &c. Waiting on stream closure instead means -* we'll get just one notification if the sink dies. Note that when data -* starts arriving from the sink we'll resume waiting for data to be written, -* dropping this closure-only callback entirely. -*/ - _waitForSinkClosure: function() - { - dumpn("*** _waitForSinkClosure"); - - this._sink.asyncWait(this, Ci.nsIAsyncOutputStream.WAIT_CLOSURE_ONLY, 0, - gThreadManager.mainThread); - }, - - /** -* Closes input with the given status, if it hasn't already been closed; -* otherwise a no-op. -* -* @param status : nsresult -* status code use to close the source stream if necessary -*/ - _finishSource: function(status) - { - dumpn("*** _finishSource(" + status.toString(16) + ")"); - - if (this._source !== null) - { - this._source.closeWithStatus(status); - this._source = null; - } - } -}; - - -/** -* A container for utility functions used with HTTP headers. -*/ -const headerUtils = -{ - /** -* Normalizes fieldName (by converting it to lowercase) and ensures it is a -* valid header field name (although not necessarily one specified in RFC -* 2616). -* -* @throws NS_ERROR_INVALID_ARG -* if fieldName does not match the field-name production in RFC 2616 -* @returns string -* fieldName converted to lowercase if it is a valid header, for characters -* where case conversion is possible -*/ - normalizeFieldName: function(fieldName) - { - if (fieldName == "") - throw Cr.NS_ERROR_INVALID_ARG; - - for (var i = 0, sz = fieldName.length; i < sz; i++) - { - if (!IS_TOKEN_ARRAY[fieldName.charCodeAt(i)]) - { - dumpn(fieldName + " is not a valid header field name!"); - throw Cr.NS_ERROR_INVALID_ARG; - } - } - - return fieldName.toLowerCase(); - }, - - /** -* Ensures that fieldValue is a valid header field value (although not -* necessarily as specified in RFC 2616 if the corresponding field name is -* part of the HTTP protocol), normalizes the value if it is, and -* returns the normalized value. -* -* @param fieldValue : string -* a value to be normalized as an HTTP header field value -* @throws NS_ERROR_INVALID_ARG -* if fieldValue does not match the field-value production in RFC 2616 -* @returns string -* fieldValue as a normalized HTTP header field value -*/ - normalizeFieldValue: function(fieldValue) - { - // field-value = *( field-content | LWS ) - // field-content = <the OCTETs making up the field-value - // and consisting of either *TEXT or combinations - // of token, separators, and quoted-string> - // TEXT = <any OCTET except CTLs, - // but including LWS> - // LWS = [CRLF] 1*( SP | HT ) - // - // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) - // qdtext = <any TEXT except <">> - // quoted-pair = "\" CHAR - // CHAR = <any US-ASCII character (octets 0 - 127)> - - // Any LWS that occurs between field-content MAY be replaced with a single - // SP before interpreting the field value or forwarding the message - // downstream (section 4.2); we replace 1*LWS with a single SP - var val = fieldValue.replace(/(?:(?:\r\n)?[ \t]+)+/g, " "); - - // remove leading/trailing LWS (which has been converted to SP) - val = val.replace(/^ +/, "").replace(/ +$/, ""); - - // that should have taken care of all CTLs, so val should contain no CTLs - for (var i = 0, len = val.length; i < len; i++) - if (isCTL(val.charCodeAt(i))) - throw Cr.NS_ERROR_INVALID_ARG; - - // XXX disallows quoted-pair where CHAR is a CTL -- will not invalidly - // normalize, however, so this can be construed as a tightening of the - // spec and not entirely as a bug - return val; - } -}; - - - -/** -* Converts the given string into a string which is safe for use in an HTML -* context. -* -* @param str : string -* the string to make HTML-safe -* @returns string -* an HTML-safe version of str -*/ -function htmlEscape(str) -{ - // this is naive, but it'll work - var s = ""; - for (var i = 0; i < str.length; i++) - s += "&#" + str.charCodeAt(i) + ";"; - return s; -} - - -/** -* Constructs an object representing an HTTP version (see section 3.1). -* -* @param versionString -* a string of the form "#.#", where # is an non-negative decimal integer with -* or without leading zeros -* @throws -* if versionString does not specify a valid HTTP version number -*/ -function nsHttpVersion(versionString) -{ - var matches = /^(\d+)\.(\d+)$/.exec(versionString); - if (!matches) - throw "Not a valid HTTP version!"; - - /** The major version number of this, as a number. */ - this.major = parseInt(matches[1], 10); - - /** The minor version number of this, as a number. */ - this.minor = parseInt(matches[2], 10); - - if (isNaN(this.major) || isNaN(this.minor) || - this.major < 0 || this.minor < 0) - throw "Not a valid HTTP version!"; -} -nsHttpVersion.prototype = -{ - /** -* Returns the standard string representation of the HTTP version represented -* by this (e.g., "1.1"). -*/ - toString: function () - { - return this.major + "." + this.minor; - }, - - /** -* Returns true if this represents the same HTTP version as otherVersion, -* false otherwise. -* -* @param otherVersion : nsHttpVersion -* the version to compare against this -*/ - equals: function (otherVersion) - { - return this.major == otherVersion.major && - this.minor == otherVersion.minor; - }, - - /** True if this >= otherVersion, false otherwise. */ - atLeast: function(otherVersion) - { - return this.major > otherVersion.major || - (this.major == otherVersion.major && - this.minor >= otherVersion.minor); - } -}; - -nsHttpVersion.HTTP_1_0 = new nsHttpVersion("1.0"); -nsHttpVersion.HTTP_1_1 = new nsHttpVersion("1.1"); - - -/** -* An object which stores HTTP headers for a request or response. -* -* Note that since headers are case-insensitive, this object converts headers to -* lowercase before storing them. This allows the getHeader and hasHeader -* methods to work correctly for any case of a header, but it means that the -* values returned by .enumerator may not be equal case-sensitively to the -* values passed to setHeader when adding headers to this. -*/ -function nsHttpHeaders() -{ - /** -* A hash of headers, with header field names as the keys and header field -* values as the values. Header field names are case-insensitive, but upon -* insertion here they are converted to lowercase. Header field values are -* normalized upon insertion to contain no leading or trailing whitespace. -* -* Note also that per RFC 2616, section 4.2, two headers with the same name in -* a message may be treated as one header with the same field name and a field -* value consisting of the separate field values joined together with a "," in -* their original order. This hash stores multiple headers with the same name -* in this manner. -*/ - this._headers = {}; -} -nsHttpHeaders.prototype = -{ - /** -* Sets the header represented by name and value in this. -* -* @param name : string -* the header name -* @param value : string -* the header value -* @throws NS_ERROR_INVALID_ARG -* if name or value is not a valid header component -*/ - setHeader: function(fieldName, fieldValue, merge) - { - var name = headerUtils.normalizeFieldName(fieldName); - var value = headerUtils.normalizeFieldValue(fieldValue); - - // The following three headers are stored as arrays because their real-world - // syntax prevents joining individual headers into a single header using - // ",". See also <http://hg.mozilla.org/mozilla-central/diff/9b2a99adc05e/netwerk/protocol/http/src/nsHttpHeaderArray.cpp#l77> - if (merge && name in this._headers) - { - if (name === "www-authenticate" || - name === "proxy-authenticate" || - name === "set-cookie") - { - this._headers[name].push(value); - } - else - { - this._headers[name][0] += "," + value; - NS_ASSERT(this._headers[name].length === 1, - "how'd a non-special header have multiple values?") - } - } - else - { - this._headers[name] = [value]; - } - }, - - /** -* Returns the value for the header specified by this. -* -* @throws NS_ERROR_INVALID_ARG -* if fieldName does not constitute a valid header field name -* @throws NS_ERROR_NOT_AVAILABLE -* if the given header does not exist in this -* @returns string -* the field value for the given header, possibly with non-semantic changes -* (i.e., leading/trailing whitespace stripped, whitespace runs replaced -* with spaces, etc.) at the option of the implementation; multiple -* instances of the header will be combined with a comma, except for -* the three headers noted in the description of getHeaderValues -*/ - getHeader: function(fieldName) - { - return this.getHeaderValues(fieldName).join("\n"); - }, - - /** -* Returns the value for the header specified by fieldName as an array. -* -* @throws NS_ERROR_INVALID_ARG -* if fieldName does not constitute a valid header field name -* @throws NS_ERROR_NOT_AVAILABLE -* if the given header does not exist in this -* @returns [string] -* an array of all the header values in this for the given -* header name. Header values will generally be collapsed -* into a single header by joining all header values together -* with commas, but certain headers (Proxy-Authenticate, -* WWW-Authenticate, and Set-Cookie) violate the HTTP spec -* and cannot be collapsed in this manner. For these headers -* only, the returned array may contain multiple elements if -* that header has been added more than once. -*/ - getHeaderValues: function(fieldName) - { - var name = headerUtils.normalizeFieldName(fieldName); - - if (name in this._headers) - return this._headers[name]; - else - throw Cr.NS_ERROR_NOT_AVAILABLE; - }, - - /** -* Returns true if a header with the given field name exists in this, false -* otherwise. -* -* @param fieldName : string -* the field name whose existence is to be determined in this -* @throws NS_ERROR_INVALID_ARG -* if fieldName does not constitute a valid header field name -* @returns boolean -* true if the header's present, false otherwise -*/ - hasHeader: function(fieldName) - { - var name = headerUtils.normalizeFieldName(fieldName); - return (name in this._headers); - }, - - /** -* Returns a new enumerator over the field names of the headers in this, as -* nsISupportsStrings. The names returned will be in lowercase, regardless of -* how they were input using setHeader (header names are case-insensitive per -* RFC 2616). -*/ - get enumerator() - { - var headers = []; - for (var i in this._headers) - { - var supports = new SupportsString(); - supports.data = i; - headers.push(supports); - } - - return new nsSimpleEnumerator(headers); - } -}; - - -/** -* Constructs an nsISimpleEnumerator for the given array of items. -* -* @param items : Array -* the items, which must all implement nsISupports -*/ -function nsSimpleEnumerator(items) -{ - this._items = items; - this._nextIndex = 0; -} -nsSimpleEnumerator.prototype = -{ - hasMoreElements: function() - { - return this._nextIndex < this._items.length; - }, - getNext: function() - { - if (!this.hasMoreElements()) - throw Cr.NS_ERROR_NOT_AVAILABLE; - - return this._items[this._nextIndex++]; - }, - QueryInterface: function(aIID) - { - if (Ci.nsISimpleEnumerator.equals(aIID) || - Ci.nsISupports.equals(aIID)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - - -/** -* A representation of the data in an HTTP request. -* -* @param port : uint -* the port on which the server receiving this request runs -*/ -function Request(port) -{ - /** Method of this request, e.g. GET or POST. */ - this._method = ""; - - /** Path of the requested resource; empty paths are converted to '/'. */ - this._path = ""; - - /** Query string, if any, associated with this request (not including '?'). */ - this._queryString = ""; - - /** Scheme of requested resource, usually http, always lowercase. */ - this._scheme = "http"; - - /** Hostname on which the requested resource resides. */ - this._host = undefined; - - /** Port number over which the request was received. */ - this._port = port; - - var bodyPipe = new Pipe(false, false, 0, PR_UINT32_MAX, null); - - /** Stream from which data in this request's body may be read. */ - this._bodyInputStream = bodyPipe.inputStream; - - /** Stream to which data in this request's body is written. */ - this._bodyOutputStream = bodyPipe.outputStream; - - /** -* The headers in this request. -*/ - this._headers = new nsHttpHeaders(); - - /** -* For the addition of ad-hoc properties and new functionality without having -* to change nsIHttpRequest every time; currently lazily created, as its only -* use is in directory listings. -*/ - this._bag = null; -} -Request.prototype = -{ - // SERVER METADATA - - // - // see nsIHttpRequest.scheme - // - get scheme() - { - return this._scheme; - }, - - // - // see nsIHttpRequest.host - // - get host() - { - return this._host; - }, - - // - // see nsIHttpRequest.port - // - get port() - { - return this._port; - }, - - // REQUEST LINE - - // - // see nsIHttpRequest.method - // - get method() - { - return this._method; - }, - - // - // see nsIHttpRequest.httpVersion - // - get httpVersion() - { - return this._httpVersion.toString(); - }, - - // - // see nsIHttpRequest.path - // - get path() - { - return this._path; - }, - - // - // see nsIHttpRequest.queryString - // - get queryString() - { - return this._queryString; - }, - - // HEADERS - - // - // see nsIHttpRequest.getHeader - // - getHeader: function(name) - { - return this._headers.getHeader(name); - }, - - // - // see nsIHttpRequest.hasHeader - // - hasHeader: function(name) - { - return this._headers.hasHeader(name); - }, - - // - // see nsIHttpRequest.headers - // - get headers() - { - return this._headers.enumerator; - }, - - // - // see nsIPropertyBag.enumerator - // - get enumerator() - { - this._ensurePropertyBag(); - return this._bag.enumerator; - }, - - // - // see nsIHttpRequest.headers - // - get bodyInputStream() - { - return this._bodyInputStream; - }, - - // - // see nsIPropertyBag.getProperty - // - getProperty: function(name) - { - this._ensurePropertyBag(); - return this._bag.getProperty(name); - }, - - - // NSISUPPORTS - - // - // see nsISupports.QueryInterface - // - QueryInterface: function(iid) - { - if (iid.equals(Ci.nsIHttpRequest) || iid.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - - // PRIVATE IMPLEMENTATION - - /** Ensures a property bag has been created for ad-hoc behaviors. */ - _ensurePropertyBag: function() - { - if (!this._bag) - this._bag = new WritablePropertyBag(); - } -}; - - -// XPCOM trappings -if ("XPCOMUtils" in this && // Firefox 3.6 doesn't load XPCOMUtils in this scope for some reason... - "generateNSGetFactory" in XPCOMUtils) { - var NSGetFactory = XPCOMUtils.generateNSGetFactory([nsHttpServer]); -} - - - -/** -* Creates a new HTTP server listening for loopback traffic on the given port, -* starts it, and runs the server until the server processes a shutdown request, -* spinning an event loop so that events posted by the server's socket are -* processed. -* -* This method is primarily intended for use in running this script from within -* xpcshell and running a functional HTTP server without having to deal with -* non-essential details. -* -* Note that running multiple servers using variants of this method probably -* doesn't work, simply due to how the internal event loop is spun and stopped. -* -* @note -* This method only works with Mozilla 1.9 (i.e., Firefox 3 or trunk code); -* you should use this server as a component in Mozilla 1.8. -* @param port -* the port on which the server will run, or -1 if there exists no preference -* for a specific port; note that attempting to use some values for this -* parameter (particularly those below 1024) may cause this method to throw or -* may result in the server being prematurely shut down -* @param basePath -* a local directory from which requests will be served (i.e., if this is -* "/home/jwalden/" then a request to /index.html will load -* /home/jwalden/index.html); if this is omitted, only the default URLs in -* this server implementation will be functional -*/ -function server(port, basePath) -{ - if (basePath) - { - var lp = Cc["@mozilla.org/file/local;1"] - .createInstance(Ci.nsILocalFile); - lp.initWithPath(basePath); - } - - // if you're running this, you probably want to see debugging info - DEBUG = true; - - var srv = new nsHttpServer(); - if (lp) - srv.registerDirectory("/", lp); - srv.registerContentType("sjs", SJS_TYPE); - srv.identity.setPrimary("http", "localhost", port); - srv.start(port); - - var thread = gThreadManager.currentThread; - while (!srv.isStopped()) - thread.processNextEvent(true); - - // get rid of any pending requests - while (thread.hasPendingEvents()) - thread.processNextEvent(true); - - DEBUG = false; -} - -function startServerAsync(port, basePath) -{ - if (basePath) - { - var lp = Cc["@mozilla.org/file/local;1"] - .createInstance(Ci.nsILocalFile); - lp.initWithPath(basePath); - } - - var srv = new nsHttpServer(); - if (lp) - srv.registerDirectory("/", lp); - srv.registerContentType("sjs", "sjs"); - srv.identity.setPrimary("http", "localhost", port); - srv.start(port); - return srv; -} - -exports.nsHttpServer = nsHttpServer; -exports.ScriptableInputStream = ScriptableInputStream; -exports.server = server; -exports.startServerAsync = startServerAsync; diff --git a/tools/addon-sdk-1.12/lib/sdk/test/loader.js b/tools/addon-sdk-1.12/lib/sdk/test/loader.js deleted file mode 100644 index 282cf5e..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/test/loader.js +++ /dev/null @@ -1,28 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { Loader, resolveURI, Require, - unload, override, descriptor } = require('../loader/cuddlefish'); - -exports.Loader = function(module, globals, packaging) { - let options = packaging || require("@loader/options"); - options = override(options, { - globals: override(require('../system/globals'), globals || {}) - }); - - let loader = Loader(options); - return Object.create(loader, descriptor({ - require: Require(loader, module), - sandbox: function(id) { - let requirement = loader.resolve(id, module.id); - let uri = resolveURI(requirement, loader.mapping); - return loader.sandboxes[uri]; - }, - unload: function(reason) { - unload(loader, reason); - } - })); -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/test/runner.js b/tools/addon-sdk-1.12/lib/sdk/test/runner.js deleted file mode 100644 index 4db9aca..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/test/runner.js +++ /dev/null @@ -1,150 +0,0 @@ -/* 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"; - -var obsvc = require("../deprecated/observer-service"); -var { exit, stdout } = require("../system"); -var cfxArgs = require("@test/options"); -var { Cc, Ci} = require("chrome"); - -function runTests(findAndRunTests) { - var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"] - .getService(Ci.nsIWindowWatcher); - - let ns = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; - let msg = 'Running tests...'; - let markup = '<?xml version="1.0"?><window xmlns="' + ns + - '" windowtype="test:runner"><label>' + msg + '</label></window>'; - let url = "data:application/vnd.mozilla.xul+xml;charset=utf-8," + escape(markup); - - - var window = ww.openWindow(null, url, "harness", "centerscreen", null); - - var harness = require("./harness"); - - function onDone(tests) { - stdout.write("\n"); - var total = tests.passed + tests.failed; - stdout.write(tests.passed + " of " + total + " tests passed.\n"); - - window.close(); - if (tests.failed == 0) { - if (tests.passed === 0) - stdout.write("No tests were run\n"); - exit(0); - } else { - printFailedTests(tests, cfxArgs.verbose, stdout.write); - exit(1); - } - }; - - // We have to wait for this window to be fully loaded *and* focused - // in order to avoid it to mess with our various window/focus tests. - // We are first waiting for our window to be fully loaded before ensuring - // that it will take the focus, and then we wait for it to be focused. - window.addEventListener("load", function onload() { - window.removeEventListener("load", onload, true); - - window.addEventListener("focus", function onfocus() { - window.removeEventListener("focus", onfocus, true); - // Finally, we have to run test on next cycle, otherwise XPCOM components - // are not correctly updated. - // For ex: nsIFocusManager.getFocusedElementForWindow may throw - // NS_ERROR_ILLEGAL_VALUE exception. - require("../timers").setTimeout(function () { - harness.runTests({ - findAndRunTests: findAndRunTests, - iterations: cfxArgs.iterations || 1, - filter: cfxArgs.filter, - profileMemory: cfxArgs.profileMemory, - stopOnError: cfxArgs.stopOnError, - verbose: cfxArgs.verbose, - print: stdout.write, - onDone: onDone - }); - }, 0); - }, true); - window.focus(); - }, true); -} - -function printFailedTests(tests, verbose, print) { - if (!verbose) - return; - - let iterationNumber = 0; - let singleIteration = tests.testRuns.length == 1; - let padding = singleIteration ? "" : " "; - - print("\nThe following tests failed:\n"); - - for each (let testRun in tests.testRuns) { - iterationNumber++; - - if (!singleIteration) - print(" Iteration " + iterationNumber + ":\n"); - - for each (let test in testRun) { - if (test.failed > 0) { - print(padding + " " + test.name + ": " + test.errors +"\n"); - } - } - print("\n"); - } -} - -function main() { - var testsStarted = false; - - if (!testsStarted) { - testsStarted = true; - runTests(function findAndRunTests(loader, nextIteration) { - loader.require("../deprecated/unit-test").findAndRunTests({ - testOutOfProcess: false, - testInProcess: true, - stopOnError: cfxArgs.stopOnError, - filter: cfxArgs.filter, - onDone: nextIteration - }); - }); - } -}; - -if (require.main === module) - main(); - -exports.runTestsFromModule = function runTestsFromModule(module) { - let id = module.id; - // Make a copy of exports as it may already be frozen by module loader - let exports = {}; - Object.keys(module.exports).forEach(function(key) { - exports[key] = module.exports[key]; - }); - - runTests(function findAndRunTests(loader, nextIteration) { - // Consider that all these tests are CommonJS ones - loader.require('../test').run(exports); - - // Reproduce what is done in unit-test-finder.findTests() - let tests = []; - for each (let name in Object.keys(exports).sort()) { - tests.push({ - setup: exports.setup, - teardown: exports.teardown, - testFunction: exports[name], - name: id + "." + name - }); - } - - // Reproduce what is done by unit-test.findAndRunTests() - var { TestRunner } = loader.require("../deprecated/unit-test"); - var runner = new TestRunner(); - runner.startMany({ - tests: tests, - stopOnError: cfxArgs.stopOnError, - onDone: nextIteration - }); - }); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/test/tmp-file.js b/tools/addon-sdk-1.12/lib/sdk/test/tmp-file.js deleted file mode 100644 index 634d2f1..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/test/tmp-file.js +++ /dev/null @@ -1,73 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const { Cc, Ci } = require("chrome"); - -const system = require("sdk/system"); -const file = require("sdk/io/file"); -const unload = require("sdk/system/unload"); - -// Retrieve the path to the OS temporary directory: -const tmpDir = require("sdk/system").pathFor("TmpD"); - -// List of all tmp file created -let files = []; - -// Remove all tmp files on addon disabling -unload.when(function () { - files.forEach(function (path){ - // Catch exception in order to avoid leaking following files - try { - if (file.exists(path)) - file.remove(path); - } - catch(e) { - console.exception(e); - } - }); -}); - -// Utility function that synchronously reads local resource from the given -// `uri` and returns content string. Read in binary mode. -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 = Cc["@mozilla.org/binaryinputstream;1"]. - createInstance(Ci.nsIBinaryInputStream); - stream.setInputStream(channel.open()); - - let data = ""; - while (true) { - let available = stream.available(); - if (available <= 0) - break; - data += stream.readBytes(available); - } - stream.close(); - - return data; -} - -// Create a temporary file from a given string and returns its path -exports.createFromString = function createFromString(data, tmpName) { - let filename = (tmpName ? tmpName : "tmp-file") + "-" + (new Date().getTime()); - let path = file.join(tmpDir, filename); - - let tmpFile = file.open(path, "wb"); - tmpFile.write(data); - tmpFile.close(); - - // Register tmp file path - files.push(path); - - return path; -} - -// Create a temporary file from a given URL and returns its path -exports.createFromURL = function createFromURL(url, tmpName) { - let data = readBinaryURI(url); - return exports.createFromString(data, tmpName); -} - diff --git a/tools/addon-sdk-1.12/lib/sdk/timers.js b/tools/addon-sdk-1.12/lib/sdk/timers.js deleted file mode 100644 index bc72717..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/timers.js +++ /dev/null @@ -1,53 +0,0 @@ -/* 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'; - -module.metadata = { - "stability": "deprecated" -}; - -const { CC, Ci } = require('chrome'); -const { when: unload } = require('./system/unload'); - -const { TYPE_ONE_SHOT, TYPE_REPEATING_SLACK } = Ci.nsITimer; -const Timer = CC('@mozilla.org/timer;1', 'nsITimer'); -const timers = Object.create(null); - -// Last timer id. -let lastID = 0; - -// Sets typer either by timeout or by interval -// depending on a given type. -function setTimer(type, callback, delay) { - let id = ++ lastID; - let timer = timers[id] = Timer(); - let args = Array.slice(arguments, 3); - timer.initWithCallback({ - notify: function notify() { - try { - if (type === TYPE_ONE_SHOT) - delete timers[id]; - callback.apply(null, args); - } - catch(error) { - console.exception(error); - } - } - }, delay || 0, type); - return id; -} - -function unsetTimer(id) { - let timer = timers[id]; - delete timers[id]; - if (timer) - timer.cancel(); -} - -exports.setTimeout = setTimer.bind(null, TYPE_ONE_SHOT); -exports.setInterval = setTimer.bind(null, TYPE_REPEATING_SLACK); -exports.clearTimeout = unsetTimer.bind(null); -exports.clearInterval = unsetTimer.bind(null); - -unload(function() { Object.keys(timers).forEach(unsetTimer) }); diff --git a/tools/addon-sdk-1.12/lib/sdk/url.js b/tools/addon-sdk-1.12/lib/sdk/url.js deleted file mode 100644 index 3581398..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/url.js +++ /dev/null @@ -1,236 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "experimental" -}; - -const { Cc, Ci, Cr } = require("chrome"); - -const { Class } = require("./core/heritage"); -const base64 = require("./base64"); - -var ios = Cc['@mozilla.org/network/io-service;1'] - .getService(Ci.nsIIOService); - -var resProt = ios.getProtocolHandler("resource") - .QueryInterface(Ci.nsIResProtocolHandler); - -function newURI(uriStr, base) { - try { - let baseURI = base ? ios.newURI(base, null, null) : null; - return ios.newURI(uriStr, null, baseURI); - } - catch (e if e.result == Cr.NS_ERROR_MALFORMED_URI) { - throw new Error("malformed URI: " + uriStr); - } - catch (e if (e.result == Cr.NS_ERROR_FAILURE || - e.result == Cr.NS_ERROR_ILLEGAL_VALUE)) { - throw new Error("invalid URI: " + uriStr); - } -} - -function resolveResourceURI(uri) { - var resolved; - try { - resolved = resProt.resolveURI(uri); - } catch (e if e.result == Cr.NS_ERROR_NOT_AVAILABLE) { - throw new Error("resource does not exist: " + uri.spec); - }; - return resolved; -} - -let fromFilename = exports.fromFilename = function fromFilename(path) { - var file = Cc['@mozilla.org/file/local;1'] - .createInstance(Ci.nsILocalFile); - file.initWithPath(path); - return ios.newFileURI(file).spec; -}; - -let toFilename = exports.toFilename = function toFilename(url) { - var uri = newURI(url); - if (uri.scheme == "resource") - uri = newURI(resolveResourceURI(uri)); - if (uri.scheme == "chrome") { - var channel = ios.newChannelFromURI(uri); - try { - channel = channel.QueryInterface(Ci.nsIFileChannel); - return channel.file.path; - } catch (e if e.result == Cr.NS_NOINTERFACE) { - throw new Error("chrome url isn't on filesystem: " + url); - } - } - if (uri.scheme == "file") { - var file = uri.QueryInterface(Ci.nsIFileURL).file; - return file.path; - } - throw new Error("cannot map to filename: " + url); -}; - -function URL(url, base) { - if (!(this instanceof URL)) { - return new URL(url, base); - } - - var uri = newURI(url, base); - - var userPass = null; - try { - userPass = uri.userPass ? uri.userPass : null; - } catch (e if e.result == Cr.NS_ERROR_FAILURE) {} - - var host = null; - try { - host = uri.host; - } catch (e if e.result == Cr.NS_ERROR_FAILURE) {} - - var port = null; - try { - port = uri.port == -1 ? null : uri.port; - } catch (e if e.result == Cr.NS_ERROR_FAILURE) {} - - this.__defineGetter__("scheme", function() uri.scheme); - this.__defineGetter__("userPass", function() userPass); - this.__defineGetter__("host", function() host); - this.__defineGetter__("port", function() port); - this.__defineGetter__("path", function() uri.path); - - Object.defineProperties(this, { - toString: { - value: function URL_toString() new String(uri.spec).toString(), - enumerable: false - }, - valueOf: { - value: function() new String(uri.spec).valueOf(), - enumerable: false - }, - toSource: { - value: function() new String(uri.spec).toSource(), - enumerable: false - } - }); - - return this; -}; - -URL.prototype = Object.create(String.prototype); -exports.URL = URL; - -/** - * Parse and serialize a Data URL. - * - * See: http://tools.ietf.org/html/rfc2397 - * - * Note: Could be extended in the future to decode / encode automatically binary - * data. - */ -const DataURL = Class({ - - get base64 () { - return "base64" in this.parameters; - }, - - set base64 (value) { - if (value) - this.parameters["base64"] = ""; - else - delete this.parameters["base64"]; - }, - /** - * Initialize the Data URL object. If a uri is given, it will be parsed. - * - * @param {String} [uri] The uri to parse - * - * @throws {URIError} if the Data URL is malformed - */ - initialize: function(uri) { - // Due to bug 751834 it is not possible document and define these - // properties in the prototype. - - /** - * An hashmap that contains the parameters of the Data URL. By default is - * empty, that accordingly to RFC is equivalent to {"charset" : "US-ASCII"} - */ - this.parameters = {}; - - /** - * The MIME type of the data. By default is empty, that accordingly to RFC - * is equivalent to "text/plain" - */ - this.mimeType = ""; - - /** - * The string that represent the data in the Data URL - */ - this.data = ""; - - if (typeof uri === "undefined") - return; - - uri = String(uri); - - let matches = uri.match(/^data:([^,]*),(.*)$/i); - - if (!matches) - throw new URIError("Malformed Data URL: " + uri); - - let mediaType = matches[1].trim(); - - this.data = decodeURIComponent(matches[2].trim()); - - if (!mediaType) - return; - - let parametersList = mediaType.split(";"); - - this.mimeType = parametersList.shift().trim(); - - for (let parameter, i = 0; parameter = parametersList[i++];) { - let pairs = parameter.split("="); - let name = pairs[0].trim(); - let value = pairs.length > 1 ? decodeURIComponent(pairs[1].trim()) : ""; - - this.parameters[name] = value; - } - - if (this.base64) - this.data = base64.decode(this.data); - - }, - - /** - * Returns the object as a valid Data URL string - * - * @returns {String} The Data URL - */ - toString : function() { - let parametersList = []; - - for (let name in this.parameters) { - let encodedParameter = encodeURIComponent(name); - let value = this.parameters[name]; - - if (value) - encodedParameter += "=" + encodeURIComponent(value); - - parametersList.push(encodedParameter); - } - - // If there is at least a parameter, add an empty string in order - // to start with a `;` on join call. - if (parametersList.length > 0) - parametersList.unshift(""); - - let data = this.base64 ? base64.encode(this.data) : this.data; - - return "data:" + - this.mimeType + - parametersList.join(";") + "," + - encodeURIComponent(data); - } -}); - -exports.DataURL = DataURL; diff --git a/tools/addon-sdk-1.12/lib/sdk/util/array.js b/tools/addon-sdk-1.12/lib/sdk/util/array.js deleted file mode 100644 index 764eeca..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/util/array.js +++ /dev/null @@ -1,89 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "experimental" -}; - -/** - * Returns `true` if given `array` contain given `element` or `false` - * otherwise. - * @param {Array} array - * Target array. - * @param {Object|String|Number|Boolean} element - * Element being looked up. - * @returns {Boolean} - */ -var has = exports.has = function has(array, element) { - // shorter and faster equivalent of `array.indexOf(element) >= 0` - return !!~array.indexOf(element); -}; -var hasAny = exports.hasAny = function hasAny(array, elements) { - if (arguments.length < 2) - return false; - if (!Array.isArray(elements)) - elements = [ elements ]; - return array.some(function (element) { - return has(elements, element); - }); -}; - -/** - * Adds given `element` to the given `array` if it does not contain it yet. - * `true` is returned if element was added otherwise `false` is returned. - * @param {Array} array - * Target array. - * @param {Object|String|Number|Boolean} element - * Element to be added. - * @returns {Boolean} - */ -var add = exports.add = function add(array, element) { - var result; - if ((result = !has(array, element))) - array.push(element); - - return result; -}; - -/** - * Removes first occurrence of the given `element` from the given `array`. If - * `array` does not contain given `element` `false` is returned otherwise - * `true` is returned. - * @param {Array} array - * Target array. - * @param {Object|String|Number|Boolean} element - * Element to be removed. - * @returns {Boolean} - */ -exports.remove = function remove(array, element) { - var result; - if ((result = has(array, element))) - array.splice(array.indexOf(element), 1); - - return result; -}; - -/** - * Produces a duplicate-free version of the given `array`. - * @param {Array} array - * Source array. - * @returns {Array} - */ -exports.unique = function unique(array) { - var value = []; - return array.forEach(function(element) { - add(value, element); - }); - return value; -}; - -exports.flatten = function flatten(array){ - var flat = []; - for (var i = 0, l = array.length; i < l; i++) { - flat = flat.concat(Array.isArray(array[i]) ? flatten(array[i]) : array[i]); - } - return flat; -}; diff --git a/tools/addon-sdk-1.12/lib/sdk/util/collection.js b/tools/addon-sdk-1.12/lib/sdk/util/collection.js deleted file mode 100644 index 54e7313..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/util/collection.js +++ /dev/null @@ -1,112 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "experimental" -}; - -exports.Collection = Collection; - -/** - * Adds a collection property to the given object. Setting the property to a - * scalar value empties the collection and adds the value. Setting it to an - * array empties the collection and adds all the items in the array. - * - * @param obj - * The property will be defined on this object. - * @param propName - * The name of the property. - * @param array - * If given, this will be used as the collection's backing array. - */ -exports.addCollectionProperty = function addCollProperty(obj, propName, array) { - array = array || []; - let publicIface = new Collection(array); - - obj.__defineSetter__(propName, function (itemOrItems) { - array.splice(0, array.length); - publicIface.add(itemOrItems); - }); - - obj.__defineGetter__(propName, function () { - return publicIface; - }); -}; - -/** - * A collection is ordered, like an array, but its items are unique, like a set. - * - * @param array - * The collection is backed by an array. If this is given, it will be - * used as the backing array. This way the caller can fully control the - * collection. Otherwise a new empty array will be used, and no one but - * the collection will have access to it. - */ -function Collection(array) { - array = array || []; - - /** - * Provides iteration over the collection. Items are yielded in the order - * they were added. - */ - this.__iterator__ = function Collection___iterator__() { - let items = array.slice(); - for (let i = 0; i < items.length; i++) - yield items[i]; - }; - - /** - * The number of items in the collection. - */ - this.__defineGetter__("length", function Collection_get_length() { - return array.length; - }); - - /** - * Adds a single item or an array of items to the collection. Any items - * already contained in the collection are ignored. - * - * @param itemOrItems - * An item or array of items. - * @return The collection. - */ - this.add = function Collection_add(itemOrItems) { - let items = toArray(itemOrItems); - for (let i = 0; i < items.length; i++) { - let item = items[i]; - if (array.indexOf(item) < 0) - array.push(item); - } - return this; - }; - - /** - * Removes a single item or an array of items from the collection. Any items - * not contained in the collection are ignored. - * - * @param itemOrItems - * An item or array of items. - * @return The collection. - */ - this.remove = function Collection_remove(itemOrItems) { - let items = toArray(itemOrItems); - for (let i = 0; i < items.length; i++) { - let idx = array.indexOf(items[i]); - if (idx >= 0) - array.splice(idx, 1); - } - return this; - }; -}; - -function toArray(itemOrItems) { - let isArr = itemOrItems && - itemOrItems.constructor && - itemOrItems.constructor.name === "Array"; - return isArr ? itemOrItems : [itemOrItems]; -} diff --git a/tools/addon-sdk-1.12/lib/sdk/util/deprecate.js b/tools/addon-sdk-1.12/lib/sdk/util/deprecate.js deleted file mode 100644 index b38606a..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/util/deprecate.js +++ /dev/null @@ -1,27 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "experimental" -}; - -const traceback = require("../console/traceback"); - -function deprecateUsage(msg) { - // Print caller stacktrace in order to help figuring out which code - // does use deprecated thing - let stack = traceback.get().slice(0, -2); - console.error("DEPRECATED: " + msg + "\n" + traceback.format(stack)); -} -exports.deprecateUsage = deprecateUsage; - -function deprecateFunction(fun, msg) { - return function deprecated() { - deprecateUsage(msg); - return fun.apply(this, arguments); - }; -} -exports.deprecateFunction = deprecateFunction; diff --git a/tools/addon-sdk-1.12/lib/sdk/util/list.js b/tools/addon-sdk-1.12/lib/sdk/util/list.js deleted file mode 100644 index 0a143d9..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/util/list.js +++ /dev/null @@ -1,78 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Class } = require('../core/heritage'); -const listNS = require('../core/namespace').ns(); - -const List = Class({ - /** - * List constructor can take any number of element to populate itself. - * @params {Object|String|Number} element - * @example - * List(1,2,3).length == 3 // true - */ - initialize: function List() { - listNS(this).keyValueMap = []; - - for (let i = 0, ii = arguments.length; i < ii; i++) - addListItem(this, arguments[i]); - }, - /** - * Number of elements in this list. - * @type {Number} - */ - get length() listNS(this).keyValueMap.length, - /** - * Returns a string representing this list. - * @returns {String} - */ - toString: function toString() 'List(' + listNS(this).keyValueMap + ')', - /** - * Custom iterator providing `List`s enumeration behavior. - * We cant reuse `_iterator` that is defined by `Iterable` since it provides - * iteration in an arbitrary order. - * @see https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in - * @param {Boolean} onKeys - */ - __iterator__: function __iterator__(onKeys, onKeyValue) { - let array = listNS(this).keyValueMap.slice(0), - i = -1; - for each(let element in array) - yield onKeyValue ? [++i, element] : onKeys ? ++i : element; - } -}); -exports.List = List; - -function addListItem(that, value) { - let list = listNS(that).keyValueMap, - index = list.indexOf(value); - - if (-1 === index) { - try { - that[that.length] = value; - } - catch (e) {} - list.push(value); - } -} -exports.addListItem = addListItem; - -function removeListItem(that, element) { - let list = listNS(that).keyValueMap, - index = list.indexOf(element); - - if (0 <= index) { - list.splice(index, 1); - try { - for (let length = list.length; index < length; index++) - that[index] = list[index]; - that[list.length] = undefined; - } - catch(e){} - } -} -exports.removeListItem = removeListItem; - -exports.listNS = listNS; diff --git a/tools/addon-sdk-1.12/lib/sdk/util/object.js b/tools/addon-sdk-1.12/lib/sdk/util/object.js deleted file mode 100644 index 4b1a2d4..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/util/object.js +++ /dev/null @@ -1,57 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -/** - * Merges all the properties of all arguments into first argument. If two or - * more argument objects have own properties with the same name, the property - * is overridden, with precedence from right to left, implying, that properties - * of the object on the left are overridden by a same named property of the - * object on the right. - * - * Any argument given with "falsy" value - commonly `null` and `undefined` in - * case of objects - are skipped. - * - * @examples - * var a = { bar: 0, a: 'a' } - * var b = merge(a, { foo: 'foo', bar: 1 }, { foo: 'bar', name: 'b' }); - * b === a // true - * b.a // 'a' - * b.foo // 'bar' - * b.bar // 1 - * b.name // 'b' - */ -function merge(source) { - let descriptor = {}; - // `Boolean` converts the first parameter to a boolean value. Any object is - // converted to `true` where `null` and `undefined` becames `false`. Therefore - // the `filter` method will keep only objects that are defined and not null. - Array.slice(arguments, 1).filter(Boolean).forEach(function onEach(properties) { - Object.getOwnPropertyNames(properties).forEach(function(name) { - descriptor[name] = Object.getOwnPropertyDescriptor(properties, name); - }); - }); - return Object.defineProperties(source, descriptor); -} -exports.merge = merge; - -/** - * Returns an object that inherits from the first argument and contains all the - * properties from all following arguments. - * `extend(source1, source2, source3)` is equivalent of - * `merge(Object.create(source1), source2, source3)`. - */ -function extend(source) { - let rest = Array.slice(arguments, 1); - rest.unshift(Object.create(source)); - return merge.apply(null, rest); -} -exports.extend = extend; - - diff --git a/tools/addon-sdk-1.12/lib/sdk/util/registry.js b/tools/addon-sdk-1.12/lib/sdk/util/registry.js deleted file mode 100644 index 7d385f0..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/util/registry.js +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { EventEmitter } = require('../deprecated/events'); -const unload = require('../system/unload'); - -const Registry = EventEmitter.compose({ - _registry: null, - _constructor: null, - constructor: function Registry(constructor) { - this._registry = []; - this._constructor = constructor; - this.on('error', this._onError = this._onError.bind(this)); - unload.ensure(this, "_destructor"); - }, - _destructor: function _destructor() { - let _registry = this._registry.slice(0); - for each (let instance in _registry) - this._emit('remove', instance); - this._registry.splice(0); - }, - _onError: function _onError(e) { - if (!this._listeners('error').length) - console.error(e); - }, - has: function has(instance) { - let _registry = this._registry; - return ( - (0 <= _registry.indexOf(instance)) || - (instance && instance._public && 0 <= _registry.indexOf(instance._public)) - ); - }, - add: function add(instance) { - let { _constructor, _registry } = this; - if (!(instance instanceof _constructor)) - instance = new _constructor(instance); - if (0 > _registry.indexOf(instance)) { - _registry.push(instance); - this._emit('add', instance); - } - return instance; - }, - remove: function remove(instance) { - let _registry = this._registry; - let index = _registry.indexOf(instance) - if (0 <= index) { - this._emit('remove', instance); - _registry.splice(index, 1); - } - } -}); -exports.Registry = Registry; - diff --git a/tools/addon-sdk-1.12/lib/sdk/util/uuid.js b/tools/addon-sdk-1.12/lib/sdk/util/uuid.js deleted file mode 100644 index 0ebf981..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/util/uuid.js +++ /dev/null @@ -1,17 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci, components: { ID: parseUUID } } = require('chrome'); -const { generateUUID } = Cc['@mozilla.org/uuid-generator;1']. - getService(Ci.nsIUUIDGenerator); - -// Returns `uuid`. If `id` is passed then it's parsed to `uuid` and returned -// if not then new one is generated. -exports.uuid = function uuid(id) id ? parseUUID(id) : generateUUID() diff --git a/tools/addon-sdk-1.12/lib/sdk/widget.js b/tools/addon-sdk-1.12/lib/sdk/widget.js deleted file mode 100644 index f1236aa..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/widget.js +++ /dev/null @@ -1,938 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "stable" -}; - -// Widget content types -const CONTENT_TYPE_URI = 1; -const CONTENT_TYPE_HTML = 2; -const CONTENT_TYPE_IMAGE = 3; - -const ERR_CONTENT = "No content or contentURL property found. Widgets must " - + "have one or the other.", - ERR_LABEL = "The widget must have a non-empty label property.", - ERR_ID = "You have to specify a unique value for the id property of " + - "your widget in order for the application to remember its " + - "position.", - ERR_DESTROYED = "The widget has been destroyed and can no longer be used."; - -// Supported events, mapping from DOM event names to our event names -const EVENTS = { - "click": "click", - "mouseover": "mouseover", - "mouseout": "mouseout", -}; - -if (!require("./system/xul-app").is("Firefox")) { - throw new Error([ - "The widget module currently supports only Firefox. In the future ", - "it will support other applications. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." - ].join("")); -} - -const { validateOptions } = require("./deprecated/api-utils"); -const panels = require("./panel"); -const { EventEmitter, EventEmitterTrait } = require("./deprecated/events"); -const { Trait } = require("./deprecated/traits"); -const LightTrait = require('./deprecated/light-traits').Trait; -const { Loader, Symbiont } = require("./content/content"); -const { Cortex } = require('./deprecated/cortex'); -const windowsAPI = require("./windows"); -const { WindowTracker } = require("./deprecated/window-utils"); -const { isBrowser } = require("./window/utils"); -const { setTimeout } = require("./timers"); -const unload = require("./system/unload"); -const { uuid } = require("./util/uuid"); - -// Data types definition -const valid = { - number: { is: ["null", "undefined", "number"] }, - string: { is: ["null", "undefined", "string"] }, - id: { - is: ["string"], - ok: function (v) v.length > 0, - msg: ERR_ID, - readonly: true - }, - label: { - is: ["string"], - ok: function (v) v.length > 0, - msg: ERR_LABEL - }, - panel: { - is: ["null", "undefined", "object"], - ok: function(v) !v || v instanceof panels.Panel - }, - width: { - is: ["null", "undefined", "number"], - map: function (v) { - if (null === v || undefined === v) v = 16; - return v; - }, - defaultValue: 16 - }, - allow: { - is: ["null", "undefined", "object"], - map: function (v) { - if (!v) v = { script: true }; - return v; - }, - get defaultValue() ({ script: true }) - }, -}; - -// Widgets attributes definition -let widgetAttributes = { - label: valid.label, - id: valid.id, - tooltip: valid.string, - width: valid.width, - content: valid.string, - panel: valid.panel, - allow: valid.allow -}; - -// Import data definitions from loader, but don't compose with it as Model -// functions allow us to recreate easily all Loader code. -let loaderAttributes = require("./content/loader").validationAttributes; -for (let i in loaderAttributes) - widgetAttributes[i] = loaderAttributes[i]; - -widgetAttributes.contentURL.optional = true; - -// Widgets public events list, that are automatically binded in options object -const WIDGET_EVENTS = [ - "click", - "mouseover", - "mouseout", - "error", - "message", - "attach" -]; - -// `Model` utility functions that help creating these various Widgets objects -let model = { - - // Validate one attribute using api-utils.js:validateOptions function - _validate: function _validate(name, suspect, validation) { - let $1 = {}; - $1[name] = suspect; - let $2 = {}; - $2[name] = validation; - return validateOptions($1, $2)[name]; - }, - - /** - * This method has two purposes: - * 1/ Validate and define, on a given object, a set of attribute - * 2/ Emit a "change" event on this object when an attribute is changed - * - * @params {Object} object - * Object on which we can bind attributes on and watch for their changes. - * This object must have an EventEmitter interface, or, at least `_emit` - * method - * @params {Object} attrs - * Dictionary of attributes definition following api-utils:validateOptions - * scheme - * @params {Object} values - * Dictionary of attributes default values - */ - setAttributes: function setAttributes(object, attrs, values) { - let properties = {}; - for (let name in attrs) { - let value = values[name]; - let req = attrs[name]; - - // Retrieve default value from typedef if the value is not defined - if ((typeof value == "undefined" || value == null) && req.defaultValue) - value = req.defaultValue; - - // Check for valid value if value is defined or mandatory - if (!req.optional || typeof value != "undefined") - value = model._validate(name, value, req); - - // In any case, define this property on `object` - let property = null; - if (req.readonly) { - property = { - value: value, - writable: false, - enumerable: true, - configurable: false - }; - } - else { - property = model._createWritableProperty(name, value); - } - - properties[name] = property; - } - Object.defineProperties(object, properties); - }, - - // Generate ES5 property definition for a given attribute - _createWritableProperty: function _createWritableProperty(name, value) { - return { - get: function () { - return value; - }, - set: function (newValue) { - value = newValue; - // The main goal of all this Model stuff is here: - // We want to forward all changes to some listeners - this._emit("change", name, value); - }, - enumerable: true, - configurable: false - }; - }, - - /** - * Automagically register listeners in options dictionary - * by detecting listener attributes with name starting with `on` - * - * @params {Object} object - * Target object that need to follow EventEmitter interface, or, at least, - * having `on` method. - * @params {Array} events - * List of events name to automatically bind. - * @params {Object} listeners - * Dictionary of event listener functions to register. - */ - setEvents: function setEvents(object, events, listeners) { - for (let i = 0, l = events.length; i < l; i++) { - let name = events[i]; - let onName = "on" + name[0].toUpperCase() + name.substr(1); - if (!listeners[onName]) - continue; - object.on(name, listeners[onName].bind(object)); - } - } - -}; - - -/** - * Main Widget class: entry point of the widget API - * - * Allow to control all widget across all existing windows with a single object. - * Widget.getView allow to retrieve a WidgetView instance to control a widget - * specific to one window. - */ -const WidgetTrait = LightTrait.compose(EventEmitterTrait, LightTrait({ - - _initWidget: function _initWidget(options) { - model.setAttributes(this, widgetAttributes, options); - - browserManager.validate(this); - - // We must have at least content or contentURL defined - if (!(this.content || this.contentURL)) - throw new Error(ERR_CONTENT); - - this._views = []; - - // Set tooltip to label value if we don't have tooltip defined - if (!this.tooltip) - this.tooltip = this.label; - - model.setEvents(this, WIDGET_EVENTS, options); - - this.on('change', this._onChange.bind(this)); - - let self = this; - this._port = EventEmitterTrait.create({ - emit: function () { - let args = arguments; - self._views.forEach(function(v) v.port.emit.apply(v.port, args)); - } - }); - // expose wrapped port, that exposes only public properties. - this._port._public = Cortex(this._port); - - // Register this widget to browser manager in order to create new widget on - // all new windows - browserManager.addItem(this); - }, - - _onChange: function _onChange(name, value) { - // Set tooltip to label value if we don't have tooltip defined - if (name == 'tooltip' && !value) { - // we need to change tooltip again in order to change the value of the - // attribute itself - this.tooltip = this.label; - return; - } - - // Forward attributes changes to WidgetViews - if (['width', 'tooltip', 'content', 'contentURL'].indexOf(name) != -1) { - this._views.forEach(function(v) v[name] = value); - } - }, - - _onEvent: function _onEvent(type, eventData) { - this._emit(type, eventData); - }, - - _createView: function _createView() { - // Create a new WidgetView instance - let view = WidgetView(this); - - // Keep a reference to it - this._views.push(view); - - return view; - }, - - // a WidgetView instance is destroyed - _onViewDestroyed: function _onViewDestroyed(view) { - let idx = this._views.indexOf(view); - this._views.splice(idx, 1); - }, - - /** - * Called on browser window closed, to destroy related WidgetViews - * @params {ChromeWindow} window - * Window that has been closed - */ - _onWindowClosed: function _onWindowClosed(window) { - for each (let view in this._views) { - if (view._isInChromeWindow(window)) { - view.destroy(); - break; - } - } - }, - - /** - * Get the WidgetView instance related to a BrowserWindow instance - * @params {BrowserWindow} window - * BrowserWindow reference from "windows" module - */ - getView: function getView(window) { - for each (let view in this._views) { - if (view._isInWindow(window)) { - return view._public; - } - } - return null; - }, - - get port() this._port._public, - set port(v) {}, // Work around Cortex failure with getter without setter - // See bug 653464 - _port: null, - - postMessage: function postMessage(message) { - this._views.forEach(function(v) v.postMessage(message)); - }, - - destroy: function destroy() { - if (this.panel) - this.panel.destroy(); - - // Dispatch destroy calls to views - // we need to go backward as we remove items from this array in - // _onViewDestroyed - for (let i = this._views.length - 1; i >= 0; i--) - this._views[i].destroy(); - - // Unregister widget to stop creating it over new windows - // and allow creation of new widget with same id - browserManager.removeItem(this); - } - -})); - -// Widget constructor -const Widget = function Widget(options) { - let w = WidgetTrait.create(Widget.prototype); - w._initWidget(options); - - // Return a Cortex of widget in order to hide private attributes like _onEvent - let _public = Cortex(w); - unload.ensure(_public, "destroy"); - return _public; -} -exports.Widget = Widget; - - - -/** - * WidgetView is an instance of a widget for a specific window. - * - * This is an external API that can be retrieved by calling Widget.getView or - * by watching `attach` event on Widget. - */ -const WidgetViewTrait = LightTrait.compose(EventEmitterTrait, LightTrait({ - - // Reference to the matching WidgetChrome - // set right after constructor call - _chrome: null, - - // Public interface of the WidgetView, passed in `attach` event or in - // Widget.getView - _public: null, - - _initWidgetView: function WidgetView__initWidgetView(baseWidget) { - this._baseWidget = baseWidget; - - model.setAttributes(this, widgetAttributes, baseWidget); - - this.on('change', this._onChange.bind(this)); - - let self = this; - this._port = EventEmitterTrait.create({ - emit: function () { - if (!self._chrome) - throw new Error(ERR_DESTROYED); - self._chrome.update(self._baseWidget, "emit", arguments); - } - }); - // expose wrapped port, that exposes only public properties. - this._port._public = Cortex(this._port); - - this._public = Cortex(this); - }, - - // Called by WidgetChrome, when the related Worker is applied to the document, - // so that we can start sending events to it - _onWorkerReady: function () { - // Emit an `attach` event with a WidgetView instance without private attrs - this._baseWidget._emit("attach", this._public); - }, - - _onChange: function WidgetView__onChange(name, value) { - if (name == 'tooltip' && !value) { - this.tooltip = this.label; - return; - } - - // Forward attributes changes to WidgetChrome instance - if (['width', 'tooltip', 'content', 'contentURL'].indexOf(name) != -1) { - this._chrome.update(this._baseWidget, name, value); - } - }, - - _onEvent: function WidgetView__onEvent(type, eventData, domNode) { - // Dispatch event in view - this._emit(type, eventData); - - // And forward it to the main Widget object - if ("click" == type || type.indexOf("mouse") == 0) - this._baseWidget._onEvent(type, this._public); - else - this._baseWidget._onEvent(type, eventData); - - // Special case for click events: if the widget doesn't have a click - // handler, but it does have a panel, display the panel. - if ("click" == type && !this._listeners("click").length && this.panel) - this.panel.show(domNode); - }, - - _isInWindow: function WidgetView__isInWindow(window) { - return windowsAPI.BrowserWindow({ - window: this._chrome.window - }) == window; - }, - - _isInChromeWindow: function WidgetView__isInChromeWindow(window) { - return this._chrome.window == window; - }, - - _onPortEvent: function WidgetView__onPortEvent(args) { - let port = this._port; - port._emit.apply(port, args); - let basePort = this._baseWidget._port; - basePort._emit.apply(basePort, args); - }, - - get port() this._port._public, - set port(v) {}, // Work around Cortex failure with getter without setter - // See bug 653464 - _port: null, - - postMessage: function WidgetView_postMessage(message) { - if (!this._chrome) - throw new Error(ERR_DESTROYED); - this._chrome.update(this._baseWidget, "postMessage", message); - }, - - destroy: function WidgetView_destroy() { - this._chrome.destroy(); - delete this._chrome; - this._baseWidget._onViewDestroyed(this); - this._emit("detach"); - } - -})); - -const WidgetView = function WidgetView(baseWidget) { - let w = WidgetViewTrait.create(WidgetView.prototype); - w._initWidgetView(baseWidget); - return w; -} - - - -/** - * Keeps track of all browser windows. - * Exposes methods for adding/removing widgets - * across all open windows (and future ones). - * Create a new instance of BrowserWindow per window. - */ -let browserManager = { - items: [], - windows: [], - - // Registers the manager to listen for window openings and closings. Note - // that calling this method can cause onTrack to be called immediately if - // there are open windows. - init: function () { - let windowTracker = new WindowTracker(this); - unload.ensure(windowTracker); - }, - - // Registers a window with the manager. This is a WindowTracker callback. - onTrack: function browserManager_onTrack(window) { - if (isBrowser(window)) { - let win = new BrowserWindow(window); - win.addItems(this.items); - this.windows.push(win); - } - }, - - // Unregisters a window from the manager. It's told to undo all - // modifications. This is a WindowTracker callback. Note that when - // WindowTracker is unloaded, it calls onUntrack for every currently opened - // window. The browserManager therefore doesn't need to specially handle - // unload itself, since unloading the browserManager means untracking all - // currently opened windows. - onUntrack: function browserManager_onUntrack(window) { - if (isBrowser(window)) { - this.items.forEach(function(i) i._onWindowClosed(window)); - for (let i = 0; i < this.windows.length; i++) { - if (this.windows[i].window == window) { - this.windows.splice(i, 1)[0]; - return; - } - } - - } - }, - - // Used to validate widget by browserManager before adding it, - // in order to check input very early in widget constructor - validate : function (item) { - let idx = this.items.indexOf(item); - if (idx > -1) - throw new Error("The widget " + item + " has already been added."); - if (item.id) { - let sameId = this.items.filter(function(i) i.id == item.id); - if (sameId.length > 0) - throw new Error("This widget ID is already used: " + item.id); - } else { - item.id = this.items.length; - } - }, - - // Registers an item with the manager. It's added to all currently registered - // windows, and when new windows are registered it will be added to them, too. - addItem: function browserManager_addItem(item) { - this.items.push(item); - this.windows.forEach(function (w) w.addItems([item])); - }, - - // Unregisters an item from the manager. It's removed from all windows that - // are currently registered. - removeItem: function browserManager_removeItem(item) { - let idx = this.items.indexOf(item); - if (idx > -1) - this.items.splice(idx, 1); - } -}; - - - -/** - * Keeps track of a single browser window. - * - * This is where the core of how a widget's content is added to a window lives. - */ -function BrowserWindow(window) { - this.window = window; - this.doc = window.document; -} - -BrowserWindow.prototype = { - - // Adds an array of items to the window. - addItems: function BW_addItems(items) { - items.forEach(this._addItemToWindow, this); - }, - - _addItemToWindow: function BW__addItemToWindow(baseWidget) { - // Create a WidgetView instance - let widget = baseWidget._createView(); - - // Create a WidgetChrome instance - let item = new WidgetChrome({ - widget: widget, - doc: this.doc, - window: this.window - }); - - widget._chrome = item; - - this._insertNodeInToolbar(item.node); - - // We need to insert Widget DOM Node before finishing widget view creation - // (because fill creates an iframe and tries to access its docShell) - item.fill(); - }, - - _insertNodeInToolbar: function BW__insertNodeInToolbar(node) { - // Add to the customization palette - let toolbox = this.doc.getElementById("navigator-toolbox"); - let palette = toolbox.palette; - palette.appendChild(node); - - // Search for widget toolbar by reading toolbar's currentset attribute - let container = null; - let toolbars = this.doc.getElementsByTagName("toolbar"); - let id = node.getAttribute("id"); - for (let i = 0, l = toolbars.length; i < l; i++) { - let toolbar = toolbars[i]; - if (toolbar.getAttribute("currentset").indexOf(id) == -1) - continue; - container = toolbar; - } - - // if widget isn't in any toolbar, add it to the addon-bar - // TODO: we may want some "first-launch" module to do this only on very - // first execution - if (!container) { - container = this.doc.getElementById("addon-bar"); - // TODO: find a way to make the following code work when we use "cfx run": - // http://mxr.mozilla.org/mozilla-central/source/browser/base/content/browser.js#8586 - // until then, force display of addon bar directly from sdk code - // https://bugzilla.mozilla.org/show_bug.cgi?id=627484 - if (container.collapsed) - this.window.toggleAddonBar(); - } - - // Now retrieve a reference to the next toolbar item - // by reading currentset attribute on the toolbar - let nextNode = null; - let currentSet = container.getAttribute("currentset"); - let ids = (currentSet == "__empty") ? [] : currentSet.split(","); - let idx = ids.indexOf(id); - if (idx != -1) { - for (let i = idx; i < ids.length; i++) { - nextNode = this.doc.getElementById(ids[i]); - if (nextNode) - break; - } - } - - // Finally insert our widget in the right toolbar and in the right position - container.insertItem(id, nextNode, null, false); - - // Update DOM in order to save position: which toolbar, and which position - // in this toolbar. But only do this the first time we add it to the toolbar - // Otherwise, this code will collide with other instance of Widget module - // during Firefox startup. See bug 685929. - if (ids.indexOf(id) == -1) { - container.setAttribute("currentset", container.currentSet); - // Save DOM attribute in order to save position on new window opened - this.window.document.persist(container.id, "currentset"); - } - } -} - - -/** - * Final Widget class that handles chrome DOM Node: - * - create initial DOM nodes - * - receive instruction from WidgetView through update method and update DOM - * - watch for DOM events and forward them to WidgetView - */ -function WidgetChrome(options) { - this.window = options.window; - this._doc = options.doc; - this._widget = options.widget; - this._symbiont = null; // set later - this.node = null; // set later - - this._createNode(); -} - -// Update a property of a widget. -WidgetChrome.prototype.update = function WC_update(updatedItem, property, value) { - switch(property) { - case "contentURL": - case "content": - this.setContent(); - break; - case "width": - this.node.style.minWidth = value + "px"; - this.node.querySelector("iframe").style.width = value + "px"; - break; - case "tooltip": - this.node.setAttribute("tooltiptext", value); - break; - case "postMessage": - this._symbiont.postMessage(value); - break; - case "emit": - let port = this._symbiont.port; - port.emit.apply(port, value); - break; - } -} - -// Add a widget to this window. -WidgetChrome.prototype._createNode = function WC__createNode() { - // XUL element container for widget - let node = this._doc.createElement("toolbaritem"); - let guid = String(uuid()); - - // Temporary work around require("self") failing on unit-test execution ... - let jetpackID = "testID"; - try { - jetpackID = require("./self").id; - } catch(e) {} - - // Compute an unique and stable widget id with jetpack id and widget.id - let id = "widget:" + jetpackID + "-" + this._widget.id; - node.setAttribute("id", id); - node.setAttribute("label", this._widget.label); - node.setAttribute("tooltiptext", this._widget.tooltip); - node.setAttribute("align", "center"); - // Bug 626326: Prevent customize toolbar context menu to appear - node.setAttribute("context", ""); - - // TODO move into a stylesheet, configurable by consumers. - // Either widget.style, exposing the style object, or a URL - // (eg, can load local stylesheet file). - node.setAttribute("style", [ - "overflow: hidden; margin: 1px 2px 1px 2px; padding: 0px;", - "min-height: 16px;", - ].join("")); - - node.style.minWidth = this._widget.width + "px"; - - this.node = node; -} - -// Initial population of a widget's content. -WidgetChrome.prototype.fill = function WC_fill() { - // Create element - var iframe = this._doc.createElement("iframe"); - iframe.setAttribute("type", "content"); - iframe.setAttribute("transparent", "transparent"); - iframe.style.overflow = "hidden"; - iframe.style.height = "16px"; - iframe.style.maxHeight = "16px"; - iframe.style.width = this._widget.width + "px"; - iframe.setAttribute("flex", "1"); - iframe.style.border = "none"; - iframe.style.padding = "0px"; - - // Do this early, because things like contentWindow are null - // until the node is attached to a document. - this.node.appendChild(iframe); - - // add event handlers - this.addEventHandlers(); - - // set content - this.setContent(); -} - -// Get widget content type. -WidgetChrome.prototype.getContentType = function WC_getContentType() { - if (this._widget.content) - return CONTENT_TYPE_HTML; - return (this._widget.contentURL && /\.(jpg|gif|png|ico)$/.test(this._widget.contentURL)) - ? CONTENT_TYPE_IMAGE : CONTENT_TYPE_URI; -} - -// Set widget content. -WidgetChrome.prototype.setContent = function WC_setContent() { - let type = this.getContentType(); - let contentURL = null; - - switch (type) { - case CONTENT_TYPE_HTML: - contentURL = "data:text/html;charset=utf-8," + encodeURIComponent(this._widget.content); - break; - case CONTENT_TYPE_URI: - contentURL = this._widget.contentURL; - break; - case CONTENT_TYPE_IMAGE: - let imageURL = this._widget.contentURL; - contentURL = "data:text/html;charset=utf-8,<html><body><img src='" + - encodeURI(imageURL) + "'></body></html>"; - break; - default: - throw new Error("The widget's type cannot be determined."); - } - - let iframe = this.node.firstElementChild; - - let self = this; - // Cleanup previously created symbiont (in case we are update content) - if (this._symbiont) - this._symbiont.destroy(); - - this._symbiont = Trait.compose(Symbiont.resolve({ - _onContentScriptEvent: "_onContentScriptEvent-not-used", - _onInit: "_initSymbiont" - }), { - // Overload `Symbiont._onInit` in order to know when the related worker - // is ready. - _onInit: function () { - this._initSymbiont(); - self._widget._onWorkerReady(); - }, - _onContentScriptEvent: function () { - // Redirect events to WidgetView - self._widget._onPortEvent(arguments); - } - })({ - frame: iframe, - contentURL: contentURL, - contentScriptFile: this._widget.contentScriptFile, - contentScript: this._widget.contentScript, - contentScriptWhen: this._widget.contentScriptWhen, - contentScriptOptions: this._widget.contentScriptOptions, - allow: this._widget.allow, - onMessage: function(message) { - setTimeout(function() { - self._widget._onEvent("message", message); - }, 0); - } - }); -} - -// Detect if document consists of a single image. -WidgetChrome._isImageDoc = function WC__isImageDoc(doc) { - return doc.body.childNodes.length == 1 && - doc.body.firstElementChild && - doc.body.firstElementChild.tagName == "IMG"; -} - -// Set up all supported events for a widget. -WidgetChrome.prototype.addEventHandlers = function WC_addEventHandlers() { - let contentType = this.getContentType(); - - let self = this; - let listener = function(e) { - // Ignore event firings that target the iframe. - if (e.target == self.node.firstElementChild) - return; - - // The widget only supports left-click for now, - // so ignore right-clicks. - if (e.type == "click" && e.button == 2) - return; - - // Proxy event to the widget - setTimeout(function() { - self._widget._onEvent(EVENTS[e.type], null, self.node); - }, 0); - }; - - this.eventListeners = {}; - let iframe = this.node.firstElementChild; - for (let type in EVENTS) { - iframe.addEventListener(type, listener, true, true); - - // Store listeners for later removal - this.eventListeners[type] = listener; - } - - // On document load, make modifications required for nice default - // presentation. - function loadListener(e) { - let containerStyle = self.window.getComputedStyle(self.node.parentNode); - // Ignore event firings that target the iframe - if (e.target == iframe) - return; - // Ignore about:blank loads - if (e.type == "load" && e.target.location == "about:blank") - return; - - // We may have had an unload event before that cleaned up the symbiont - if (!self._symbiont) - self.setContent(); - - let doc = e.target; - if (contentType == CONTENT_TYPE_IMAGE || WidgetChrome._isImageDoc(doc)) { - // Force image content to size. - // Add-on authors must size their images correctly. - doc.body.firstElementChild.style.width = self._widget.width + "px"; - doc.body.firstElementChild.style.height = "16px"; - } - - // Extend the add-on bar's default text styles to the widget. - doc.body.style.color = containerStyle.color; - doc.body.style.fontFamily = containerStyle.fontFamily; - doc.body.style.fontSize = containerStyle.fontSize; - doc.body.style.fontWeight = containerStyle.fontWeight; - doc.body.style.textShadow = containerStyle.textShadow; - // Allow all content to fill the box by default. - doc.body.style.margin = "0"; - } - iframe.addEventListener("load", loadListener, true); - this.eventListeners["load"] = loadListener; - - // Register a listener to unload symbiont if the toolbaritem is moved - // on user toolbars customization - function unloadListener(e) { - if (e.target.location == "about:blank") - return; - self._symbiont.destroy(); - self._symbiont = null; - // This may fail but not always, it depends on how the node is - // moved or removed - try { - self.setContent(); - } catch(e) {} - - } - - iframe.addEventListener("unload", unloadListener, true); - this.eventListeners["unload"] = unloadListener; -} - -// Remove and unregister the widget from everything -WidgetChrome.prototype.destroy = function WC_destroy(removedItems) { - // remove event listeners - for (let type in this.eventListeners) { - let listener = this.eventListeners[type]; - this.node.firstElementChild.removeEventListener(type, listener, true); - } - // remove dom node - this.node.parentNode.removeChild(this.node); - // cleanup symbiont - this._symbiont.destroy(); - // cleanup itself - this.eventListeners = null; - this._widget = null; - this._symbiont = null; -} - -// Init the browserManager only after setting prototypes and such above, because -// it will cause browserManager.onTrack to be called immediately if there are -// open windows. -browserManager.init(); diff --git a/tools/addon-sdk-1.12/lib/sdk/window/browser.js b/tools/addon-sdk-1.12/lib/sdk/window/browser.js deleted file mode 100644 index 2b63a83..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/window/browser.js +++ /dev/null @@ -1,41 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Class } = require('../core/heritage'); -const { windowNS } = require('./namespace'); -const { on, off, once } = require('../event/core'); -const { method } = require('../lang/functional'); -const { getWindowTitle } = require('./utils'); -const unload = require('../system/unload'); -const { getMode } = require('../private-browsing/utils'); -const { EventTarget } = require('../event/target'); - -const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("tabs") instead'; - -const BrowserWindow = Class({ - initialize: function initialize(options) { - EventTarget.prototype.initialize.call(this, options); - windowNS(this).window = options.window; - }, - activate: function activate() { - // TODO - return null; - }, - close: function() { - throw new Error(ERR_FENNEC_MSG); - return null; - }, - get title() getWindowTitle(windowNS(this).window), - // NOTE: Fennec only has one window, which is assumed below - // TODO: remove assumption below - // NOTE: tabs requires windows - get tabs() require('../tabs'), - get activeTab() require('../tabs').activeTab, - on: method(on), - removeListener: method(off), - once: method(once), - get isPrivateBrowsing() getMode(windowNS(this).window), -}); -exports.BrowserWindow = BrowserWindow; diff --git a/tools/addon-sdk-1.12/lib/sdk/window/namespace.js b/tools/addon-sdk-1.12/lib/sdk/window/namespace.js deleted file mode 100644 index b486f88..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/window/namespace.js +++ /dev/null @@ -1,6 +0,0 @@ -/* 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"; - -exports.windowNS = require('../core/namespace').ns(); diff --git a/tools/addon-sdk-1.12/lib/sdk/window/utils.js b/tools/addon-sdk-1.12/lib/sdk/window/utils.js deleted file mode 100644 index 0bca8b1..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/window/utils.js +++ /dev/null @@ -1,205 +0,0 @@ -/* 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'; - -module.metadata = { - 'stability': 'unstable' -}; - -const { Cc, Ci } = require('chrome'); -const array = require('../util/array'); - -const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1']. - getService(Ci.nsIWindowWatcher); -const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. - getService(Ci.nsIAppShellService); -const observers = require('../deprecated/observer-service'); -const WM = Cc['@mozilla.org/appshell/window-mediator;1']. - getService(Ci.nsIWindowMediator); - -const BROWSER = 'navigator:browser', - URI_BROWSER = 'chrome://browser/content/browser.xul', - NAME = '_blank', - FEATURES = 'chrome,all,dialog=no'; - -function getMostRecentBrowserWindow() { - return WM.getMostRecentWindow(BROWSER); -} -exports.getMostRecentBrowserWindow = getMostRecentBrowserWindow; - -/** - * Returns the ID of the window's current inner window. - */ -function getInnerId(window) { - return window.QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID; -}; -exports.getInnerId = getInnerId; - -/** - * Returns the ID of the window's outer window. - */ -function getOuterId(window) { - return window.QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIDOMWindowUtils).outerWindowID; -}; -exports.getOuterId = getOuterId; - -/** - * Returns `nsIXULWindow` for the given `nsIDOMWindow`. - */ -function getXULWindow(window) { - return window.QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIWebNavigation). - QueryInterface(Ci.nsIDocShellTreeItem). - treeOwner.QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIXULWindow); -}; -exports.getXULWindow = getXULWindow; - -/** - * Returns `nsIBaseWindow` for the given `nsIDOMWindow`. - */ -function getBaseWindow(window) { - return window.QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIWebNavigation). - QueryInterface(Ci.nsIDocShell). - QueryInterface(Ci.nsIDocShellTreeItem). - treeOwner. - QueryInterface(Ci.nsIBaseWindow); -} -exports.getBaseWindow = getBaseWindow; - -function getWindowDocShell(window) window.gBrowser.docShell; -exports.getWindowDocShell = getWindowDocShell; - -function getWindowLoadingContext(window) { - return getWindowDocShell(window). - QueryInterface(Ci.nsILoadContext); -} -exports.getWindowLoadingContext = getWindowLoadingContext; - -/** - * Removes given window from the application's window registry. Unless - * `options.close` is `false` window is automatically closed on application - * quit. - * @params {nsIDOMWindow} window - * @params {Boolean} options.close - */ -function backgroundify(window, options) { - let base = getBaseWindow(window); - base.visibility = false; - base.enabled = false; - appShellService.unregisterTopLevelWindow(getXULWindow(window)); - if (!options || options.close !== false) - observers.add('quit-application-granted', window.close.bind(window)); - - return window; -} -exports.backgroundify = backgroundify; - -/** - * Takes hash of options and serializes it to a features string that - * can be used passed to `window.open`. For more details on features string see: - * https://developer.mozilla.org/en/DOM/window.open#Position_and_size_features - */ -function serializeFeatures(options) { - return Object.keys(options).reduce(function(result, name) { - let value = options[name]; - return result + ',' + name + '=' + - (value === true ? 'yes' : value === false ? 'no' : value); - }, '').substr(1); -} - -/** - * Opens a top level window and returns it's `nsIDOMWindow` representation. - * @params {String} uri - * URI of the document to be loaded into window. - * @params {nsIDOMWindow} options.parent - * Used as parent for the created window. - * @params {String} options.name - * Optional name that is assigned to the window. - * @params {Object} options.features - * Map of key, values like: `{ width: 10, height: 15, chrome: true }`. - */ -function open(uri, options) { - options = options || {}; - return windowWatcher. - openWindow(options.parent || null, - uri, - options.name || null, - serializeFeatures(options.features || {}), - options.args || null); -} -exports.open = open; - -/** - * Opens a top level window and returns it's `nsIDOMWindow` representation. - * Same as `open` but with more features - * @param {Object} options - * - */ -function openDialog(options) { - options = options || {}; - - let browser = WM.getMostRecentWindow(BROWSER); - return browser.openDialog.apply( - browser, - array.flatten([ - options.url || URI_BROWSER, - options.name || '_blank', - options.features || 'chrome,all,dialog=no', - options.args || null - ]) - ); -} -exports.openDialog = openDialog; - -/** - * Returns an array of all currently opened windows. - * Note that these windows may still be loading. - */ -function windows() { - let list = []; - let winEnum = windowWatcher.getWindowEnumerator(); - while (winEnum.hasMoreElements()) { - let window = winEnum.getNext().QueryInterface(Ci.nsIDOMWindow); - list.push(window); - } - return list; -} -exports.windows = windows; - -/** - * Check if the given window is completely loaded. - * i.e. if its "load" event has already been fired and all possible DOM content - * is done loading (the whole DOM document, images content, ...) - * @params {nsIDOMWindow} window - */ -function isDocumentLoaded(window) { - return window.document.readyState == "complete"; -} -exports.isDocumentLoaded = isDocumentLoaded; - -function isBrowser(window) { - return window.document.documentElement.getAttribute("windowtype") === BROWSER; -}; -exports.isBrowser = isBrowser; - -function getWindowTitle(window) { - return window && window.document ? window.document.title : null; -} -exports.getWindowTitle = getWindowTitle; - -function isXULBrowser(window) { - return !!(isBrowser(window) && window.XULBrowserWindow); -} -exports.isXULBrowser = isXULBrowser; - -function getFrames(window) { - return Array.slice(window.frames).reduce(function(frames, frame) { - return frames.concat(frame, getFrames(frame)) - }, []) -} -exports.getFrames = getFrames; diff --git a/tools/addon-sdk-1.12/lib/sdk/windows.js b/tools/addon-sdk-1.12/lib/sdk/windows.js deleted file mode 100644 index 15399c9..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/windows.js +++ /dev/null @@ -1,21 +0,0 @@ -/* 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'; - -module.metadata = { - 'stability': 'stable' -}; - -if (require('./system/xul-app').is('Firefox')) { - module.exports = require('./windows/firefox'); -} -else if (require('./system/xul-app').is('Fennec')) { - module.exports = require('./windows/fennec'); -} -else { - throw new Error([ - 'The windows module currently supports only Firefox & Fennec. In the future', - ' we would like it to support other applications, however.' - ].join('')); -} diff --git a/tools/addon-sdk-1.12/lib/sdk/windows/dom.js b/tools/addon-sdk-1.12/lib/sdk/windows/dom.js deleted file mode 100644 index ac2b86b..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/windows/dom.js +++ /dev/null @@ -1,33 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Trait } = require('../deprecated/traits'); -const { getWindowTitle } = require('../window/utils'); -const { getMode } = require('../private-browsing/utils'); - -module.metadata = { - "stability": "unstable" -}; - -const WindowDom = Trait.compose({ - _window: Trait.required, - get title() { - return getWindowTitle(this._window); - }, - close: function close() { - let window = this._window; - if (window) window.close(); - return this._public; - }, - activate: function activate() { - let window = this._window; - if (window) window.focus(); - return this._public; - }, - get isPrivateBrowsing() { - return getMode(this._window); - } -}); -exports.WindowDom = WindowDom; diff --git a/tools/addon-sdk-1.12/lib/sdk/windows/fennec.js b/tools/addon-sdk-1.12/lib/sdk/windows/fennec.js deleted file mode 100644 index a497ea9..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/windows/fennec.js +++ /dev/null @@ -1,84 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Class } = require('../core/heritage'); -const { BrowserWindow } = require('../window/browser'); -const windowUtils = require('../deprecated/window-utils'); -const { WindowTracker } = windowUtils; -const { isBrowser } = require('../window/utils'); -const { windowNS } = require('../window/namespace'); -const { on, off, once, emit } = require('../event/core'); -const { method } = require('../lang/functional'); -const { EventTarget } = require('../event/target'); -const { List, addListItem } = require('../util/list'); - -const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("tabs") instead'; - -// NOTE: On Fennec there is only one window. - -let BrowserWindows = Class({ - implements: [ List ], - extends: EventTarget, - initialize: function() { - List.prototype.initialize.apply(this); - }, - get activeWindow() { - let window = windowUtils.activeBrowserWindow; - return window ? getBrowserWindow({window: window}) : null; - }, - open: function open(options) { - throw new Error(ERR_FENNEC_MSG); - return null; - } -}); -const browserWindows = exports.browserWindows = BrowserWindows(); - - -/** - * Gets a `BrowserWindow` for the given `chromeWindow` if previously - * registered, `null` otherwise. - */ -function getRegisteredWindow(chromeWindow) { - for each (let window in browserWindows) { - if (chromeWindow === windowNS(window).window) - return window; - } - - return null; -} - -/** - * Gets a `BrowserWindow` for the provided window options obj - * @params {Object} options - * Options that are passed to the the `BrowserWindowTrait` - * @returns {BrowserWindow} - */ -function getBrowserWindow(options) { - let window = null; - - // if we have a BrowserWindow already then use it - if ('window' in options) - window = getRegisteredWindow(options.window); - if (window) - return window; - - // we don't have a BrowserWindow yet, so create one - var window = BrowserWindow(options); - addListItem(browserWindows, window); - return window; -} - -WindowTracker({ - onTrack: function onTrack(chromeWindow) { - if (!isBrowser(chromeWindow)) return; - let window = getBrowserWindow({ window: chromeWindow }); - emit(browserWindows, 'open', window); - }, - onUntrack: function onUntrack(chromeWindow) { - if (!isBrowser(chromeWindow)) return; - let window = getBrowserWindow({ window: chromeWindow }); - emit(browserWindows, 'close', window); - } -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/windows/firefox.js b/tools/addon-sdk-1.12/lib/sdk/windows/firefox.js deleted file mode 100644 index 9a55a08..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/windows/firefox.js +++ /dev/null @@ -1,241 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Cc, Ci, Cr } = require('chrome'), - { Trait } = require('../deprecated/traits'), - { List } = require('../deprecated/list'), - { EventEmitter } = require('../deprecated/events'), - { WindowTabs, WindowTabTracker } = require('./tabs-firefox'), - { WindowDom } = require('./dom'), - { WindowLoader } = require('./loader'), - { isBrowser, getWindowDocShell } = require('../window/utils'), - { Options } = require('../tabs/common'), - apiUtils = require('../deprecated/api-utils'), - unload = require('../system/unload'), - windowUtils = require('../deprecated/window-utils'), - { WindowTrackerTrait } = windowUtils, - { ns } = require('../core/namespace'), - { observer: windowObserver } = require('./observer'), - { isWindowPBEnabled } = require('../private-browsing/utils'); - -/** - * Window trait composes safe wrappers for browser window that are E10S - * compatible. - */ -const BrowserWindowTrait = Trait.compose( - EventEmitter, - WindowDom.resolve({ close: '_close' }), - WindowTabs, - WindowTabTracker, - WindowLoader, - /* WindowSidebars, */ - Trait.compose({ - _emit: Trait.required, - _close: Trait.required, - _load: Trait.required, - /** - * Constructor returns wrapper of the specified chrome window. - * @param {nsIWindow} window - */ - constructor: function BrowserWindow(options) { - // Register this window ASAP, in order to avoid loop that would try - // to create this window instance over and over (see bug 648244) - windows.push(this); - - // make sure we don't have unhandled errors - this.on('error', console.exception.bind(console)); - - if ('onOpen' in options) - this.on('open', options.onOpen); - if ('onClose' in options) - this.on('close', options.onClose); - if ('onActivate' in options) - this.on('activate', options.onActivate); - if ('onDeactivate' in options) - this.on('deactivate', options.onDeactivate); - if ('window' in options) - this._window = options.window; - - if ('tabs' in options) { - this._tabOptions = Array.isArray(options.tabs) ? - options.tabs.map(Options) : - [ Options(options.tabs) ]; - } - else if ('url' in options) { - this._tabOptions = [ Options(options.url) ]; - } - - this._load(); - return this; - }, - destroy: function () this._onUnload(), - _tabOptions: [], - _onLoad: function() { - try { - this._initWindowTabTracker(); - } - catch(e) { - this._emit('error', e); - } - - this._emitOnObject(browserWindows, 'open', this._public); - }, - _onUnload: function() { - if (!this._window) - return; - this._destroyWindowTabTracker(); - this._emitOnObject(browserWindows, 'close', this._public); - this._window = null; - // Removing reference from the windows array. - windows.splice(windows.indexOf(this), 1); - this._removeAllListeners(); - }, - close: function close(callback) { - // maybe we should deprecate this with message ? - if (callback) this.on('close', callback); - return this._close(); - } - }) -); - -/** - * Gets a `BrowserWindowTrait` for the given `chromeWindow` if previously - * registered, `null` otherwise. - */ -function getRegisteredWindow(chromeWindow) { - for each (let window in windows) { - if (chromeWindow === window._window) - return window; - } - - return null; -} - -/** - * Wrapper for `BrowserWindowTrait`. Creates new instance if wrapper for - * window doesn't exists yet. If wrapper already exists then returns it - * instead. - * @params {Object} options - * Options that are passed to the the `BrowserWindowTrait` - * @returns {BrowserWindow} - * @see BrowserWindowTrait - */ -function BrowserWindow(options) { - let window = null; - - if ("window" in options) - window = getRegisteredWindow(options.window); - - return (window || BrowserWindowTrait(options))._public; -} -// to have proper `instanceof` behavior will go away when #596248 is fixed. -BrowserWindow.prototype = BrowserWindowTrait.prototype; -exports.BrowserWindow = BrowserWindow; - -const windows = []; - -const browser = ns(); - -function onWindowActivation (chromeWindow, event) { - if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window. - - let window = getRegisteredWindow(chromeWindow); - - if (window) - window._emit(event.type, window._public); - else - window = BrowserWindowTrait({ window: chromeWindow }); - - browser(browserWindows).internals._emit(event.type, window._public); -} - -windowObserver.on("activate", onWindowActivation); -windowObserver.on("deactivate", onWindowActivation); - -/** - * `BrowserWindows` trait is composed out of `List` trait and it represents - * "live" list of currently open browser windows. Instance mutates itself - * whenever new browser window gets opened / closed. - */ -// Very stupid to resolve all `toStrings` but this will be fixed by #596248 -const browserWindows = Trait.resolve({ toString: null }).compose( - List.resolve({ constructor: '_initList' }), - EventEmitter.resolve({ toString: null }), - WindowTrackerTrait.resolve({ constructor: '_initTracker', toString: null }), - Trait.compose({ - _emit: Trait.required, - _add: Trait.required, - _remove: Trait.required, - - // public API - - /** - * Constructor creates instance of `Windows` that represents live list of open - * windows. - */ - constructor: function BrowserWindows() { - browser(this._public).internals = this; - - this._trackedWindows = []; - this._initList(); - this._initTracker(); - unload.ensure(this, "_destructor"); - }, - _destructor: function _destructor() { - this._removeAllListeners('open'); - this._removeAllListeners('close'); - this._removeAllListeners('activate'); - this._removeAllListeners('deactivate'); - this._clear(); - - delete browser(this._public).internals; - }, - /** - * This property represents currently active window. - * Property is non-enumerable, in order to preserve array like enumeration. - * @type {Window|null} - */ - get activeWindow() { - let window = windowUtils.activeBrowserWindow; - return window ? BrowserWindow({window: window}) : null; - }, - open: function open(options) { - if (typeof options === "string") - // `tabs` option is under review and may be removed. - options = { tabs: [Options(options)] }; - return BrowserWindow(options); - }, - - /** - * Internal listener which is called whenever new window gets open. - * Creates wrapper and adds to this list. - * @param {nsIWindow} chromeWindow - */ - _onTrack: function _onTrack(chromeWindow) { - if (!isBrowser(chromeWindow)) return; - let window = BrowserWindow({ window: chromeWindow }); - this._add(window); - this._emit('open', window); - }, - /** - * Internal listener which is called whenever window gets closed. - * Cleans up references and removes wrapper from this list. - * @param {nsIWindow} window - */ - _onUntrack: function _onUntrack(chromeWindow) { - if (!isBrowser(chromeWindow)) return; - let window = BrowserWindow({ window: chromeWindow }); - this._remove(window); - this._emit('close', window); - - // Bug 724404: do not leak this module and linked windows: - // We have to do it on untrack and not only when `_onUnload` is called - // when windows are closed, otherwise, we will leak on addon disabling. - window.destroy(); - } - }).resolve({ toString: null }) -)(); - -exports.browserWindows = browserWindows; diff --git a/tools/addon-sdk-1.12/lib/sdk/windows/loader.js b/tools/addon-sdk-1.12/lib/sdk/windows/loader.js deleted file mode 100644 index be3d84c..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/windows/loader.js +++ /dev/null @@ -1,120 +0,0 @@ -/* 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'; - -module.metadata = { - "stability": "unstable" -}; - -const { Cc, Ci } = require('chrome'), - { setTimeout } = require('../timers'), - { Trait } = require('../deprecated/traits'), - { openDialog } = require('../window/utils'), - - ON_LOAD = 'load', - ON_UNLOAD = 'unload', - STATE_LOADED = 'complete'; - -/** - * Trait provides private `_window` property and requires `_onLoad` property - * that will be called when `_window` is loaded. If `_window` property value - * is changed with already loaded window `_onLoad` still will be called. - */ -const WindowLoader = Trait.compose({ - /** - * Internal listener that is called when window is loaded. - * Please keep in mind that this trait will not handle exceptions that may - * be thrown by this method so method itself should take care of - * handling them. - * @param {nsIWindow} window - */ - _onLoad: Trait.required, - _tabOptions: Trait.required, - /** - * Internal listener that is called when `_window`'s DOM 'unload' event - * is dispatched. Please note that this trait will not handle exceptions that - * may be thrown by this method so method itself should take care of - * handling them. - */ - _onUnload: Trait.required, - _load: function _load() { - if (this.__window) return; - this._window = openDialog({ - args: this._tabOptions.map(function(options) options.url).join("|") - }); - }, - /** - * Private window who's load event is being tracked. Once window is loaded - * `_onLoad` is called. - * @type {nsIWindow} - */ - get _window() this.__window, - set _window(window) { - let _window = this.__window; - if (!window) window = null; - - if (window !== _window) { - if (_window) { - _window.removeEventListener(ON_UNLOAD, this.__unloadListener, false); - _window.removeEventListener(ON_LOAD, this.__loadListener, false); - } - - if (window) { - window.addEventListener( - ON_UNLOAD, - this.__unloadListener || - (this.__unloadListener = this._unloadListener.bind(this)) - , - false - ); - - this.__window = window; - - // If window is not loaded yet setting up a listener. - if (STATE_LOADED != window.document.readyState) { - window.addEventListener( - ON_LOAD, - this.__loadListener || - (this.__loadListener = this._loadListener.bind(this)) - , - false - ); - } - else { // If window is loaded calling listener next turn of event loop. - this._onLoad(window) - } - } - } - }, - __window: null, - /** - * Internal method used for listening 'load' event on the `_window`. - * Method takes care of removing itself from 'load' event listeners once - * event is being handled. - */ - _loadListener: function _loadListener(event) { - let window = this._window; - if (!event.target || event.target.defaultView != window) return; - window.removeEventListener(ON_LOAD, this.__loadListener, false); - this._onLoad(window); - }, - __loadListener: null, - /** - * Internal method used for listening 'unload' event on the `_window`. - * Method takes care of removing itself from 'unload' event listeners once - * event is being handled. - */ - _unloadListener: function _unloadListener(event) { - let window = this._window; - if (!event.target - || event.target.defaultView != window - || STATE_LOADED != window.document.readyState - ) return; - window.removeEventListener(ON_UNLOAD, this.__unloadListener, false); - this._onUnload(window); - }, - __unloadListener: null -}); -exports.WindowLoader = WindowLoader; - diff --git a/tools/addon-sdk-1.12/lib/sdk/windows/observer.js b/tools/addon-sdk-1.12/lib/sdk/windows/observer.js deleted file mode 100644 index 1097bf3..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/windows/observer.js +++ /dev/null @@ -1,52 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { EventEmitterTrait: EventEmitter } = require("../deprecated/events"); -const { WindowTracker, windowIterator } = require("../deprecated/window-utils"); -const { DOMEventAssembler } = require("../deprecated/events/assembler"); -const { Trait } = require("../deprecated/light-traits"); - -// Event emitter objects used to register listeners and emit events on them -// when they occur. -const observer = Trait.compose(DOMEventAssembler, EventEmitter).create({ - /** - * Method is implemented by `EventEmitter` and is used just for emitting - * events on registered listeners. - */ - _emit: Trait.required, - /** - * Events that are supported and emitted by the module. - */ - supportedEventsTypes: [ "activate", "deactivate" ], - /** - * Function handles all the supported events on all the windows that are - * observed. Method is used to proxy events to the listeners registered on - * this event emitter. - * @param {Event} event - * Keyboard event being emitted. - */ - handleEvent: function handleEvent(event) { - this._emit(event.type, event.target, event); - } -}); - -// Using `WindowTracker` to track window events. -WindowTracker({ - onTrack: function onTrack(chromeWindow) { - observer._emit("open", chromeWindow); - observer.observe(chromeWindow); - }, - onUntrack: function onUntrack(chromeWindow) { - observer._emit("close", chromeWindow); - observer.ignore(chromeWindow); - } -}); - -exports.observer = observer; diff --git a/tools/addon-sdk-1.12/lib/sdk/windows/tabs-fennec.js b/tools/addon-sdk-1.12/lib/sdk/windows/tabs-fennec.js deleted file mode 100644 index ddf4aeb..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/windows/tabs-fennec.js +++ /dev/null @@ -1,190 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Class } = require('../core/heritage'); -const { Tab } = require('../tabs/tab'); -const { browserWindows } = require('./fennec'); -const { windowNS } = require('../window/namespace'); -const { tabsNS, tabNS } = require('../tabs/namespace'); -const { openTab, getTabs, getSelectedTab } = require('../tabs/utils'); -const { Options } = require('../tabs/common'); -const { on, once, off, emit } = require('../event/core'); -const { method } = require('../lang/functional'); -const { EVENTS } = require('../tabs/events'); -const { EventTarget } = require('../event/target'); -const { when: unload } = require('../system/unload'); -const { windowIterator } = require('../deprecated/window-utils'); -const { List, addListItem, removeListItem } = require('../util/list'); - -const mainWindow = windowNS(browserWindows.activeWindow).window; - -const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec'; - -const Tabs = Class({ - implements: [ List ], - extends: EventTarget, - initialize: function initialize(options) { - let tabsInternals = tabsNS(this); - let window = tabsNS(this).window = options.window || mainWindow; - - EventTarget.prototype.initialize.call(this, options); - List.prototype.initialize.apply(this, getTabs(window).map(Tab)); - - // TabOpen event - window.BrowserApp.deck.addEventListener(EVENTS.open.dom, onTabOpen, false); - - // TabSelect - window.BrowserApp.deck.addEventListener(EVENTS.activate.dom, onTabSelect, false); - - // TabClose - window.BrowserApp.deck.addEventListener(EVENTS.close.dom, onTabClose, false); - }, - get activeTab() { - return getTabForRawTab(getSelectedTab(tabsNS(this).window)); - }, - open: function(options) { - options = Options(options); - let activeWin = browserWindows.activeWindow; - - if (options.isPinned) { - console.error(ERR_FENNEC_MSG); // TODO - } - - let rawTab = openTab(windowNS(activeWin).window, options.url, { - inBackground: options.inBackground - }); - - // by now the tab has been created - let tab = getTabForRawTab(rawTab); - - if (options.onClose) - tab.on('close', options.onClose); - - if (options.onOpen) { - // NOTE: on Fennec this will be true - if (tabNS(tab).opened) - options.onOpen(tab); - - tab.on('open', options.onOpen); - } - - if (options.onReady) - tab.on('ready', options.onReady); - - if (options.onActivate) - tab.on('activate', options.onActivate); - - return tab; - } -}); -let gTabs = exports.tabs = Tabs(mainWindow); - -function tabsUnloader(event, window) { - window = window || (event && event.target); - if (!(window && window.BrowserApp)) - return; - window.BrowserApp.deck.removeEventListener(EVENTS.open.dom, onTabOpen, false); - window.BrowserApp.deck.removeEventListener(EVENTS.activate.dom, onTabSelect, false); - window.BrowserApp.deck.removeEventListener(EVENTS.close.dom, onTabClose, false); -} - -// unload handler -unload(function() { - for (let window in windowIterator()) { - tabsUnloader(null, window); - } -}); - -function addTab(tab) { - addListItem(gTabs, tab); - return tab; -} - -function removeTab(tab) { - removeListItem(gTabs, tab); - return tab; -} - -function getTabForBrowser(browser) { - return getTabForRawTab(getRawTabForBrowser(browser)); -} - -function getRawTabForBrowser(browser) { - let tabs = mainWindow.BrowserApp.tabs; - for (let i = 0; i < tabs.length; i++) { - let tab = tabs[i]; - if (tab.browser === browser) - return tab - } - return null; -} - -// TabOpen -function onTabOpen(event) { - let browser = event.target; - - let tab = getTabForBrowser(browser); - if (tab === null) { - let rawTab = getRawTabForBrowser(browser); - - // create a Tab instance for this new tab - tab = addTab(Tab(rawTab)); - } - - tabNS(tab).opened = true; - - // TabReady - let onReady = tabNS(tab).onReady = onTabReady.bind(tab); - browser.addEventListener(EVENTS.ready.dom, onReady, false); - - emit(tab, 'open', tab); - emit(gTabs, 'open', tab); -}; - -function onTabReady() { - emit(this, 'ready', this); - emit(gTabs, 'ready', this); -} - -// TabSelect -function onTabSelect(event) { - // Set value whenever new tab becomes active. - let tab = getTabForBrowser(event.target); - emit(tab, 'activate', tab); - emit(gTabs, 'activate', tab); - - for each (let t in gTabs) { - if (t === tab) continue; - emit(t, 'deactivate', t); - emit(gTabs, 'deactivate', t); - } -}; - -// TabClose -function onTabClose(event) { - let tab = getTabForBrowser(event.target); - removeTab(tab); - - emit(gTabs, 'close', tab); - emit(tab, 'close', tab); -}; - -function getTabForRawTab(rawTab) { - for each (let tab in gTabs) { - if (tabNS(tab).tab === rawTab) - return tab; - } - return null; -} - -unload(function() { - for each (let tab in gTabs) { - let tabInternals = tabNS(tab); - tabInternals.tab.browser.removeEventListener(EVENTS.ready.dom, tabInternals.onReady, false); - tabInternals.onReady = null; - tabInternals.tab = null; - tabInternals.window = null; - } -}); diff --git a/tools/addon-sdk-1.12/lib/sdk/windows/tabs-firefox.js b/tools/addon-sdk-1.12/lib/sdk/windows/tabs-firefox.js deleted file mode 100644 index f41e752..0000000 --- a/tools/addon-sdk-1.12/lib/sdk/windows/tabs-firefox.js +++ /dev/null @@ -1,188 +0,0 @@ -/* 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"; - -module.metadata = { - "stability": "unstable" -}; - -const { Trait } = require("../deprecated/traits"); -const { List } = require("../deprecated/list"); -const { Tab } = require("../tabs/tab"); -const { EventEmitter } = require("../deprecated/events"); -const { EVENTS } = require("../tabs/events"); -const { getOwnerWindow, getActiveTab, getTabs, - openTab, activateTab } = require("../tabs/utils"); -const { Options } = require("../tabs/common"); -const { observer: tabsObserver } = require("../tabs/observer"); - -const TAB_BROWSER = "tabbrowser"; - -/** - * This is a trait that is used in composition of window wrapper. Trait tracks - * tab related events of the wrapped window in order to keep track of open - * tabs and maintain their wrappers. Every new tab gets wrapped and jetpack - * type event is emitted. - */ -const WindowTabTracker = Trait.compose({ - /** - * Chrome window whose tabs are tracked. - */ - _window: Trait.required, - /** - * Function used to emit events. - */ - _emit: EventEmitter.required, - _tabOptions: Trait.required, - /** - * Function to add event listeners. - */ - on: EventEmitter.required, - removeListener: EventEmitter.required, - /** - * Initializes tab tracker for a browser window. - */ - _initWindowTabTracker: function _initWindowTabTracker() { - // Ugly hack that we have to remove at some point (see Bug 658059). At this - // point it is necessary to invoke lazy `tabs` getter on the windows object - // which creates a `TabList` instance. - this.tabs; - - // Binding all methods used as event listeners to the instance. - this._onTabReady = this._emitEvent.bind(this, "ready"); - this._onTabOpen = this._onTabEvent.bind(this, "open"); - this._onTabClose = this._onTabEvent.bind(this, "close"); - this._onTabActivate = this._onTabEvent.bind(this, "activate"); - this._onTabDeactivate = this._onTabEvent.bind(this, "deactivate"); - this._onTabPinned = this._onTabEvent.bind(this, "pinned"); - this._onTabUnpinned = this._onTabEvent.bind(this, "unpinned"); - - for each (let tab in getTabs(this._window)) { - // We emulate "open" events for all open tabs since gecko does not emits - // them on the tabs that new windows are open with. Also this is - // necessary to synchronize tabs lists with an actual state. - this._onTabOpen(tab); - } - - // We also emulate "activate" event so that it's picked up by a tab list. - this._onTabActivate(getActiveTab(this._window)); - - // Setting up event listeners - tabsObserver.on("open", this._onTabOpen); - tabsObserver.on("close", this._onTabClose); - tabsObserver.on("activate", this._onTabActivate); - tabsObserver.on("deactivate", this._onTabDeactivate); - tabsObserver.on("pinned", this._onTabPinned); - tabsObserver.on("unpinned", this._onTabUnpinned); - }, - _destroyWindowTabTracker: function _destroyWindowTabTracker() { - // We emulate close events on all tabs, since gecko does not emits such - // events by itself. - for each (let tab in this.tabs) - this._emitEvent("close", tab); - - this._tabs._clear(); - - tabsObserver.removeListener("open", this._onTabOpen); - tabsObserver.removeListener("close", this._onTabClose); - tabsObserver.removeListener("activate", this._onTabActivate); - tabsObserver.removeListener("deactivate", this._onTabDeactivate); - }, - _onTabEvent: function _onTabEvent(type, tab) { - if (this._window === getOwnerWindow(tab)) { - let options = this._tabOptions.shift() || {}; - options.tab = tab; - options.window = this._public; - - // creating tab wrapper and adding listener to "ready" events. - let wrappedTab = Tab(options); - - // Setting up an event listener for ready events. - if (type === "open") - wrappedTab.on("ready", this._onTabReady); - - this._emitEvent(type, wrappedTab); - } - }, - _emitEvent: function _emitEvent(type, tab) { - // Notifies combined tab list that tab was added / removed. - tabs._emit(type, tab); - // Notifies contained tab list that window was added / removed. - this._tabs._emit(type, tab); - } -}); -exports.WindowTabTracker = WindowTabTracker; - -/** - * This trait is used to create live representation of open tab lists. Each - * window wrapper's tab list is represented by an object created from this - * trait. It is also used to represent list of all the open windows. Trait is - * composed out of `EventEmitter` in order to emit 'TabOpen', 'TabClose' events. - * **Please note** that objects created by this trait can't be exposed outside - * instead you should expose it's `_public` property, see comments in - * constructor for details. - */ -const TabList = List.resolve({ constructor: "_init" }).compose( - // This is ugly, but necessary. Will be removed by #596248 - EventEmitter.resolve({ toString: null }), - Trait.compose({ - on: Trait.required, - _emit: Trait.required, - constructor: function TabList(options) { - this._window = options.window; - // Add new items to the list - this.on(EVENTS.open.name, this._add.bind(this)); - - // Remove closed items from the list - this.on(EVENTS.close.name, this._remove.bind(this)); - - // Set value whenever new tab becomes active. - this.on("activate", function onTabActivate(tab) { - this._activeTab = tab; - }.bind(this)); - - // Initialize list. - this._init(); - // This list is not going to emit any events, object holding this list - // will do it instead, to make that possible we return a private API. - return this; - }, - get activeTab() this._activeTab, - _activeTab: null, - - open: function open(options) { - options = Options(options); - this._window._tabOptions.push(options); - let tab = openTab(this._window._window, options.url); - if (!options.inBackground) - activateTab(tab); - } - // This is ugly, but necessary. Will be removed by #596248 - }).resolve({ toString: null }) -); - -/** - * Combined list of all open tabs on all the windows. - * type {TabList} - */ -var tabs = TabList({ window: null }); -exports.tabs = tabs._public; - -/** - * Trait is a part of composition that represents window wrapper. This trait is - * composed out of `WindowTabTracker` that allows it to keep track of open tabs - * on the window being wrapped. - */ -const WindowTabs = Trait.compose( - WindowTabTracker, - Trait.compose({ - _window: Trait.required, - /** - * List of tabs - */ - get tabs() (this._tabs || (this._tabs = TabList({ window: this })))._public, - _tabs: null, - }) -); -exports.WindowTabs = WindowTabs; diff --git a/tools/addon-sdk-1.12/lib/toolkit/loader.js b/tools/addon-sdk-1.12/lib/toolkit/loader.js deleted file mode 100644 index 09a9889..0000000 --- a/tools/addon-sdk-1.12/lib/toolkit/loader.js +++ /dev/null @@ -1,400 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 expandtab */ -/* 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(id, factory) { // Module boilerplate :( - if (typeof(define) === 'function') { // RequireJS - define(factory); - } else if (typeof(require) === 'function') { // CommonJS - factory.call(this, require, exports, module); - } else if (~String(this).indexOf('BackstagePass')) { // JSM - factory(function require(uri) { - var imports = {}; - this['Components'].utils.import(uri, imports); - return imports; - }, this, { uri: __URI__, id: id }); - this.EXPORTED_SYMBOLS = Object.keys(this); - } else if (~String(this).indexOf('Sandbox')) { // Sandbox - factory(function require(uri) {}, this, { uri: __URI__, id: id }); - } else { // Browser or alike - var globals = this - factory(function require(id) { - return globals[id]; - }, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id }); - } -}).call(this, 'loader', function(require, exports, module) { - -'use strict'; - -module.metadata = { - "stability": "unstable" -}; - -const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu, - results: Cr, manager: Cm } = Components; -const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')(); -const { loadSubScript } = Cc['@mozilla.org/moz/jssubscript-loader;1']. - getService(Ci.mozIJSSubScriptLoader); -const { notifyObservers } = Cc['@mozilla.org/observer-service;1']. - getService(Ci.nsIObserverService); - -// Define some shortcuts. -const bind = Function.call.bind(Function.bind); -const getOwnPropertyNames = Object.getOwnPropertyNames; -const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; -const define = Object.defineProperties; -const prototypeOf = Object.getPrototypeOf; -const create = Object.create; -const keys = Object.keys; - -// Workaround for bug 674195. Freezing objects from other compartments fail, -// so we use `Object.freeze` from the same component instead. -function freeze(object) { - if (prototypeOf(object) === null) { - Object.freeze(object); - } - else { - prototypeOf(prototypeOf(object.isPrototypeOf)). - constructor. // `Object` from the owner compartment. - freeze(object); - } - return object; -} - -// Returns map of given `object`-s own property descriptors. -const descriptor = iced(function descriptor(object) { - let value = {}; - getOwnPropertyNames(object).forEach(function(name) { - value[name] = getOwnPropertyDescriptor(object, name) - }); - return value; -}); -exports.descriptor = descriptor; - -// Freeze important built-ins so they can't be used by untrusted code as a -// message passing channel. -freeze(Object); -freeze(Object.prototype); -freeze(Function); -freeze(Function.prototype); -freeze(Array); -freeze(Array.prototype); -freeze(String); -freeze(String.prototype); - -// This function takes `f` function sets it's `prototype` to undefined and -// freezes it. We need to do this kind of deep freeze with all the exposed -// functions so that untrusted code won't be able to use them a message -// passing channel. -function iced(f) { - f.prototype = undefined; - return freeze(f); -} - -// Defines own properties of given `properties` object on the given -// target object overriding any existing property with a conflicting name. -// Returns `target` object. Note we only export this function because it's -// useful during loader bootstrap when other util modules can't be used & -// thats only case where this export should be used. -const override = iced(function override(target, source) { - let properties = descriptor(target) - let extension = descriptor(source || {}) - getOwnPropertyNames(extension).forEach(function(name) { - properties[name] = extension[name]; - }); - return define({}, properties); -}); -exports.override = override; - -// Function takes set of options and returns a JS sandbox. Function may be -// passed set of options: -// - `name`: A string value which identifies the sandbox in about:memory. Will -// throw exception if omitted. -// - `principal`: String URI or `nsIPrincipal` for the sandbox. Defaults to -// system principal. -// - `prototype`: Ancestor for the sandbox that will be created. Defaults to -// `{}`. -// - `wantXrays`: A Boolean value indicating whether code outside the sandbox -// wants X-ray vision with respect to objects inside the sandbox. Defaults -// to `true`. -// - `sandbox`: A sandbox to share JS compartment with. If omitted new -// compartment will be created. -// For more details see: -// https://developer.mozilla.org/en/Components.utils.Sandbox -const Sandbox = iced(function Sandbox(options) { - // Normalize options and rename to match `Cu.Sandbox` expectations. - options = { - // Do not expose `Components` if you really need them (bad idea!) you - // still can expose via prototype. - wantComponents: false, - sandboxName: options.name, - principal: 'principal' in options ? options.principal : systemPrincipal, - wantXrays: 'wantXrays' in options ? options.wantXrays : true, - sandboxPrototype: 'prototype' in options ? options.prototype : {}, - sameGroupAs: 'sandbox' in options ? options.sandbox : null - }; - - // Make `options.sameGroupAs` only if `sandbox` property is passed, - // otherwise `Cu.Sandbox` will throw. - if (!options.sameGroupAs) - delete options.sameGroupAs; - - let sandbox = Cu.Sandbox(options.principal, options); - - // Each sandbox at creation gets set of own properties that will be shadowing - // ones from it's prototype. We override delete such `sandbox` properties - // to avoid shadowing. - delete sandbox.Iterator; - delete sandbox.Components; - delete sandbox.importFunction; - delete sandbox.debug; - - return sandbox; -}); -exports.Sandbox = Sandbox; - -// Evaluates code from the given `uri` into given `sandbox`. If -// `options.source` is passed, then that code is evaluated instead. -// Optionally following options may be given: -// - `options.encoding`: Source encoding, defaults to 'UTF-8'. -// - `options.line`: Line number to start count from for stack traces. -// Defaults to 1. -// - `options.version`: Version of JS used, defaults to '1.8'. -const evaluate = iced(function evaluate(sandbox, uri, options) { - let { source, line, version, encoding } = override({ - encoding: 'UTF-8', - line: 1, - version: '1.8', - source: null - }, options); - - - return source ? Cu.evalInSandbox(source, sandbox, version, uri, line) - : loadSubScript(uri, sandbox, encoding); -}); -exports.evaluate = evaluate; - -// Populates `exports` of the given CommonJS `module` object, in the context -// of the given `loader` by evaluating code associated with it. -const load = iced(function load(loader, module) { - let { sandboxes, globals } = loader; - let require = Require(loader, module); - - let sandbox = sandboxes[module.uri] = Sandbox({ - name: module.uri, - // Get an existing module sandbox, if any, so we can reuse its compartment - // when creating the new one to reduce memory consumption. - sandbox: sandboxes[keys(sandboxes).shift()], - // We expose set of properties defined by `CommonJS` specification via - // prototype of the sandbox. Also globals are deeper in the prototype - // chain so that each module has access to them as well. - prototype: create(globals, descriptor({ - require: require, - module: module, - exports: module.exports - })), - wantXrays: false - }); - - evaluate(sandbox, module.uri); - - if (module.exports && typeof(module.exports) === 'object') - freeze(module.exports); - - return module; -}); -exports.load = load; - -// Utility function to check if id is relative. -function isRelative(id) { return id[0] === '.'; } -// Utility function to normalize module `uri`s so they have `.js` extension. -function normalize(uri) { return uri.substr(-3) === '.js' ? uri : uri + '.js'; } -// Utility function to join paths. In common case `base` is a -// `requirer.uri` but in some cases it may be `baseURI`. In order to -// avoid complexity we require `baseURI` with a trailing `/`. -const resolve = iced(function resolve(id, base) { - let paths = id.split('/'); - let result = base.split('/'); - result.pop(); - while (paths.length) { - let path = paths.shift(); - if (path === '..') - result.pop(); - else if (path !== '.') - result.push(path); - } - return result.join('/'); -}); -exports.resolve = resolve; - -const resolveURI = iced(function resolveURI(id, mapping) { - let count = mapping.length, index = 0; - while (index < count) { - let [ path, uri ] = mapping[index ++]; - if (id.indexOf(path) === 0) - return normalize(id.replace(path, uri)); - } -}); -exports.resolveURI = resolveURI; - -// Creates version of `require` that will be exposed to the given `module` -// in the context of the given `loader`. Each module gets own limited copy -// of `require` that is allowed to load only a modules that are associated -// with it during link time. -const Require = iced(function Require(loader, requirer) { - let { modules, mapping, resolve } = loader; - - function require(id) { - if (!id) // Throw if `id` is not passed. - throw Error('you must provide a module name when calling require() from ' - + requirer.id, requirer.uri); - - // Resolve `id` to its requirer if it's relative. - let requirement = requirer ? resolve(id, requirer.id) : id; - - - // Resolves `uri` of module using loaders resolve function. - let uri = resolveURI(requirement, mapping); - - if (!uri) // Throw if `uri` can not be resolved. - throw Error('Module: Can not resolve "' + id + '" module required by ' + - requirer.id + ' located at ' + requirer.uri, requirer.uri); - - let module = null; - // If module is already cached by loader then just use it. - if (uri in modules) { - module = modules[uri]; - } - // Otherwise load and cache it. We also freeze module to prevent it from - // further changes at runtime. - else { - module = modules[uri] = Module(requirement, uri); - freeze(load(loader, module)); - } - - return module.exports; - } - // Make `require.main === module` evaluate to true in main module scope. - require.main = loader.main === requirer ? requirer : undefined; - return iced(require); -}); -exports.Require = Require; - -const main = iced(function main(loader, id) { - let uri = resolveURI(id, loader.mapping) - let module = loader.main = loader.modules[uri] = Module(id, uri); - return load(loader, module).exports; -}); -exports.main = main; - -// Makes module object that is made available to CommonJS modules when they -// are evaluated, along with `exports` and `require`. -const Module = iced(function Module(id, uri) { - return create(null, { - id: { enumerable: true, value: id }, - exports: { enumerable: true, writable: true, value: create(null) }, - uri: { value: uri } - }); -}); -exports.Module = Module; - -// Takes `loader`, and unload `reason` string and notifies all observers that -// they should cleanup after them-self. -const unload = iced(function unload(loader, reason) { - // subject is a unique object created per loader instance. - // This allows any code to cleanup on loader unload regardless of how - // it was loaded. To handle unload for specific loader subject may be - // asserted against loader.destructor or require('@loader/unload') - // Note: We don not destroy loader's module cache or sandboxes map as - // some modules may do cleanup in subsequent turns of event loop. Destroying - // cache may cause module identity problems in such cases. - let subject = { wrappedJSObject: loader.destructor }; - notifyObservers(subject, 'sdk:loader:destroy', reason); -}); -exports.unload = unload; - -// Function makes new loader that can be used to load CommonJS modules -// described by a given `options.manifest`. Loader takes following options: -// - `globals`: Optional map of globals, that all module scopes will inherit -// from. Map is also exposed under `globals` property of the returned loader -// so it can be extended further later. Defaults to `{}`. -// - `modules` Optional map of built-in module exports mapped by module id. -// These modules will incorporated into module cache. Each module will be -// frozen. -// - `resolve` Optional module `id` resolution function. If given it will be -// used to resolve module URIs, by calling it with require term, requirer -// module object (that has `uri` property) and `baseURI` of the loader. -// If `resolve` does not returns `uri` string exception will be thrown by -// an associated `require` call. -const Loader = iced(function Loader(options) { - let { modules, globals, resolve, paths } = override({ - paths: {}, - modules: {}, - globals: {}, - resolve: exports.resolve - }, options); - - // We create an identity object that will be dispatched on an unload - // event as subject. This way unload listeners will be able to assert - // which loader is unloaded. Please note that we intentionally don't - // use `loader` as subject to prevent a loader access leakage through - // observer notifications. - let destructor = freeze(create(null)); - - // Make mapping array that is sorted from longest path to shortest path - // to allow overlays. - let mapping = keys(paths). - sort(function(a, b) { return b.length - a.length }). - map(function(path) { return [ path, paths[path] ] }); - - // Define pseudo modules. - modules = override({ - '@loader/unload': destructor, - '@loader/options': options, - 'chrome': { Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm, - CC: bind(CC, Components), components: Components, - // `ChromeWorker` has to be inject in loader global scope. - // It is done by bootstrap.js:loadSandbox for the SDK. - ChromeWorker: ChromeWorker - } - }, modules); - - modules = keys(modules).reduce(function(result, id) { - // We resolve `uri` from `id` since modules are cached by `uri`. - let uri = resolveURI(id, mapping); - let module = Module(id, uri); - module.exports = freeze(modules[id]); - result[uri] = freeze(module); - return result; - }, {}); - - // Loader object is just a representation of a environment - // state. We freeze it and mark make it's properties non-enumerable - // as they are pure implementation detail that no one should rely upon. - return freeze(create(null, { - destructor: { enumerable: false, value: destructor }, - globals: { enumerable: false, value: globals }, - mapping: { enumerable: false, value: mapping }, - // Map of module objects indexed by module URIs. - modules: { enumerable: false, value: modules }, - // Map of module sandboxes indexed by module URIs. - sandboxes: { enumerable: false, value: {} }, - resolve: { enumerable: false, value: resolve }, - // Main (entry point) module, it can be set only once, since loader - // instance can have only one main module. - main: new function() { - let main; - return { - enumerable: false, - get: function() { return main; }, - // Only set main if it has not being set yet! - set: function(module) { main = main || module; } - } - } - })); -}); -exports.Loader = Loader; - -}); - |