1 // Copyright (c) 2009-2013 Kris Maglione <maglione.k@gmail.com>
3 // This work is licensed for reuse under an MIT license. Details are
4 // given in the LICENSE.txt file included with this file.
7 var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
9 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
12 Cu.import("resource://gre/modules/ctypes.jsm");
16 let objproto = Object.prototype;
17 let { __lookupGetter__, __lookupSetter__, __defineGetter__, __defineSetter__,
18 hasOwnProperty, propertyIsEnumerable } = objproto;
20 if (typeof XPCSafeJSObjectWrapper === "undefined")
21 this.XPCSafeJSObjectWrapper = XPCNativeWrapper;
23 let getGlobalForObject = Cu.getGlobalForObject || (obj => obj.__parent__);
25 function require(module, target) JSMLoader.load(module, target);
27 function lazyRequire(module, names, target) {
28 for each (let name in names)
29 memoize(target || this, name, name => require(module)[name]);
32 let jsmodules = { lazyRequire: lazyRequire };
33 jsmodules.jsmodules = jsmodules;
35 function toString() "[module-global " + this.NAME + "]";
41 function defineModule(name, params, module) {
46 module.EXPORTED_SYMBOLS = params.exports || [];
47 if (!~module.EXPORTED_SYMBOLS.indexOf("File"))
50 defineModule.loadLog.push("[Begin " + name + "]");
51 defineModule.prefix += " ";
53 for (let [, mod] in Iterator(params.require || []))
56 module._lastModule = currentModule;
57 currentModule = module;
58 module.startTime = Date.now();
61 defineModule.loadLog = [];
62 Object.defineProperty(defineModule.loadLog, "push", {
63 value: function (val) {
64 val = defineModule.prefix + val;
66 defineModule.dump(val + "\n");
67 this[this.length] = Date.now() + " " + val;
70 defineModule.prefix = "";
71 defineModule.dump = function dump_(...args) {
72 let msg = args.map(function (msg) {
73 if (loaded.util && typeof msg == "object")
74 msg = util.objectToString(msg);
77 dump(String.replace(msg, /\n?$/, "\n")
78 .replace(/^./gm, JSMLoader.name + ": $&"));
80 defineModule.modules = [];
81 defineModule.time = function time(major, minor, func, self, ...args) {
82 let time = Date.now();
83 if (typeof func !== "function")
87 var res = func.apply(self, args);
90 loaded.util && util.reportError(e);
93 JSMLoader.times.add(major, minor, Date.now() - time);
97 function endModule() {
98 defineModule.prefix = defineModule.prefix.slice(0, -2);
99 defineModule.loadLog.push("(End " + currentModule.NAME + ")");
101 loaded[currentModule.NAME] = 1;
102 require(currentModule.NAME, jsmodules);
103 currentModule = currentModule._lastModule;
106 function require_(obj, name, from, targetName) {
108 if (arguments.length === 1)
109 [obj, name] = [{}, obj];
111 let caller = Components.stack.caller;
114 defineModule.loadLog.push((from || "require") + ": loading " + name +
115 " into " + (targetName || obj.NAME || caller.filename + ":" + caller.lineNumber));
117 JSMLoader.load(name + ".jsm", obj);
119 if (!loaded[name] && obj != jsmodules)
120 JSMLoader.load(name + ".jsm", jsmodules);
125 defineModule.dump("loading " + String.quote(name + ".jsm") + "\n");
129 defineModule.dump(" " + (e.filename || e.fileName) + ":" + e.lineNumber + ": " + e + "\n");
133 defineModule("base", {
134 // sed -n 's/^(const|var|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt
136 "ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Finished", "Module", "JSMLoader",
137 "Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMShim", "XPCOMUtils",
138 "XPCSafeJSObjectWrapper", "array", "bind", "call", "callable", "ctypes", "curry",
139 "debuggerProperties", "defineModule", "deprecated", "endModule", "forEach", "isArray",
140 "isGenerator", "isinstance", "isObject", "isString", "isSubclass", "isXML", "iter",
141 "iterAll", "iterOwnProperties", "keys", "literal", "memoize", "octal", "properties",
142 "require", "set", "update", "values", "update_"
146 this.lazyRequire("cache", ["cache"]);
147 this.lazyRequire("config", ["config"]);
148 this.lazyRequire("messages", ["_", "Messages"]);
149 this.lazyRequire("services", ["services"]);
150 this.lazyRequire("storage", ["File"]);
151 this.lazyRequire("util", ["FailedAssertion", "util"]);
153 function literal(/* comment */) {
154 let { caller } = Components.stack;
155 while (caller && caller.language != 2)
156 caller = caller.caller;
158 let file = caller.filename.replace(/.* -> /, "");
159 let key = "literal:" + file + ":" + caller.line;
161 let source = File.readURL(file);
163 let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" +
164 ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source);
168 return cache.get(key, function () {
169 let source = cache.get("literal:" + file,
170 () => util.httpGet(file).responseText);
172 let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" +
173 ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source);
179 * Returns a list of all of the top-level properties of an object, by
180 * way of the debugger.
182 * @param {object} obj
183 * @returns [jsdIProperty]
185 function debuggerProperties(obj) {
186 if (loaded.services && services.debugger.isOn) {
188 services.debugger.wrapValue(obj).getProperties(res, {});
194 * Iterates over the names of all of the top-level properties of an
195 * object or, if prototypes is given, all of the properties in the
196 * prototype chain below the top. Uses the debugger if possible.
198 * @param {object} obj The object to inspect.
199 * @param {boolean} properties Whether to inspect the prototype chain
201 * @returns {Generator}
203 function prototype(obj)
204 obj.__proto__ || Object.getPrototypeOf(obj) ||
205 XPCNativeWrapper.unwrap(obj).__proto__ ||
206 Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj));
208 function properties(obj, prototypes, debugger_) {
210 let seen = { dactylPropertyNames: true };
213 if ("dactylPropertyNames" in obj && !prototypes)
214 for (let key in values(obj.dactylPropertyNames))
215 if (key in obj && !Set.add(seen, key))
220 function props(obj) {
223 return Object.getOwnPropertyNames(obj);
226 if (e.result === Cr.NS_ERROR_FAILURE) {
227 // This is being thrown for PDF.js content windows,
229 let filter = function filter(prop) {
234 util.reportError("Filtering properties for " +
235 String.quote(obj) + ", " +
236 "error checking presence of " +
237 String.quote(prop) + ": " + e);
241 return array.uniq([k for (k in obj)].concat(
242 Object.getOwnPropertyNames(
243 XPCNativeWrapper.unwrap(obj))
252 for (; obj; obj = prototypes && prototype(obj)) {
254 if (!debugger_ || !services.debugger.isOn)
255 var iter = (v for each (v in props(obj)));
259 iter = (prop.name.stringValue for (prop in values(debuggerProperties(obj))));
261 for (let key in iter)
262 if (!prototypes || !Set.add(seen, key) && obj != orig)
267 function iterOwnProperties(obj) {
268 for (let prop in properties(obj))
269 yield [prop, Object.getOwnPropertyDescriptor(obj, prop)];
272 function deprecated(alternative, fn) {
274 return Class.Property(iter(fn).map(([k, v]) => [k, callable(v) ? deprecated(alternative, v) : v])
277 let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments);
279 function deprecatedMethod() {
280 let obj = !this ? "" :
281 this.className ? this.className + "#" :
282 this.constructor.className ? this.constructor.className + "#" :
285 deprecated.warn(func, obj + (fn.name || name), alternative);
286 return func.apply(this, arguments);
289 return callable(fn) ? deprecatedMethod : Class.Property({
290 get: function () deprecatedMethod,
291 init: function (prop) { name = prop; }
294 deprecated.warn = function warn(func, name, alternative, frame) {
295 if (!func.seenCaller)
296 func.seenCaller = Set([
297 "resource://dactyl/javascript.jsm",
298 "resource://dactyl/util.jsm"
301 frame = frame || Components.stack.caller.caller;
302 let filename = util.fixURI(frame.filename || "unknown");
303 if (!Set.add(func.seenCaller, filename))
304 util.dactyl(func).warn([util.urlPath(filename), frame.lineNumber, " "].join(":")
305 + _("warn.deprecated", name, alternative));
309 * Iterates over all of the top-level, iterable property names of an
312 * @param {object} obj The object to inspect.
313 * @returns {Generator}
315 function keys(obj) iter(function keys() {
317 if (hasOwnProperty.call(obj, k))
322 * Iterates over all of the top-level, iterable property values of an
325 * @param {object} obj The object to inspect.
326 * @returns {Generator}
328 function values(obj) iter(function values() {
329 if (isinstance(obj, ["Generator", "Iterator", Iter]))
334 if (hasOwnProperty.call(obj, k))
338 var forEach = deprecated("iter.forEach", function forEach() iter.forEach.apply(iter, arguments));
339 var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments));
342 * Utility for managing sets of strings. Given an array, returns an
343 * object with one key for each value thereof.
345 * @param {[string]} ary @optional
351 for (let val in values(ary))
356 * Adds an element to a set and returns true if the element was
357 * previously contained.
359 * @param {object} set The set.
360 * @param {string} key The key to add.
363 Set.add = curry(function set_add(set, key) {
364 let res = this.has(set, key);
369 * Returns true if the given set contains the given key.
371 * @param {object} set The set.
372 * @param {string} key The key to check.
375 Set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) &&
376 propertyIsEnumerable.call(set, key));
378 * Returns a new set containing the members of the first argument which
379 * do not exist in any of the other given arguments.
381 * @param {object} set The set.
384 Set.subtract = function set_subtract(set) {
385 set = update({}, set);
386 for (let i = 1; i < arguments.length; i++)
387 for (let k in keys(arguments[i]))
392 * Removes an element from a set and returns true if the element was
393 * previously contained.
395 * @param {object} set The set.
396 * @param {string} key The key to remove.
399 Set.remove = curry(function set_remove(set, key) {
400 let res = set.has(set, key);
406 deprecated.warn(set, "set", "Set");
407 return Set.apply(this, arguments);
409 Object.keys(Set).forEach(function (meth) {
410 set[meth] = function proxy() {
411 deprecated.warn(proxy, "set." + meth, "Set." + meth);
412 return Set[meth].apply(Set, arguments);
417 * Curries a function to the given number of arguments. Each
418 * call of the resulting function returns a new function. When
419 * a call does not contain enough arguments to satisfy the
420 * required number, the resulting function is another curried
421 * function with previous arguments accumulated.
423 * function foo(a, b, c) [a, b, c].join(" ");
424 * curry(foo)(1, 2, 3) -> "1 2 3";
425 * curry(foo)(4)(5, 6) -> "4 5 6";
426 * curry(foo)(7)(8)(9) -> "7 8 9";
428 * @param {function} fn The function to curry.
429 * @param {integer} length The number of arguments expected.
432 * @param {object} self The 'this' value for the returned function. When
433 * omitted, the value of 'this' from the first call to the function is
437 function curry(fn, length, self, acc) {
443 // Close over function with 'this'
444 function close(self, fn) (...args) => fn.apply(self, args);
449 return function curried(...args) {
450 // The curried result should preserve 'this'
451 if (args.length == 0)
452 return close(self || this, curried);
454 let args = acc.concat(args);
456 if (args.length >= length)
457 return fn.apply(self || this, args);
459 return curry(fn, length, self || this, args);
464 var bind = function bind(meth, self, ...args) let (func = callable(meth) ? meth : self[meth])
465 func.bind.apply(func, [self].concat(args));
467 var bind = function bind(func, self, ...args) {
471 return function bound(...args2) func.apply(self, args.concat(args2));
475 * Returns true if both arguments are functions and
476 * (targ() instanceof src) would also return true.
478 * @param {function} targ
479 * @param {function} src
482 function isSubclass(targ, src) {
483 return src === targ ||
484 targ && typeof targ === "function" && targ.prototype instanceof src;
488 * Returns true if *object* is an instance of *interfaces*. If *interfaces* is
489 * an array, returns true if *object* is an instance of any element of
490 * *interfaces*. If *interfaces* is the object form of a primitive type,
491 * returns true if *object* is a non-boxed version of the type, i.e., if
492 * (typeof object == "string"), isinstance(object, String) is true. Finally, if
493 * *interfaces* is a string, returns true if ({}.toString.call(object) ==
494 * "[object <interfaces>]").
496 * @param {object} object The object to check.
497 * @param {constructor|[constructor|string]} interfaces The types to check *object* against.
500 var isinstance_types = {
506 function isinstance(object, interfaces) {
510 return Array.concat(interfaces).some(function isinstance_some(iface) {
511 if (typeof iface === "string") {
512 if (objproto.toString.call(object) === "[object " + iface + "]")
515 else if (typeof object === "object" && "isinstance" in object && object.isinstance !== isinstance) {
516 if (object.isinstance(iface))
520 if (object instanceof iface)
522 var type = isinstance_types[typeof object];
523 if (type && isSubclass(iface, type))
531 * Returns true if obj is a non-null object.
533 function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
536 * Returns true if obje is an E4X XML object.
539 function isXML(obj) typeof obj === "xml";
542 * Returns true if and only if its sole argument is an
543 * instance of the builtin Array type. The array may come from
544 * any window, frame, namespace, or execution context, which
545 * is not the case when using (obj instanceof Array).
549 // This is bloody stupid.
550 ? function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array"
551 : function isArray(val) objproto.toString.call(val) == "[object Array]";
554 * Returns true if and only if its sole argument is an
555 * instance of the builtin Generator type. This includes
556 * functions containing the 'yield' statement and generator
557 * statements such as (x for (x in obj)).
559 function isGenerator(val) objproto.toString.call(val) == "[object Generator]";
562 * Returns true if and only if its sole argument is a String,
563 * as defined by the builtin type. May be constructed via
564 * String(foo) or new String(foo) from any window, frame,
565 * namespace, or execution context, which is not the case when
566 * using (obj instanceof String) or (typeof obj == "string").
568 function isString(val) objproto.toString.call(val) == "[object String]";
571 * Returns true if and only if its sole argument may be called
572 * as a function. This includes classes and function objects.
574 function callable(val) typeof val === "function" && !(val instanceof Ci.nsIDOMElement);
576 function call(fn, self, ...args) {
577 fn.apply(self, args);
582 * Memoizes an object property value.
584 * @param {object} obj The object to add the property to.
585 * @param {string} key The property name.
586 * @param {function} getter The function which will return the initial
587 * value of the property.
589 function memoize(obj, key, getter) {
590 if (arguments.length == 1) {
591 let res = update(Object.create(obj), obj);
592 for each (let prop in Object.getOwnPropertyNames(obj)) {
593 let get = __lookupGetter__.call(obj, prop);
595 memoize(res, prop, get);
601 Object.defineProperty(obj, key, {
605 get: function g_replaceProperty() {
607 Class.replaceProperty(this.instance || this, key, null);
608 return Class.replaceProperty(this.instance || this, key, getter.call(this, key));
615 set: function s_replaceProperty(val)
616 Class.replaceProperty(this.instance || this, key, val)
620 obj[key] = getter.call(obj, key);
624 let sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance());
625 sandbox.__proto__ = this;
628 * Updates an object with the properties of another object. Getters
629 * and setters are copied as expected. Moreover, any function
630 * properties receive new 'supercall' and 'superapply' properties,
631 * which will call the identically named function in target's
634 * let a = { foo: function (arg) "bar " + arg }
635 * let b = { __proto__: a }
636 * update(b, { foo: function foo() foo.supercall(this, "baz") });
638 * a.foo("foo") -> "bar foo"
639 * b.foo() -> "bar baz"
641 * @param {Object} target The object to update.
642 * @param {Object} src The source object from which to update target.
643 * May be provided multiple times.
644 * @returns {Object} Returns its updated first argument.
646 function update(target) {
647 for (let i = 1; i < arguments.length; i++) {
648 let src = arguments[i];
649 Object.getOwnPropertyNames(src || {}).forEach(function (k) {
650 let desc = Object.getOwnPropertyDescriptor(src, k);
651 if (desc.value instanceof Class.Property)
652 desc = desc.value.init(k, target) || desc.value;
655 if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) {
656 let func = desc.value.wrapped || desc.value;
657 if (!func.superapply) {
658 func.__defineGetter__("super", function get_super() Object.getPrototypeOf(target)[k]);
659 func.superapply = function superapply(self, args)
660 let (meth = Object.getPrototypeOf(target)[k])
661 meth && meth.apply(self, args);
662 func.supercall = function supercall(self, ...args)
663 func.superapply(self, args);
666 Object.defineProperty(target, k, desc);
673 function update_(target) {
674 for (let i = 1; i < arguments.length; i++) {
675 let src = arguments[i];
676 Object.getOwnPropertyNames(src || {}).forEach(function (k) {
677 let desc = Object.getOwnPropertyDescriptor(src, k);
678 if (desc.value instanceof Class.Property)
679 desc = desc.value.init(k, target) || desc.value;
682 if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) {
683 let func = desc.value.wrapped || desc.value;
684 if (!func.superapply) {
685 func.__defineGetter__("super", function get_super_() Object.getPrototypeOf(target)[k]);
686 func.superapply = function super_apply(self, args)
687 let (meth = Object.getPrototypeOf(target)[k])
688 meth && meth.apply(self, args);
689 func.supercall = function super_call(self, ...args)
690 func.superapply(self, args);
693 Object.defineProperty(target, k, desc);
704 * Constructs a new Class. Arguments marked as optional must be
705 * either entirely elided, or they must have the exact type
708 * @param {string} name The class's as it will appear when toString
709 * is called, as well as in stack traces.
711 * @param {function} base The base class for this module. May be any
715 * @param {Object} prototype The prototype for instances of this
716 * object. The object itself is copied and not used as a prototype
718 * @param {Object} classProperties The class properties for the new
719 * module constructor. More than one may be provided.
722 * @returns {function} The constructor for the resulting class.
724 function Class(...args) {
726 if (isString(args[0]))
727 var name = args.shift();
728 var superclass = Class;
729 if (callable(args[0]))
730 superclass = args.shift();
732 if (loaded.config && (config.haveGecko("5.*", "6.0") || config.haveGecko("6.*"))) // Bug 657418.
733 var Constructor = function Constructor() {
734 var self = Object.create(Constructor.prototype);
735 self.instance = self;
736 self.globalInstance = self;
738 if ("_metaInit_" in self && self._metaInit_)
739 self._metaInit_.apply(self, arguments);
741 var res = self.init.apply(self, arguments);
742 return res !== undefined ? res : self;
745 var Constructor = eval(String.replace('\n\
746 (function constructor(PARAMS) { \n\
747 var self = Object.create(Constructor.prototype); \n\
748 self.instance = self; \n\
749 self.globalInstance = self; \n\
751 if ("_metaInit_" in self && self._metaInit_) \n\
752 self._metaInit_.apply(self, arguments); \n\
754 var res = self.init.apply(self, arguments); \n\
755 return res !== undefined ? res : self; \n\
757 "constructor", (name || superclass.className).replace(/\W/g, "_"))
758 .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1]
759 .replace(/\b(self|res|Constructor)\b/g, "$1_")));
761 Constructor.className = name || superclass.className || superclass.name;
763 if ("init" in superclass.prototype)
764 Constructor.__proto__ = superclass;
766 let superc = superclass;
767 superclass = function Shim() {};
768 Class.extend(superclass, superc, {
771 superclass.__proto__ = superc;
774 Class.extend(Constructor, superclass, args[0]);
775 memoize(Constructor, "closure", Class.makeClosure);
776 update(Constructor, args[1]);
778 Constructor.__proto__ = superclass;
780 args.slice(2).forEach(function (obj) {
783 update(Constructor.prototype, obj);
788 if (Cu.getGlobalForObject)
789 Class.objectGlobal = function (object) {
791 return Cu.getGlobalForObject(object);
798 Class.objectGlobal = function (object) {
799 while (object.__parent__)
800 object = object.__parent__;
805 * @class Class.Property
806 * A class which, when assigned to a property in a Class's prototype
807 * or class property object, defines that property's descriptor
808 * rather than its value. If the desc object has an init property, it
809 * will be called with the property's name before the descriptor is
812 * @param {Object} desc The property descriptor.
814 Class.Property = function Property(desc) update(
815 Object.create(Property.prototype), desc || { configurable: true, writable: true });
816 Class.Property.prototype.init = function () {};
818 * Extends a subclass with a superclass. The subclass's
819 * prototype is replaced with a new object, which inherits
820 * from the superclass's prototype, {@see update}d with the
821 * members of *overrides*.
823 * @param {function} subclass
824 * @param {function} superclass
825 * @param {Object} overrides @optional
827 Class.extend = function extend(subclass, superclass, overrides) {
828 subclass.superclass = superclass;
830 subclass.prototype = Object.create(superclass.prototype);
831 update(subclass.prototype, overrides);
832 subclass.prototype.constructor = subclass;
833 subclass.prototype._class_ = subclass;
835 if (superclass.prototype.constructor === objproto.constructor)
836 superclass.prototype.constructor = superclass;
840 * Memoizes the value of a class property to the value returned by
841 * the passed function the first time the property is accessed.
843 * @param {function(string)} getter The function which returns the
845 * @returns {Class.Property}
847 Class.Memoize = function Memoize(getter, wait)
851 init: function (key) {
855 // Crazy, yeah, I know. -- Kris
856 this.get = function replace() {
857 let obj = this.instance || this;
858 Object.defineProperty(obj, key, {
859 configurable: true, enumerable: false,
860 get: function get() {
861 util.waitFor(() => done);
866 util.yieldable(function () {
868 for (var res in getter.call(obj)) {
869 if (wait !== undefined)
873 Class.replaceProperty(obj, key, res);
880 this.get = function g_Memoize() {
881 let obj = this.instance || this;
883 Class.replaceProperty(obj, key, null);
884 return Class.replaceProperty(obj, key, getter.call(this, key));
891 this.set = function s_Memoize(val) Class.replaceProperty(this.instance || this, key, val);
895 Class.memoize = deprecated("Class.Memoize", function memoize() Class.Memoize.apply(this, arguments));
898 * Updates the given object with the object in the target class's
901 Class.Update = function Update(obj)
906 init: function (key, target) {
907 this.value = update({}, target[key], obj);
911 Class.replaceProperty = function replaceProperty(obj, prop, value) {
912 Object.defineProperty(obj, prop, { configurable: true, enumerable: true, value: value, writable: true });
915 Class.toString = function toString() "[class " + this.className + "]";
918 * Initializes new instances of this class. Called automatically
919 * when new instances are created.
921 init: function c_init() {},
924 set instance(val) Class.replaceProperty(this, "instance", val),
926 withSavedValues: function withSavedValues(names, callback, self) {
927 let vals = names.map(name => this[name]);
929 return callback.call(self || this);
932 names.forEach((name, i) => { this[name] = vals[i]; });
936 toString: function C_toString() {
937 if (this.toStringParams)
938 var params = "(" + this.toStringParams.map(m => (isArray(m) ? "[" + m + "]" :
939 isString(m) ? m.quote() : String(m)))
941 return "[instance " + this.constructor.className + (params || "") + "]";
945 * Executes *callback* after *timeout* milliseconds. The value of
946 * 'this' is preserved in the invocation of *callback*.
948 * @param {function} callback The function to call after *timeout*
949 * @param {number} timeout The time, in milliseconds, to wait
950 * before calling *callback*.
951 * @returns {nsITimer} The timer which backs this timeout.
953 timeout: function timeout(callback, timeout) {
954 let timeout_notify = (timer) => {
956 util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"]))
958 this.timeouts.splice(this.timeouts.indexOf(timer), 1);
959 util.trapErrors(callback, this);
961 let timer = services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
962 this.timeouts.push(timer);
968 * Updates this instance with the properties of the given objects.
969 * Like the update function, but with special semantics for
970 * localized properties.
972 update: function update() {
975 for (let i = 0; i < arguments.length; i++) {
976 let src = arguments[i];
977 Object.getOwnPropertyNames(src || {}).forEach((k) => {
978 let desc = Object.getOwnPropertyDescriptor(src, k);
979 if (desc.value instanceof Class.Property)
980 desc = desc.value.init(k, this) || desc.value;
982 if (typeof desc.value === "function") {
983 let func = desc.value.wrapped || desc.value;
984 if (!func.superapply) {
985 func.__defineGetter__("super", () => Object.getPrototypeOf(this)[k]);
987 func.superapply = function superapply(self, args) {
988 let meth = Object.getPrototypeOf(self)[k];
989 return meth && meth.apply(self, args);
992 func.supercall = function supercall(self, ...args) {
993 return func.superapply(self, args);
999 if ("value" in desc && (k in this.localizedProperties || k in this.magicalProperties))
1000 this[k] = desc.value;
1002 Object.defineProperty(this, k, desc);
1010 localizedProperties: {},
1011 magicalProperties: {}
1013 for (let name in properties(Class.prototype)) {
1014 let desc = Object.getOwnPropertyDescriptor(Class.prototype, name);
1015 desc.enumerable = false;
1016 Object.defineProperty(Class.prototype, name, desc);
1019 Class.makeClosure = function makeClosure() {
1021 function closure(fn) {
1022 function _closure() {
1024 return fn.apply(self, arguments);
1026 catch (e if !(e instanceof FailedAssertion)) {
1027 util.reportError(e);
1028 throw e.stack ? e : Error(e);
1031 _closure.wrapped = fn;
1035 iter(properties(this), properties(this, true)).forEach(function (k) {
1036 if (!__lookupGetter__.call(this, k) && callable(this[k]))
1037 closure[k] = closure(this[k]);
1038 else if (!(k in closure))
1039 Object.defineProperty(closure, k, {
1042 get: function get_proxy() self[k],
1043 set: function set_proxy(val) self[k] = val,
1049 memoize(Class.prototype, "closure", Class.makeClosure);
1052 * A base class generator for classes which implement XPCOM interfaces.
1054 * @param {nsIIID|[nsIJSIID]} interfaces The interfaces which the class
1056 * @param {Class} superClass A super class. @optional
1059 function XPCOM(interfaces, superClass) {
1060 interfaces = Array.concat(interfaces);
1062 let shim = XPCOMShim(interfaces);
1064 let res = Class("XPCOM(" + interfaces + ")", superClass || Class,
1066 v === undefined || callable(v) ? stub : v]
1067 for ([k, v] in Iterator(shim))).toObject(),
1068 { QueryInterface: XPCOMUtils.generateQI(interfaces) }));
1072 function XPCOMShim(interfaces) {
1073 let ip = services.InterfacePointer({
1074 QueryInterface: function (iid) {
1075 if (iid.equals(Ci.nsISecurityCheckedComponent))
1076 throw Cr.NS_ERROR_NO_INTERFACE;
1079 getHelperForLanguage: function () null,
1080 getInterfaces: function (count) { count.value = 0; }
1082 return (interfaces || []).reduce((shim, iface) => shim.QueryInterface(Ci[iface]),
1085 let stub = Class.Property({
1088 value: function stub() null,
1093 * An abstract base class for classes that wish to inherit from Error.
1095 var ErrorBase = Class("ErrorBase", Error, {
1097 init: function EB_init(message, level = 0) {
1098 let error = Error(message);
1099 update(this, error);
1100 this.stack = error.stack;
1101 this.message = message;
1103 let frame = Components.stack;
1104 for (let i = 0; i < this.level + level; i++) {
1105 frame = frame.caller;
1106 this.stack = this.stack.replace(/^.*\n/, "");
1108 this.fileName = frame.filename;
1109 this.lineNumber = frame.lineNumber;
1111 toString: function () String(this.message)
1115 * An Error subclass to throw in order to stop sourcing a plugin without
1116 * printing a stack trace.
1118 var Finished = Class("Finished", ErrorBase);
1121 * Constructs a new Module class and instantiates an instance into the current
1122 * module global object.
1124 * @param {string} name The name of the instance.
1125 * @param {Object} prototype The instance prototype.
1126 * @param {Object} classProperties Properties to be applied to the class constructor.
1129 function Module(name, prototype, ...args) {
1131 let init = callable(prototype) ? 2 : 1;
1132 let proto = callable(prototype) ? args[0] : prototype;
1134 proto._metaInit_ = function () {
1135 delete module.prototype._metaInit_;
1136 currentModule[name.toLowerCase()] = this;
1139 const module = Class.apply(Class, [name, prototype, ...args.slice(0, init)]);
1140 let instance = module();
1141 module.className = name.toLowerCase();
1143 instance.INIT = update(Object.create(Module.INIT),
1146 currentModule[module.className] = instance;
1147 defineModule.modules.push(instance);
1151 if (typeof e === "string")
1154 dump(e.fileName + ":" + e.lineNumber + ": " + e + "\n" + (e.stack || Error().stack));
1158 init: function Module_INIT_init(dactyl, modules, window) {
1159 let args = arguments;
1162 for (let local = this.Local; local; local = local.super)
1165 if (locals.length) {
1166 let module = this, objs = {};
1167 for (let i in locals) {
1168 module = objs[i] = Object.create(module);
1169 module.modules = modules;
1171 module.isLocalModule = true;
1173 modules.jsmodules[this.constructor.className] = module;
1174 locals.reverse().forEach((fn, i) => { update(objs[i], fn.apply(module, args)); });
1176 memoize(module, "closure", Class.makeClosure);
1177 module.instance = module;
1181 modules.dactyl.registerObservers(module);
1189 * Creates a new Struct constructor, used for creating objects with
1190 * a fixed set of named members. Each argument should be the name of
1191 * a member in the resulting objects. These names will correspond to
1192 * the arguments passed to the resultant constructor. Instances of
1193 * the new struct may be treated very much like arrays, and provide
1194 * many of the same methods.
1196 * const Point = Struct("x", "y", "z");
1197 * let p1 = Point(x, y, z);
1199 * @returns {function} The constructor for the new Struct.
1201 function Struct(...args) {
1202 if (/^[A-Z]/.test(args[0]))
1203 var className = args.shift();
1205 const Struct = Class(className || "Struct", StructBase, {
1206 length: args.length,
1207 members: array.toObject(args.map((v, k) => [v, k]))
1209 args.forEach(function (name, i) {
1210 Struct.prototype.__defineGetter__(name, function () this[i]);
1211 Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1215 var StructBase = Class("StructBase", Array, {
1216 init: function struct_init() {
1217 for (let i = 0; i < arguments.length; i++)
1218 if (arguments[i] != undefined)
1219 this[i] = arguments[i];
1222 get toStringParams() this,
1224 clone: function struct_clone() this.constructor.apply(null, this.slice()),
1226 closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1228 get: function struct_get(key, val) this[this.members[key]],
1229 set: function struct_set(key, val) this[this.members[key]] = val,
1231 toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1233 // Iterator over our named members
1234 __iterator__: function struct__iterator__() {
1236 return ([k, self[k]] for (k in keys(self.members)))
1239 fromArray: function fromArray(ary) {
1240 if (!(ary instanceof this))
1241 ary.__proto__ = this.prototype;
1246 * Sets a lazily constructed default value for a member of
1247 * the struct. The value is constructed once, the first time
1248 * it is accessed and memoized thereafter.
1250 * @param {string} key The name of the member for which to
1251 * provide the default value.
1252 * @param {function} val The function which is to generate
1253 * the default value.
1255 defaultValue: function defaultValue(key, val) {
1256 let i = this.prototype.members[key];
1257 this.prototype.__defineGetter__(i, function () (this[i] = val.call(this)));
1258 this.prototype.__defineSetter__(i, function (value)
1259 Class.replaceProperty(this, i, value));
1263 localize: function localize(key, defaultValue) {
1264 let i = this.prototype.members[key];
1265 Object.defineProperty(this.prototype, i, Messages.Localized(defaultValue).init(key, this.prototype));
1270 var Timer = Class("Timer", {
1271 init: function init(minInterval, maxInterval, callback, self = this) {
1272 this._timer = services.Timer();
1273 this.callback = callback;
1275 this.minInterval = minInterval;
1276 this.maxInterval = maxInterval;
1281 notify: function notify(timer, force) {
1283 if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1286 this._timer.cancel();
1288 // minInterval is the time between the completion of the command and the next firing
1289 this.doneAt = Date.now() + this.minInterval;
1291 this.callback.call(this.self, this.arg);
1294 if (typeof util === "undefined")
1295 dump(JSMLoader.name + ": " + e + "\n" + (e.stack || Error().stack));
1297 util.reportError(e);
1300 this.doneAt = Date.now() + this.minInterval;
1304 tell: function tell(arg) {
1305 if (arguments.length > 0)
1308 let now = Date.now();
1309 if (this.doneAt == -1)
1310 this._timer.cancel();
1312 let timeout = this.minInterval;
1313 if (now > this.doneAt && this.doneAt > -1)
1315 else if (this.latest)
1316 timeout = Math.min(timeout, this.latest - now);
1318 this.latest = now + this.maxInterval;
1320 this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1324 reset: function reset() {
1325 this._timer.cancel();
1329 flush: function flush(force) {
1330 if (this.doneAt == -1 || force)
1331 this.notify(null, true);
1336 * Idempotent function which returns the UTF-8 encoded value of an
1337 * improperly-decoded string.
1339 * @param {string} str
1342 function UTF8(str) {
1344 return decodeURIComponent(escape(str));
1351 function octal(decimal) parseInt(decimal, 8);
1354 * Iterates over an arbitrary object. The following iterator types are
1355 * supported, and work as a user would expect:
1357 * • nsIDOMNodeIterator
1358 * • mozIStorageStatement
1360 * Additionally, the following array-like objects yield a tuple of the
1361 * form [index, element] for each contained element:
1363 * • nsIDOMHTMLCollection
1364 * • nsIDOMNodeList
1366 * and the following likewise yield one element of the form
1367 * [name, element] for each contained element:
1369 * • nsIDOMNamedNodeMap
1371 * Duck typing is implemented for any other type. If the object
1372 * contains the "enumerator" property, iter is called on that. If the
1373 * property is a function, it is called first. If it contains the
1374 * property "getNext" along with either "hasMoreItems" or "hasMore", it
1375 * is iterated over appropriately.
1377 * For all other cases, this function behaves exactly like the Iterator
1380 * @param {object} obj
1381 * @param {nsIJSIID} iface The interface to which to query all elements.
1382 * @returns {Generator}
1384 function iter(obj, iface) {
1385 if (arguments.length == 2 && iface instanceof Ci.nsIJSIID)
1386 return iter(obj).map(item => item.QueryInterface(iface));
1388 let args = arguments;
1389 let res = Iterator(obj);
1391 if (args.length > 1)
1392 res = (function () {
1393 for (let i = 0; i < args.length; i++)
1394 for (let j in iter(args[i]))
1397 else if (isinstance(obj, ["Iterator", "Generator"]))
1399 else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1400 while (obj.constructor instanceof ctypes.PointerType)
1402 if (obj.constructor instanceof ctypes.ArrayType)
1403 res = array.iterItems(obj);
1404 else if (obj.constructor instanceof ctypes.StructType)
1405 res = (function () {
1406 for (let prop in values(obj.constructor.fields))
1407 yield let ([name, type] = Iterator(prop).next()) [name, obj[name]];
1412 else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
1413 res = array.iterItems(obj);
1414 else if (Ci.nsIDOMNamedNodeMap && obj instanceof Ci.nsIDOMNamedNodeMap ||
1415 Ci.nsIDOMMozNamedAttrMap && obj instanceof Ci.nsIDOMMozNamedAttrMap)
1416 res = (function () {
1417 for (let i = 0; i < obj.length; i++)
1418 yield [obj.name, obj];
1420 else if (obj instanceof Ci.mozIStorageStatement)
1421 res = (function (obj) {
1422 while (obj.executeStep())
1426 else if ("getNext" in obj) {
1427 if ("hasMoreElements" in obj)
1428 res = (function () {
1429 while (obj.hasMoreElements())
1430 yield obj.getNext();
1432 else if ("hasMore" in obj)
1433 res = (function () {
1434 while (obj.hasMore())
1435 yield obj.getNext();
1438 else if ("enumerator" in obj) {
1439 if (callable(obj.enumerator))
1440 return iter(obj.enumerator());
1441 return iter(obj.enumerator);
1446 toArray: function toArray(iter) array(iter).array,
1448 // See array.prototype for API docs.
1449 toObject: function toObject(iter) {
1451 for (let [k, v] in iter)
1452 if (v instanceof Class.Property)
1453 Object.defineProperty(obj, k, v.init(k, obj) || v);
1459 compact: function compact(iter) (item for (item in iter) if (item != null)),
1461 every: function every(iter, pred, self) {
1462 pred = pred || util.identity;
1463 for (let elem in iter)
1464 if (!pred.call(self, elem))
1468 some: function every(iter, pred, self) {
1469 pred = pred || util.identity;
1470 for (let elem in iter)
1471 if (pred.call(self, elem))
1476 filter: function filter(iter, pred, self) {
1477 for (let elem in iter)
1478 if (pred.call(self, elem))
1483 * Iterates over an iterable object and calls a callback for each
1486 * @param {object} iter The iterator.
1487 * @param {function} fn The callback.
1488 * @param {object} self The this object for *fn*.
1490 forEach: function forEach(iter, func, self) {
1491 for (let val in iter)
1492 func.call(self, val);
1495 indexOf: function indexOf(iter, elem) {
1497 for (let item in iter) {
1505 * Returns the array that results from applying *func* to each property of
1508 * @param {Object} obj
1509 * @param {function} func
1512 map: function map(iter, func, self) {
1514 yield func.call(self, i);
1518 * Returns the nth member of the given array that matches the
1521 nth: function nth(iter, pred, n, self) {
1522 if (typeof pred === "number")
1523 [pred, n] = [() => true, pred]; // Hack.
1525 for (let elem in iter)
1526 if (pred.call(self, elem) && n-- === 0)
1531 sort: function sort(iter, fn, self)
1532 array(this.toArray(iter).sort(fn, self)),
1534 uniq: function uniq(iter) {
1536 for (let item in iter)
1537 if (!Set.add(seen, item))
1542 * Zips the contents of two arrays. The resulting array is the length of
1543 * ary1, with any shortcomings of ary2 replaced with null strings.
1545 * @param {Array} ary1
1546 * @param {Array} ary2
1549 zip: function zip(iter1, iter2) {
1551 yield [iter1.next(), iter2.next()];
1553 catch (e if e instanceof StopIteration) {}
1557 const Iter = Class("Iter", {
1558 init: function init(iter) {
1560 if ("__iterator__" in iter)
1561 this.iter = iter.__iterator__();
1563 if (this.iter.finalize)
1564 this.finalize = function finalize() this.iter.finalize.apply(this.iter, arguments);
1567 next: function next() this.iter.next(),
1569 send: function send() this.iter.send.apply(this.iter, arguments),
1571 __iterator__: function () this.iter
1575 * Array utility methods.
1577 var array = Class("array", Array, {
1578 init: function (ary) {
1579 if (isinstance(ary, ["Iterator", "Generator"]) || "__iterator__" in ary)
1580 ary = [k for (k in ary)];
1581 else if (ary.length)
1582 ary = Array.slice(ary);
1586 __iterator__: function () this.iterItems(),
1587 __noSuchMethod__: function (meth, args) {
1588 var res = array[meth].apply(null, [this.array].concat(args));
1591 if (isinstance(res, ["Iterator", "Generator"]))
1596 toString: function () this.array.toString(),
1597 concat: function (...args) this.__noSuchMethod__("concat", args),
1598 filter: function (...args) this.__noSuchMethod__("filter", args),
1599 map: function (...args) this.__noSuchMethod__("map", args)
1604 * Converts an array to an object. As in lisp, an assoc is an
1605 * array of key-value pairs, which maps directly to an object,
1607 * [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1609 * @param {[Array]} assoc
1610 * @... {string} 0 - Key
1613 toObject: function toObject(assoc) {
1615 assoc.forEach(function ([k, v]) {
1616 if (v instanceof Class.Property)
1617 Object.defineProperty(obj, k, v.init(k, obj) || v);
1625 * Compacts an array, removing all elements that are null or undefined:
1626 * ["foo", null, "bar", undefined] -> ["foo", "bar"]
1628 * @param {Array} ary
1631 compact: function compact(ary) ary.filter(item => item != null),
1634 * Returns true if each element of ary1 is equal to the
1635 * corresponding element in ary2.
1637 * @param {Array} ary1
1638 * @param {Array} ary2
1639 * @returns {boolean}
1641 equals: function (ary1, ary2)
1642 ary1.length === ary2.length && Array.every(ary1, (e, i) => e === ary2[i]),
1645 * Flattens an array, such that all elements of the array are
1646 * joined into a single array:
1647 * [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
1649 * @param {Array} ary
1652 flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1655 * Returns an Iterator for an array's values.
1657 * @param {Array} ary
1658 * @returns {Iterator(Object)}
1660 iterValues: function iterValues(ary) {
1661 for (let i = 0; i < ary.length; i++)
1666 * Returns an Iterator for an array's indices and values.
1668 * @param {Array} ary
1669 * @returns {Iterator([{number}, {Object}])}
1671 iterItems: function iterItems(ary) {
1672 let length = ary.length;
1673 for (let i = 0; i < length; i++)
1678 * Returns the nth member of the given array that matches the
1681 nth: function nth(ary, pred, n, self) {
1682 for (let elem in values(ary))
1683 if (pred.call(self, elem) && n-- === 0)
1689 * Filters out all duplicates from an array. If *unsorted* is false, the
1690 * array is sorted before duplicates are removed.
1692 * @param {Array} ary
1693 * @param {boolean} unsorted
1696 uniq: function uniq(ary, unsorted) {
1699 for (let item in values(ary))
1700 if (res.indexOf(item) == -1)
1704 for (let [, item] in Iterator(ary.sort())) {
1705 if (item != last || !res.length)
1714 * Zips the contents of two arrays. The resulting array is the length of
1715 * ary1, with any shortcomings of ary2 replaced with null strings.
1717 * @param {Array} ary1
1718 * @param {Array} ary2
1721 zip: function zip(ary1, ary2) {
1723 for (let [i, item] in Iterator(ary1))
1724 res.push([item, i in ary2 ? ary2[i] : ""]);
1729 /* Make Minefield not explode, because Minefield exploding is not fun. */
1730 let iterProto = Iter.prototype;
1731 Object.keys(iter).forEach(function (k) {
1732 iterProto[k] = function (...args) {
1733 let res = iter[k].apply(iter, [this].concat(args));
1734 if (isinstance(res, ["Iterator", "Generator"]))
1740 Object.keys(array).forEach(function (k) {
1741 if (!(k in iterProto))
1742 iterProto[k] = function (...args) {
1743 let res = array[k].apply(array, [this.toArray()].concat(args));
1744 if (isinstance(res, ["Iterator", "Generator"]))
1752 Object.getOwnPropertyNames(Array.prototype).forEach(function (k) {
1753 if (!(k in iterProto) && callable(Array.prototype[k]))
1754 iterProto[k] = function () {
1755 let ary = iter(this).toArray();
1756 let res = ary[k].apply(ary, arguments);
1765 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1767 // vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: