diff options
Diffstat (limited to 'tools/addon-sdk-1.12/lib/sdk/deprecated')
16 files changed, 3570 insertions, 0 deletions
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 new file mode 100644 index 0000000..521ad6e --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/api-utils.js @@ -0,0 +1,158 @@ +/* -*- 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 new file mode 100644 index 0000000..e09f79e --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/app-strings.js @@ -0,0 +1,67 @@ +/* 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 new file mode 100644 index 0000000..8eef91c --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/cortex.js @@ -0,0 +1,113 @@ +/* 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 new file mode 100644 index 0000000..b640d05 --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/errors.js @@ -0,0 +1,64 @@ +/* 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 new file mode 100644 index 0000000..0c28a1a --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/events.js @@ -0,0 +1,182 @@ +/* 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 new file mode 100644 index 0000000..fcde1e3 --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/events/assembler.js @@ -0,0 +1,53 @@ +/* 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 new file mode 100644 index 0000000..b18dccb --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/light-traits.js @@ -0,0 +1,600 @@ +/* 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 new file mode 100644 index 0000000..5de69e7 --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/list.js @@ -0,0 +1,118 @@ +/* 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 new file mode 100644 index 0000000..70bcffc --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/memory.js @@ -0,0 +1,118 @@ +/* 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 new file mode 100644 index 0000000..6a25109 --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/observer-service.js @@ -0,0 +1,134 @@ +/* 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 new file mode 100644 index 0000000..068471c --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/tab-browser.js @@ -0,0 +1,731 @@ +/* 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 new file mode 100644 index 0000000..30ae342 --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/traits.js @@ -0,0 +1,187 @@ +/* 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 new file mode 100644 index 0000000..c08c38f --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/traits/core.js @@ -0,0 +1,322 @@ +/* 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 new file mode 100644 index 0000000..880f554 --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/unit-test-finder.js @@ -0,0 +1,71 @@ +/* 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 new file mode 100644 index 0000000..6a8ac1b --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/unit-test.js @@ -0,0 +1,451 @@ +/* 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 new file mode 100644 index 0000000..66fcfba --- /dev/null +++ b/tools/addon-sdk-1.12/lib/sdk/deprecated/window-utils.js @@ -0,0 +1,201 @@ +/* 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(); }); + }); |