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 || function (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, function (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 function () 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(function ([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) function () fn.apply(self, arguments);
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(function () 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(function (name) this[name], this);
929 return callback.call(self || this);
932 names.forEach(function (name, i) this[name] = vals[i], this);
936 toString: function C_toString() {
937 if (this.toStringParams)
938 var params = "(" + this.toStringParams.map(function (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(function (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) {
1099 let error = Error(message);
1100 update(this, error);
1101 this.stack = error.stack;
1102 this.message = message;
1104 let frame = Components.stack;
1105 for (let i = 0; i < this.level + level; i++) {
1106 frame = frame.caller;
1107 this.stack = this.stack.replace(/^.*\n/, "");
1109 this.fileName = frame.filename;
1110 this.lineNumber = frame.lineNumber;
1112 toString: function () String(this.message)
1116 * An Error subclass to throw in order to stop sourcing a plugin without
1117 * printing a stack trace.
1119 var Finished = Class("Finished", ErrorBase);
1122 * Constructs a new Module class and instantiates an instance into the current
1123 * module global object.
1125 * @param {string} name The name of the instance.
1126 * @param {Object} prototype The instance prototype.
1127 * @param {Object} classProperties Properties to be applied to the class constructor.
1130 function Module(name, prototype, ...args) {
1132 let init = callable(prototype) ? 2 : 1;
1133 let proto = callable(prototype) ? args[0] : prototype;
1135 proto._metaInit_ = function () {
1136 delete module.prototype._metaInit_;
1137 currentModule[name.toLowerCase()] = this;
1140 const module = Class.apply(Class, [name, prototype, ...args.slice(0, init)]);
1141 let instance = module();
1142 module.className = name.toLowerCase();
1144 instance.INIT = update(Object.create(Module.INIT),
1147 currentModule[module.className] = instance;
1148 defineModule.modules.push(instance);
1152 if (typeof e === "string")
1155 dump(e.fileName + ":" + e.lineNumber + ": " + e + "\n" + (e.stack || Error().stack));
1159 init: function Module_INIT_init(dactyl, modules, window) {
1160 let args = arguments;
1163 for (let local = this.Local; local; local = local.super)
1166 if (locals.length) {
1167 let module = this, objs = {};
1168 for (let i in locals) {
1169 module = objs[i] = Object.create(module);
1170 module.modules = modules;
1172 module.isLocalModule = true;
1174 modules.jsmodules[this.constructor.className] = module;
1175 locals.reverse().forEach(function (fn, i) update(objs[i], fn.apply(module, args)));
1177 memoize(module, "closure", Class.makeClosure);
1178 module.instance = module;
1182 modules.dactyl.registerObservers(module);
1190 * Creates a new Struct constructor, used for creating objects with
1191 * a fixed set of named members. Each argument should be the name of
1192 * a member in the resulting objects. These names will correspond to
1193 * the arguments passed to the resultant constructor. Instances of
1194 * the new struct may be treated very much like arrays, and provide
1195 * many of the same methods.
1197 * const Point = Struct("x", "y", "z");
1198 * let p1 = Point(x, y, z);
1200 * @returns {function} The constructor for the new Struct.
1202 function Struct(...args) {
1203 if (/^[A-Z]/.test(args[0]))
1204 var className = args.shift();
1206 const Struct = Class(className || "Struct", StructBase, {
1207 length: args.length,
1208 members: array.toObject(args.map(function (v, k) [v, k]))
1210 args.forEach(function (name, i) {
1211 Struct.prototype.__defineGetter__(name, function () this[i]);
1212 Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1216 var StructBase = Class("StructBase", Array, {
1217 init: function struct_init() {
1218 for (let i = 0; i < arguments.length; i++)
1219 if (arguments[i] != undefined)
1220 this[i] = arguments[i];
1223 get toStringParams() this,
1225 clone: function struct_clone() this.constructor.apply(null, this.slice()),
1227 closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1229 get: function struct_get(key, val) this[this.members[key]],
1230 set: function struct_set(key, val) this[this.members[key]] = val,
1232 toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1234 // Iterator over our named members
1235 __iterator__: function struct__iterator__() {
1237 return ([k, self[k]] for (k in keys(self.members)))
1240 fromArray: function fromArray(ary) {
1241 if (!(ary instanceof this))
1242 ary.__proto__ = this.prototype;
1247 * Sets a lazily constructed default value for a member of
1248 * the struct. The value is constructed once, the first time
1249 * it is accessed and memoized thereafter.
1251 * @param {string} key The name of the member for which to
1252 * provide the default value.
1253 * @param {function} val The function which is to generate
1254 * the default value.
1256 defaultValue: function defaultValue(key, val) {
1257 let i = this.prototype.members[key];
1258 this.prototype.__defineGetter__(i, function () (this[i] = val.call(this)));
1259 this.prototype.__defineSetter__(i, function (value)
1260 Class.replaceProperty(this, i, value));
1264 localize: function localize(key, defaultValue) {
1265 let i = this.prototype.members[key];
1266 Object.defineProperty(this.prototype, i, Messages.Localized(defaultValue).init(key, this.prototype));
1271 var Timer = Class("Timer", {
1272 init: function init(minInterval, maxInterval, callback, self) {
1273 this._timer = services.Timer();
1274 this.callback = callback;
1275 this.self = self || this;
1276 this.minInterval = minInterval;
1277 this.maxInterval = maxInterval;
1282 notify: function notify(timer, force) {
1284 if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1287 this._timer.cancel();
1289 // minInterval is the time between the completion of the command and the next firing
1290 this.doneAt = Date.now() + this.minInterval;
1292 this.callback.call(this.self, this.arg);
1295 if (typeof util === "undefined")
1296 dump(JSMLoader.name + ": " + e + "\n" + (e.stack || Error().stack));
1298 util.reportError(e);
1301 this.doneAt = Date.now() + this.minInterval;
1305 tell: function tell(arg) {
1306 if (arguments.length > 0)
1309 let now = Date.now();
1310 if (this.doneAt == -1)
1311 this._timer.cancel();
1313 let timeout = this.minInterval;
1314 if (now > this.doneAt && this.doneAt > -1)
1316 else if (this.latest)
1317 timeout = Math.min(timeout, this.latest - now);
1319 this.latest = now + this.maxInterval;
1321 this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1325 reset: function reset() {
1326 this._timer.cancel();
1330 flush: function flush(force) {
1331 if (this.doneAt == -1 || force)
1332 this.notify(null, true);
1337 * Idempotent function which returns the UTF-8 encoded value of an
1338 * improperly-decoded string.
1340 * @param {string} str
1343 function UTF8(str) {
1345 return decodeURIComponent(escape(str));
1352 function octal(decimal) parseInt(decimal, 8);
1355 * Iterates over an arbitrary object. The following iterator types are
1356 * supported, and work as a user would expect:
1358 * • nsIDOMNodeIterator
1359 * • mozIStorageStatement
1361 * Additionally, the following array-like objects yield a tuple of the
1362 * form [index, element] for each contained element:
1364 * • nsIDOMHTMLCollection
1365 * • nsIDOMNodeList
1367 * and the following likewise yield one element of the form
1368 * [name, element] for each contained element:
1370 * • nsIDOMNamedNodeMap
1372 * Duck typing is implemented for any other type. If the object
1373 * contains the "enumerator" property, iter is called on that. If the
1374 * property is a function, it is called first. If it contains the
1375 * property "getNext" along with either "hasMoreItems" or "hasMore", it
1376 * is iterated over appropriately.
1378 * For all other cases, this function behaves exactly like the Iterator
1381 * @param {object} obj
1382 * @param {nsIJSIID} iface The interface to which to query all elements.
1383 * @returns {Generator}
1385 function iter(obj, iface) {
1386 if (arguments.length == 2 && iface instanceof Ci.nsIJSIID)
1387 return iter(obj).map(function (item) item.QueryInterface(iface));
1389 let args = arguments;
1390 let res = Iterator(obj);
1392 if (args.length > 1)
1393 res = (function () {
1394 for (let i = 0; i < args.length; i++)
1395 for (let j in iter(args[i]))
1398 else if (isinstance(obj, ["Iterator", "Generator"]))
1400 else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1401 while (obj.constructor instanceof ctypes.PointerType)
1403 if (obj.constructor instanceof ctypes.ArrayType)
1404 res = array.iterItems(obj);
1405 else if (obj.constructor instanceof ctypes.StructType)
1406 res = (function () {
1407 for (let prop in values(obj.constructor.fields))
1408 yield let ([name, type] = Iterator(prop).next()) [name, obj[name]];
1413 else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
1414 res = array.iterItems(obj);
1415 else if (Ci.nsIDOMNamedNodeMap && obj instanceof Ci.nsIDOMNamedNodeMap ||
1416 Ci.nsIDOMMozNamedAttrMap && obj instanceof Ci.nsIDOMMozNamedAttrMap)
1417 res = (function () {
1418 for (let i = 0; i < obj.length; i++)
1419 yield [obj.name, obj];
1421 else if (obj instanceof Ci.mozIStorageStatement)
1422 res = (function (obj) {
1423 while (obj.executeStep())
1427 else if ("getNext" in obj) {
1428 if ("hasMoreElements" in obj)
1429 res = (function () {
1430 while (obj.hasMoreElements())
1431 yield obj.getNext();
1433 else if ("hasMore" in obj)
1434 res = (function () {
1435 while (obj.hasMore())
1436 yield obj.getNext();
1439 else if ("enumerator" in obj) {
1440 if (callable(obj.enumerator))
1441 return iter(obj.enumerator());
1442 return iter(obj.enumerator);
1447 toArray: function toArray(iter) array(iter).array,
1449 // See array.prototype for API docs.
1450 toObject: function toObject(iter) {
1452 for (let [k, v] in iter)
1453 if (v instanceof Class.Property)
1454 Object.defineProperty(obj, k, v.init(k, obj) || v);
1460 compact: function compact(iter) (item for (item in iter) if (item != null)),
1462 every: function every(iter, pred, self) {
1463 pred = pred || util.identity;
1464 for (let elem in iter)
1465 if (!pred.call(self, elem))
1469 some: function every(iter, pred, self) {
1470 pred = pred || util.identity;
1471 for (let elem in iter)
1472 if (pred.call(self, elem))
1477 filter: function filter(iter, pred, self) {
1478 for (let elem in iter)
1479 if (pred.call(self, elem))
1484 * Iterates over an iterable object and calls a callback for each
1487 * @param {object} iter The iterator.
1488 * @param {function} fn The callback.
1489 * @param {object} self The this object for *fn*.
1491 forEach: function forEach(iter, func, self) {
1492 for (let val in iter)
1493 func.call(self, val);
1496 indexOf: function indexOf(iter, elem) {
1498 for (let item in iter) {
1506 * Returns the array that results from applying *func* to each property of
1509 * @param {Object} obj
1510 * @param {function} func
1513 map: function map(iter, func, self) {
1515 yield func.call(self, i);
1519 * Returns the nth member of the given array that matches the
1522 nth: function nth(iter, pred, n, self) {
1523 if (typeof pred === "number")
1524 [pred, n] = [function () true, pred]; // Hack.
1526 for (let elem in iter)
1527 if (pred.call(self, elem) && n-- === 0)
1532 sort: function sort(iter, fn, self)
1533 array(this.toArray(iter).sort(fn, self)),
1535 uniq: function uniq(iter) {
1537 for (let item in iter)
1538 if (!Set.add(seen, item))
1543 * Zips the contents of two arrays. The resulting array is the length of
1544 * ary1, with any shortcomings of ary2 replaced with null strings.
1546 * @param {Array} ary1
1547 * @param {Array} ary2
1550 zip: function zip(iter1, iter2) {
1552 yield [iter1.next(), iter2.next()];
1554 catch (e if e instanceof StopIteration) {}
1558 const Iter = Class("Iter", {
1559 init: function init(iter) {
1561 if ("__iterator__" in iter)
1562 this.iter = iter.__iterator__();
1564 if (this.iter.finalize)
1565 this.finalize = function finalize() this.iter.finalize.apply(this.iter, arguments);
1568 next: function next() this.iter.next(),
1570 send: function send() this.iter.send.apply(this.iter, arguments),
1572 __iterator__: function () this.iter
1576 * Array utility methods.
1578 var array = Class("array", Array, {
1579 init: function (ary) {
1580 if (isinstance(ary, ["Iterator", "Generator"]) || "__iterator__" in ary)
1581 ary = [k for (k in ary)];
1582 else if (ary.length)
1583 ary = Array.slice(ary);
1587 __iterator__: function () this.iterItems(),
1588 __noSuchMethod__: function (meth, args) {
1589 var res = array[meth].apply(null, [this.array].concat(args));
1592 if (isinstance(res, ["Iterator", "Generator"]))
1597 toString: function () this.array.toString(),
1598 concat: function (...args) this.__noSuchMethod__("concat", args),
1599 filter: function (...args) this.__noSuchMethod__("filter", args),
1600 map: function (...args) this.__noSuchMethod__("map", args)
1605 * Converts an array to an object. As in lisp, an assoc is an
1606 * array of key-value pairs, which maps directly to an object,
1608 * [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1610 * @param {[Array]} assoc
1611 * @... {string} 0 - Key
1614 toObject: function toObject(assoc) {
1616 assoc.forEach(function ([k, v]) {
1617 if (v instanceof Class.Property)
1618 Object.defineProperty(obj, k, v.init(k, obj) || v);
1626 * Compacts an array, removing all elements that are null or undefined:
1627 * ["foo", null, "bar", undefined] -> ["foo", "bar"]
1629 * @param {Array} ary
1632 compact: function compact(ary) ary.filter(function (item) item != null),
1635 * Returns true if each element of ary1 is equal to the
1636 * corresponding element in ary2.
1638 * @param {Array} ary1
1639 * @param {Array} ary2
1640 * @returns {boolean}
1642 equals: function (ary1, ary2)
1643 ary1.length === ary2.length && Array.every(ary1, function (e, i) e === ary2[i]),
1646 * Flattens an array, such that all elements of the array are
1647 * joined into a single array:
1648 * [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
1650 * @param {Array} ary
1653 flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1656 * Returns an Iterator for an array's values.
1658 * @param {Array} ary
1659 * @returns {Iterator(Object)}
1661 iterValues: function iterValues(ary) {
1662 for (let i = 0; i < ary.length; i++)
1667 * Returns an Iterator for an array's indices and values.
1669 * @param {Array} ary
1670 * @returns {Iterator([{number}, {Object}])}
1672 iterItems: function iterItems(ary) {
1673 let length = ary.length;
1674 for (let i = 0; i < length; i++)
1679 * Returns the nth member of the given array that matches the
1682 nth: function nth(ary, pred, n, self) {
1683 for (let elem in values(ary))
1684 if (pred.call(self, elem) && n-- === 0)
1690 * Filters out all duplicates from an array. If *unsorted* is false, the
1691 * array is sorted before duplicates are removed.
1693 * @param {Array} ary
1694 * @param {boolean} unsorted
1697 uniq: function uniq(ary, unsorted) {
1700 for (let item in values(ary))
1701 if (res.indexOf(item) == -1)
1705 for (let [, item] in Iterator(ary.sort())) {
1706 if (item != last || !res.length)
1715 * Zips the contents of two arrays. The resulting array is the length of
1716 * ary1, with any shortcomings of ary2 replaced with null strings.
1718 * @param {Array} ary1
1719 * @param {Array} ary2
1722 zip: function zip(ary1, ary2) {
1724 for (let [i, item] in Iterator(ary1))
1725 res.push([item, i in ary2 ? ary2[i] : ""]);
1730 /* Make Minefield not explode, because Minefield exploding is not fun. */
1731 let iterProto = Iter.prototype;
1732 Object.keys(iter).forEach(function (k) {
1733 iterProto[k] = function (...args) {
1734 let res = iter[k].apply(iter, [this].concat(args));
1735 if (isinstance(res, ["Iterator", "Generator"]))
1741 Object.keys(array).forEach(function (k) {
1742 if (!(k in iterProto))
1743 iterProto[k] = function (...args) {
1744 let res = array[k].apply(array, [this.toArray()].concat(args));
1745 if (isinstance(res, ["Iterator", "Generator"]))
1753 Object.getOwnPropertyNames(Array.prototype).forEach(function (k) {
1754 if (!(k in iterProto) && callable(Array.prototype[k]))
1755 iterProto[k] = function () {
1756 let ary = iter(this).toArray();
1757 let res = ary[k].apply(ary, arguments);
1766 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1768 // vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: