1 // Copyright (c) 2009-2012 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_() {
72 let msg = Array.map(arguments, 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) {
82 let time = Date.now();
83 if (typeof func !== "function")
87 var res = func.apply(self, Array.slice(arguments, 4));
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, Array.slice(arguments));
449 return function curried() {
450 let args = acc.concat(Array.slice(arguments));
452 // The curried result should preserve 'this'
453 if (arguments.length == 0)
454 return close(self || this, curried);
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) let (func = callable(meth) ? meth : self[meth])
465 func.bind.apply(func, Array.slice(arguments, 1));
467 var bind = function bind(func, self) {
471 let args = Array.slice(arguments, bind.length);
472 return function bound() func.apply(self, args.concat(Array.slice(arguments)));
476 * Returns true if both arguments are functions and
477 * (targ() instanceof src) would also return true.
479 * @param {function} targ
480 * @param {function} src
483 function isSubclass(targ, src) {
484 return src === targ ||
485 targ && typeof targ === "function" && targ.prototype instanceof src;
489 * Returns true if *object* is an instance of *interfaces*. If *interfaces* is
490 * an array, returns true if *object* is an instance of any element of
491 * *interfaces*. If *interfaces* is the object form of a primitive type,
492 * returns true if *object* is a non-boxed version of the type, i.e., if
493 * (typeof object == "string"), isinstance(object, String) is true. Finally, if
494 * *interfaces* is a string, returns true if ({}.toString.call(object) ==
495 * "[object <interfaces>]").
497 * @param {object} object The object to check.
498 * @param {constructor|[constructor|string]} interfaces The types to check *object* against.
501 var isinstance_types = {
507 function isinstance(object, interfaces) {
511 return Array.concat(interfaces).some(function isinstance_some(iface) {
512 if (typeof iface === "string") {
513 if (objproto.toString.call(object) === "[object " + iface + "]")
516 else if (typeof object === "object" && "isinstance" in object && object.isinstance !== isinstance) {
517 if (object.isinstance(iface))
521 if (object instanceof iface)
523 var type = isinstance_types[typeof object];
524 if (type && isSubclass(iface, type))
532 * Returns true if obj is a non-null object.
534 function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
537 * Returns true if obje is an E4X XML object.
540 function isXML(obj) typeof obj === "xml";
543 * Returns true if and only if its sole argument is an
544 * instance of the builtin Array type. The array may come from
545 * any window, frame, namespace, or execution context, which
546 * is not the case when using (obj instanceof Array).
550 // This is bloody stupid.
551 ? function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array"
552 : function isArray(val) objproto.toString.call(val) == "[object Array]";
555 * Returns true if and only if its sole argument is an
556 * instance of the builtin Generator type. This includes
557 * functions containing the 'yield' statement and generator
558 * statements such as (x for (x in obj)).
560 function isGenerator(val) objproto.toString.call(val) == "[object Generator]";
563 * Returns true if and only if its sole argument is a String,
564 * as defined by the builtin type. May be constructed via
565 * String(foo) or new String(foo) from any window, frame,
566 * namespace, or execution context, which is not the case when
567 * using (obj instanceof String) or (typeof obj == "string").
569 function isString(val) objproto.toString.call(val) == "[object String]";
572 * Returns true if and only if its sole argument may be called
573 * as a function. This includes classes and function objects.
575 function callable(val) typeof val === "function" && !(val instanceof Ci.nsIDOMElement);
578 fn.apply(arguments[1], Array.slice(arguments, 2));
583 * Memoizes an object property value.
585 * @param {object} obj The object to add the property to.
586 * @param {string} key The property name.
587 * @param {function} getter The function which will return the initial
588 * value of the property.
590 function memoize(obj, key, getter) {
591 if (arguments.length == 1) {
592 let res = update(Object.create(obj), obj);
593 for each (let prop in Object.getOwnPropertyNames(obj)) {
594 let get = __lookupGetter__.call(obj, prop);
596 memoize(res, prop, get);
602 Object.defineProperty(obj, key, {
606 get: function g_replaceProperty() {
608 Class.replaceProperty(this.instance || this, key, null);
609 return Class.replaceProperty(this.instance || this, key, getter.call(this, key));
616 set: function s_replaceProperty(val)
617 Class.replaceProperty(this.instance || this, key, val)
621 obj[key] = getter.call(obj, key);
625 let sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance());
626 sandbox.__proto__ = this;
629 * Updates an object with the properties of another object. Getters
630 * and setters are copied as expected. Moreover, any function
631 * properties receive new 'supercall' and 'superapply' properties,
632 * which will call the identically named function in target's
635 * let a = { foo: function (arg) "bar " + arg }
636 * let b = { __proto__: a }
637 * update(b, { foo: function foo() foo.supercall(this, "baz") });
639 * a.foo("foo") -> "bar foo"
640 * b.foo() -> "bar baz"
642 * @param {Object} target The object to update.
643 * @param {Object} src The source object from which to update target.
644 * May be provided multiple times.
645 * @returns {Object} Returns its updated first argument.
647 function update(target) {
648 for (let i = 1; i < arguments.length; i++) {
649 let src = arguments[i];
650 Object.getOwnPropertyNames(src || {}).forEach(function (k) {
651 let desc = Object.getOwnPropertyDescriptor(src, k);
652 if (desc.value instanceof Class.Property)
653 desc = desc.value.init(k, target) || desc.value;
656 if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) {
657 let func = desc.value.wrapped || desc.value;
658 if (!func.superapply) {
659 func.__defineGetter__("super", function get_super() Object.getPrototypeOf(target)[k]);
660 func.superapply = function superapply(self, args)
661 let (meth = Object.getPrototypeOf(target)[k])
662 meth && meth.apply(self, args);
663 func.supercall = function supercall(self)
664 func.superapply(self, Array.slice(arguments, 1));
667 Object.defineProperty(target, k, desc);
674 function update_(target) {
675 for (let i = 1; i < arguments.length; i++) {
676 let src = arguments[i];
677 Object.getOwnPropertyNames(src || {}).forEach(function (k) {
678 let desc = Object.getOwnPropertyDescriptor(src, k);
679 if (desc.value instanceof Class.Property)
680 desc = desc.value.init(k, target) || desc.value;
683 if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) {
684 let func = desc.value.wrapped || desc.value;
685 if (!func.superapply) {
686 func.__defineGetter__("super", function get_super_() Object.getPrototypeOf(target)[k]);
687 func.superapply = function super_apply(self, args)
688 let (meth = Object.getPrototypeOf(target)[k])
689 meth && meth.apply(self, args);
690 func.supercall = function super_call(self)
691 func.superapply(self, Array.slice(arguments, 1));
694 Object.defineProperty(target, k, desc);
705 * Constructs a new Class. Arguments marked as optional must be
706 * either entirely elided, or they must have the exact type
709 * @param {string} name The class's as it will appear when toString
710 * is called, as well as in stack traces.
712 * @param {function} base The base class for this module. May be any
716 * @param {Object} prototype The prototype for instances of this
717 * object. The object itself is copied and not used as a prototype
719 * @param {Object} classProperties The class properties for the new
720 * module constructor. More than one may be provided.
723 * @returns {function} The constructor for the resulting class.
727 var args = Array.slice(arguments);
728 if (isString(args[0]))
729 var name = args.shift();
730 var superclass = Class;
731 if (callable(args[0]))
732 superclass = args.shift();
734 if (loaded.config && (config.haveGecko("5.*", "6.0") || config.haveGecko("6.*"))) // Bug 657418.
735 var Constructor = function Constructor() {
736 var self = Object.create(Constructor.prototype);
737 self.instance = self;
738 self.globalInstance = self;
740 if ("_metaInit_" in self && self._metaInit_)
741 self._metaInit_.apply(self, arguments);
743 var res = self.init.apply(self, arguments);
744 return res !== undefined ? res : self;
747 var Constructor = eval(String.replace('\n\
748 (function constructor(PARAMS) { \n\
749 var self = Object.create(Constructor.prototype); \n\
750 self.instance = self; \n\
751 self.globalInstance = self; \n\
753 if ("_metaInit_" in self && self._metaInit_) \n\
754 self._metaInit_.apply(self, arguments); \n\
756 var res = self.init.apply(self, arguments); \n\
757 return res !== undefined ? res : self; \n\
759 "constructor", (name || superclass.className).replace(/\W/g, "_"))
760 .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1]
761 .replace(/\b(self|res|Constructor)\b/g, "$1_")));
763 Constructor.className = name || superclass.className || superclass.name;
765 if ("init" in superclass.prototype)
766 Constructor.__proto__ = superclass;
768 let superc = superclass;
769 superclass = function Shim() {};
770 Class.extend(superclass, superc, {
773 superclass.__proto__ = superc;
776 Class.extend(Constructor, superclass, args[0]);
777 memoize(Constructor, "closure", Class.makeClosure);
778 update(Constructor, args[1]);
780 Constructor.__proto__ = superclass;
782 args.slice(2).forEach(function (obj) {
785 update(Constructor.prototype, obj);
790 if (Cu.getGlobalForObject)
791 Class.objectGlobal = function (object) {
793 return Cu.getGlobalForObject(object);
800 Class.objectGlobal = function (object) {
801 while (object.__parent__)
802 object = object.__parent__;
807 * @class Class.Property
808 * A class which, when assigned to a property in a Class's prototype
809 * or class property object, defines that property's descriptor
810 * rather than its value. If the desc object has an init property, it
811 * will be called with the property's name before the descriptor is
814 * @param {Object} desc The property descriptor.
816 Class.Property = function Property(desc) update(
817 Object.create(Property.prototype), desc || { configurable: true, writable: true });
818 Class.Property.prototype.init = function () {};
820 * Extends a subclass with a superclass. The subclass's
821 * prototype is replaced with a new object, which inherits
822 * from the superclass's prototype, {@see update}d with the
823 * members of *overrides*.
825 * @param {function} subclass
826 * @param {function} superclass
827 * @param {Object} overrides @optional
829 Class.extend = function extend(subclass, superclass, overrides) {
830 subclass.superclass = superclass;
832 subclass.prototype = Object.create(superclass.prototype);
833 update(subclass.prototype, overrides);
834 subclass.prototype.constructor = subclass;
835 subclass.prototype._class_ = subclass;
837 if (superclass.prototype.constructor === objproto.constructor)
838 superclass.prototype.constructor = superclass;
842 * Memoizes the value of a class property to the value returned by
843 * the passed function the first time the property is accessed.
845 * @param {function(string)} getter The function which returns the
847 * @returns {Class.Property}
849 Class.Memoize = function Memoize(getter, wait)
853 init: function (key) {
857 // Crazy, yeah, I know. -- Kris
858 this.get = function replace() {
859 let obj = this.instance || this;
860 Object.defineProperty(obj, key, {
861 configurable: true, enumerable: false,
862 get: function get() {
863 util.waitFor(function () done);
868 util.yieldable(function () {
870 for (var res in getter.call(obj)) {
871 if (wait !== undefined)
875 Class.replaceProperty(obj, key, res);
882 this.get = function g_Memoize() {
883 let obj = this.instance || this;
885 Class.replaceProperty(obj, key, null);
886 return Class.replaceProperty(obj, key, getter.call(this, key));
893 this.set = function s_Memoize(val) Class.replaceProperty(this.instance || this, key, val);
897 Class.memoize = deprecated("Class.Memoize", function memoize() Class.Memoize.apply(this, arguments));
900 * Updates the given object with the object in the target class's
903 Class.Update = function Update(obj)
908 init: function (key, target) {
909 this.value = update({}, target[key], obj);
913 Class.replaceProperty = function replaceProperty(obj, prop, value) {
914 Object.defineProperty(obj, prop, { configurable: true, enumerable: true, value: value, writable: true });
917 Class.toString = function toString() "[class " + this.className + "]";
920 * Initializes new instances of this class. Called automatically
921 * when new instances are created.
923 init: function c_init() {},
926 set instance(val) Class.replaceProperty(this, "instance", val),
928 withSavedValues: function withSavedValues(names, callback, self) {
929 let vals = names.map(function (name) this[name], this);
931 return callback.call(self || this);
934 names.forEach(function (name, i) this[name] = vals[i], this);
938 toString: function C_toString() {
939 if (this.toStringParams)
940 var params = "(" + this.toStringParams.map(function (m) isArray(m) ? "[" + m + "]" :
941 isString(m) ? m.quote() : String(m))
943 return "[instance " + this.constructor.className + (params || "") + "]";
947 * Executes *callback* after *timeout* milliseconds. The value of
948 * 'this' is preserved in the invocation of *callback*.
950 * @param {function} callback The function to call after *timeout*
951 * @param {number} timeout The time, in milliseconds, to wait
952 * before calling *callback*.
953 * @returns {nsITimer} The timer which backs this timeout.
955 timeout: function timeout(callback, timeout) {
957 function timeout_notify(timer) {
959 util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"]))
961 self.timeouts.splice(self.timeouts.indexOf(timer), 1);
962 util.trapErrors(callback, self);
964 let timer = services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
965 this.timeouts.push(timer);
971 * Updates this instance with the properties of the given objects.
972 * Like the update function, but with special semantics for
973 * localized properties.
975 update: function update() {
979 for (let i = 0; i < arguments.length; i++) {
980 let src = arguments[i];
981 Object.getOwnPropertyNames(src || {}).forEach(function (k) {
982 let desc = Object.getOwnPropertyDescriptor(src, k);
983 if (desc.value instanceof Class.Property)
984 desc = desc.value.init(k, this) || desc.value;
986 if (typeof desc.value === "function") {
987 let func = desc.value.wrapped || desc.value;
988 if (!func.superapply) {
989 func.__defineGetter__("super", function () Object.getPrototypeOf(self)[k]);
990 func.superapply = function superapply(self, args)
991 let (meth = Object.getPrototypeOf(self)[k])
992 meth && meth.apply(self, args);
993 func.supercall = function supercall(self)
994 func.superapply(self, Array.slice(arguments, 1));
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);
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) {
1132 let init = callable(prototype) ? 4 : 3;
1133 let proto = arguments[callable(prototype) ? 2 : 1];
1135 proto._metaInit_ = function () {
1136 delete module.prototype._metaInit_;
1137 currentModule[name.toLowerCase()] = this;
1140 const module = Class.apply(Class, Array.slice(arguments, 0, init));
1141 let instance = module();
1142 module.className = name.toLowerCase();
1144 instance.INIT = update(Object.create(Module.INIT),
1145 arguments[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.
1203 if (!/^[A-Z]/.test(arguments[0]))
1204 var args = Array.slice(arguments, 0);
1206 var className = arguments[0];
1207 args = Array.slice(arguments, 1);
1210 const Struct = Class(className || "Struct", StructBase, {
1211 length: args.length,
1212 members: array.toObject(args.map(function (v, k) [v, k]))
1214 args.forEach(function (name, i) {
1215 Struct.prototype.__defineGetter__(name, function () this[i]);
1216 Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1220 var StructBase = Class("StructBase", Array, {
1221 init: function struct_init() {
1222 for (let i = 0; i < arguments.length; i++)
1223 if (arguments[i] != undefined)
1224 this[i] = arguments[i];
1227 get toStringParams() this,
1229 clone: function struct_clone() this.constructor.apply(null, this.slice()),
1231 closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1233 get: function struct_get(key, val) this[this.members[key]],
1234 set: function struct_set(key, val) this[this.members[key]] = val,
1236 toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1238 // Iterator over our named members
1239 __iterator__: function struct__iterator__() {
1241 return ([k, self[k]] for (k in keys(self.members)))
1244 fromArray: function fromArray(ary) {
1245 if (!(ary instanceof this))
1246 ary.__proto__ = this.prototype;
1251 * Sets a lazily constructed default value for a member of
1252 * the struct. The value is constructed once, the first time
1253 * it is accessed and memoized thereafter.
1255 * @param {string} key The name of the member for which to
1256 * provide the default value.
1257 * @param {function} val The function which is to generate
1258 * the default value.
1260 defaultValue: function defaultValue(key, val) {
1261 let i = this.prototype.members[key];
1262 this.prototype.__defineGetter__(i, function () (this[i] = val.call(this)));
1263 this.prototype.__defineSetter__(i, function (value)
1264 Class.replaceProperty(this, i, value));
1268 localize: function localize(key, defaultValue) {
1269 let i = this.prototype.members[key];
1270 Object.defineProperty(this.prototype, i, Messages.Localized(defaultValue).init(key, this.prototype));
1275 var Timer = Class("Timer", {
1276 init: function init(minInterval, maxInterval, callback, self) {
1277 this._timer = services.Timer();
1278 this.callback = callback;
1279 this.self = self || this;
1280 this.minInterval = minInterval;
1281 this.maxInterval = maxInterval;
1286 notify: function notify(timer, force) {
1288 if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1291 this._timer.cancel();
1293 // minInterval is the time between the completion of the command and the next firing
1294 this.doneAt = Date.now() + this.minInterval;
1296 this.callback.call(this.self, this.arg);
1299 if (typeof util === "undefined")
1300 dump(JSMLoader.name + ": " + e + "\n" + (e.stack || Error().stack));
1302 util.reportError(e);
1305 this.doneAt = Date.now() + this.minInterval;
1309 tell: function tell(arg) {
1310 if (arguments.length > 0)
1313 let now = Date.now();
1314 if (this.doneAt == -1)
1315 this._timer.cancel();
1317 let timeout = this.minInterval;
1318 if (now > this.doneAt && this.doneAt > -1)
1320 else if (this.latest)
1321 timeout = Math.min(timeout, this.latest - now);
1323 this.latest = now + this.maxInterval;
1325 this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1329 reset: function reset() {
1330 this._timer.cancel();
1334 flush: function flush(force) {
1335 if (this.doneAt == -1 || force)
1336 this.notify(null, true);
1341 * Idempotent function which returns the UTF-8 encoded value of an
1342 * improperly-decoded string.
1344 * @param {string} str
1347 function UTF8(str) {
1349 return decodeURIComponent(escape(str));
1356 function octal(decimal) parseInt(decimal, 8);
1359 * Iterates over an arbitrary object. The following iterator types are
1360 * supported, and work as a user would expect:
1362 * • nsIDOMNodeIterator
1363 * • mozIStorageStatement
1365 * Additionally, the following array-like objects yield a tuple of the
1366 * form [index, element] for each contained element:
1368 * • nsIDOMHTMLCollection
1369 * • nsIDOMNodeList
1371 * and the following likewise yield one element of the form
1372 * [name, element] for each contained element:
1374 * • nsIDOMNamedNodeMap
1376 * Duck typing is implemented for any other type. If the object
1377 * contains the "enumerator" property, iter is called on that. If the
1378 * property is a function, it is called first. If it contains the
1379 * property "getNext" along with either "hasMoreItems" or "hasMore", it
1380 * is iterated over appropriately.
1382 * For all other cases, this function behaves exactly like the Iterator
1385 * @param {object} obj
1386 * @param {nsIJSIID} iface The interface to which to query all elements.
1387 * @returns {Generator}
1389 function iter(obj, iface) {
1390 if (arguments.length == 2 && iface instanceof Ci.nsIJSIID)
1391 return iter(obj).map(function (item) item.QueryInterface(iface));
1393 let args = arguments;
1394 let res = Iterator(obj);
1396 if (args.length > 1)
1397 res = (function () {
1398 for (let i = 0; i < args.length; i++)
1399 for (let j in iter(args[i]))
1402 else if (isinstance(obj, ["Iterator", "Generator"]))
1404 else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1405 while (obj.constructor instanceof ctypes.PointerType)
1407 if (obj.constructor instanceof ctypes.ArrayType)
1408 res = array.iterItems(obj);
1409 else if (obj.constructor instanceof ctypes.StructType)
1410 res = (function () {
1411 for (let prop in values(obj.constructor.fields))
1412 yield let ([name, type] = Iterator(prop).next()) [name, obj[name]];
1417 else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
1418 res = array.iterItems(obj);
1419 else if (Ci.nsIDOMNamedNodeMap && obj instanceof Ci.nsIDOMNamedNodeMap ||
1420 Ci.nsIDOMMozNamedAttrMap && obj instanceof Ci.nsIDOMMozNamedAttrMap)
1421 res = (function () {
1422 for (let i = 0; i < obj.length; i++)
1423 yield [obj.name, obj];
1425 else if (obj instanceof Ci.mozIStorageStatement)
1426 res = (function (obj) {
1427 while (obj.executeStep())
1431 else if ("getNext" in obj) {
1432 if ("hasMoreElements" in obj)
1433 res = (function () {
1434 while (obj.hasMoreElements())
1435 yield obj.getNext();
1437 else if ("hasMore" in obj)
1438 res = (function () {
1439 while (obj.hasMore())
1440 yield obj.getNext();
1443 else if ("enumerator" in obj) {
1444 if (callable(obj.enumerator))
1445 return iter(obj.enumerator());
1446 return iter(obj.enumerator);
1451 toArray: function toArray(iter) array(iter).array,
1453 // See array.prototype for API docs.
1454 toObject: function toObject(iter) {
1456 for (let [k, v] in iter)
1457 if (v instanceof Class.Property)
1458 Object.defineProperty(obj, k, v.init(k, obj) || v);
1464 compact: function compact(iter) (item for (item in iter) if (item != null)),
1466 every: function every(iter, pred, self) {
1467 pred = pred || util.identity;
1468 for (let elem in iter)
1469 if (!pred.call(self, elem))
1473 some: function every(iter, pred, self) {
1474 pred = pred || util.identity;
1475 for (let elem in iter)
1476 if (pred.call(self, elem))
1481 filter: function filter(iter, pred, self) {
1482 for (let elem in iter)
1483 if (pred.call(self, elem))
1488 * Iterates over an iterable object and calls a callback for each
1491 * @param {object} iter The iterator.
1492 * @param {function} fn The callback.
1493 * @param {object} self The this object for *fn*.
1495 forEach: function forEach(iter, func, self) {
1496 for (let val in iter)
1497 func.call(self, val);
1500 indexOf: function indexOf(iter, elem) {
1502 for (let item in iter) {
1510 * Returns the array that results from applying *func* to each property of
1513 * @param {Object} obj
1514 * @param {function} func
1517 map: function map(iter, func, self) {
1519 yield func.call(self, i);
1523 * Returns the nth member of the given array that matches the
1526 nth: function nth(iter, pred, n, self) {
1527 if (typeof pred === "number")
1528 [pred, n] = [function () true, pred]; // Hack.
1530 for (let elem in iter)
1531 if (pred.call(self, elem) && n-- === 0)
1536 sort: function sort(iter, fn, self)
1537 array(this.toArray(iter).sort(fn, self)),
1539 uniq: function uniq(iter) {
1541 for (let item in iter)
1542 if (!Set.add(seen, item))
1547 * Zips the contents of two arrays. The resulting array is the length of
1548 * ary1, with any shortcomings of ary2 replaced with null strings.
1550 * @param {Array} ary1
1551 * @param {Array} ary2
1554 zip: function zip(iter1, iter2) {
1556 yield [iter1.next(), iter2.next()];
1558 catch (e if e instanceof StopIteration) {}
1562 const Iter = Class("Iter", {
1563 init: function init(iter) {
1565 if ("__iterator__" in iter)
1566 this.iter = iter.__iterator__();
1568 if (this.iter.finalize)
1569 this.finalize = function finalize() this.iter.finalize.apply(this.iter, arguments);
1572 next: function next() this.iter.next(),
1574 send: function send() this.iter.send.apply(this.iter, arguments),
1576 __iterator__: function () this.iter
1580 * Array utility methods.
1582 var array = Class("array", Array, {
1583 init: function (ary) {
1584 if (isinstance(ary, ["Iterator", "Generator"]) || "__iterator__" in ary)
1585 ary = [k for (k in ary)];
1586 else if (ary.length)
1587 ary = Array.slice(ary);
1591 __iterator__: function () this.iterItems(),
1592 __noSuchMethod__: function (meth, args) {
1593 var res = array[meth].apply(null, [this.array].concat(args));
1596 if (isinstance(res, ["Iterator", "Generator"]))
1601 toString: function () this.array.toString(),
1602 concat: function () this.__noSuchMethod__("concat", Array.slice(arguments)),
1603 filter: function () this.__noSuchMethod__("filter", Array.slice(arguments)),
1604 map: function () this.__noSuchMethod__("map", Array.slice(arguments))
1609 * Converts an array to an object. As in lisp, an assoc is an
1610 * array of key-value pairs, which maps directly to an object,
1612 * [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1614 * @param {[Array]} assoc
1615 * @... {string} 0 - Key
1618 toObject: function toObject(assoc) {
1620 assoc.forEach(function ([k, v]) {
1621 if (v instanceof Class.Property)
1622 Object.defineProperty(obj, k, v.init(k, obj) || v);
1630 * Compacts an array, removing all elements that are null or undefined:
1631 * ["foo", null, "bar", undefined] -> ["foo", "bar"]
1633 * @param {Array} ary
1636 compact: function compact(ary) ary.filter(function (item) item != null),
1639 * Returns true if each element of ary1 is equal to the
1640 * corresponding element in ary2.
1642 * @param {Array} ary1
1643 * @param {Array} ary2
1644 * @returns {boolean}
1646 equals: function (ary1, ary2)
1647 ary1.length === ary2.length && Array.every(ary1, function (e, i) e === ary2[i]),
1650 * Flattens an array, such that all elements of the array are
1651 * joined into a single array:
1652 * [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
1654 * @param {Array} ary
1657 flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1660 * Returns an Iterator for an array's values.
1662 * @param {Array} ary
1663 * @returns {Iterator(Object)}
1665 iterValues: function iterValues(ary) {
1666 for (let i = 0; i < ary.length; i++)
1671 * Returns an Iterator for an array's indices and values.
1673 * @param {Array} ary
1674 * @returns {Iterator([{number}, {Object}])}
1676 iterItems: function iterItems(ary) {
1677 let length = ary.length;
1678 for (let i = 0; i < length; i++)
1683 * Returns the nth member of the given array that matches the
1686 nth: function nth(ary, pred, n, self) {
1687 for (let elem in values(ary))
1688 if (pred.call(self, elem) && n-- === 0)
1694 * Filters out all duplicates from an array. If *unsorted* is false, the
1695 * array is sorted before duplicates are removed.
1697 * @param {Array} ary
1698 * @param {boolean} unsorted
1701 uniq: function uniq(ary, unsorted) {
1704 for (let item in values(ary))
1705 if (res.indexOf(item) == -1)
1709 for (let [, item] in Iterator(ary.sort())) {
1710 if (item != last || !res.length)
1719 * Zips the contents of two arrays. The resulting array is the length of
1720 * ary1, with any shortcomings of ary2 replaced with null strings.
1722 * @param {Array} ary1
1723 * @param {Array} ary2
1726 zip: function zip(ary1, ary2) {
1728 for (let [i, item] in Iterator(ary1))
1729 res.push([item, i in ary2 ? ary2[i] : ""]);
1734 /* Make Minefield not explode, because Minefield exploding is not fun. */
1735 let iterProto = Iter.prototype;
1736 Object.keys(iter).forEach(function (k) {
1737 iterProto[k] = function () {
1738 let res = iter[k].apply(iter, [this].concat(Array.slice(arguments)));
1739 if (isinstance(res, ["Iterator", "Generator"]))
1745 Object.keys(array).forEach(function (k) {
1746 if (!(k in iterProto))
1747 iterProto[k] = function () {
1748 let res = array[k].apply(array, [this.toArray()].concat(Array.slice(arguments)));
1749 if (isinstance(res, ["Iterator", "Generator"]))
1757 Object.getOwnPropertyNames(Array.prototype).forEach(function (k) {
1758 if (!(k in iterProto) && callable(Array.prototype[k]))
1759 iterProto[k] = function () {
1760 let ary = iter(this).toArray();
1761 let res = ary[k].apply(ary, arguments);
1770 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1772 // vim: set fdm=marker sw=4 ts=4 et ft=javascript: