1 // Copyright (c) 2009-2014 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 var Cs = new Proxy(Components.stack, {
10 get: function Cs_get(target, prop) Components.stack.caller[prop]
13 function module(url) {
19 var { XPCOMUtils } = module("resource://gre/modules/XPCOMUtils.jsm");
21 var ctypes = module("resource://gre/modules/ctypes.jsm");
25 let objproto = Object.prototype;
26 let { __lookupGetter__, __lookupSetter__, __defineGetter__, __defineSetter__,
27 hasOwnProperty, propertyIsEnumerable } = objproto;
29 hasOwnProperty = Function.call.bind(hasOwnProperty);
30 propertyIsEnumerable = Function.call.bind(propertyIsEnumerable);
33 if (!("find" in Array.prototype))
34 Object.defineProperty(Array.prototype, "find", {
37 value: function Array_find(pred, self) {
38 for (let [i, elem] in Iterator(this))
39 if (pred.call(self, elem, i, this))
44 if (!("findIndex" in Array.prototype))
45 Object.defineProperty(Array.prototype, "findIndex", {
48 value: function Array_findIndex(pred, self) {
49 for (let [i, elem] in Iterator(this))
50 if (pred.call(self, elem, i, this))
56 function require(module_, target) {
57 if (/^[A-Za-z0-9]+:/.test(module_))
58 return module(module_);
59 return JSMLoader.load(module_, target);
62 function lazyRequire(module, names, target) {
63 for (let name of names)
64 memoize(target || this, name, name => require(module)[name]);
67 let jsmodules = { lazyRequire: lazyRequire };
68 jsmodules.jsmodules = jsmodules;
70 function toString() "[module-global " + this.NAME + "]";
76 function defineModule(name, params, module) {
81 module.EXPORTED_SYMBOLS = params.exports || [];
82 if (!~module.EXPORTED_SYMBOLS.indexOf("File"))
85 defineModule.loadLog.push("[Begin " + name + "]");
86 defineModule.prefix += " ";
88 for (let [, mod] in Iterator(params.require || []))
91 module._lastModule = currentModule;
92 currentModule = module;
93 module.startTime = Date.now();
96 defineModule.loadLog = [];
97 Object.defineProperty(defineModule.loadLog, "push", {
98 value: function (val) {
99 val = defineModule.prefix + val;
101 defineModule.dump(val + "\n");
102 this[this.length] = Date.now() + " " + val;
105 defineModule.prefix = "";
106 defineModule.dump = function dump_(...args) {
107 let msg = args.map(function (msg) {
108 if (loaded.util && typeof msg == "object")
109 msg = util.objectToString(msg);
112 dump(String.replace(msg, /\n?$/, "\n")
113 .replace(/^./gm, JSMLoader.name + ": $&"));
115 defineModule.modules = [];
116 defineModule.time = function time(major, minor, func, self, ...args) {
117 let time = Date.now();
118 if (typeof func !== "function")
122 var res = func.apply(self, args);
125 loaded.util && util.reportError(e);
128 JSMLoader.times.add(major, minor, Date.now() - time);
132 function endModule() {
133 defineModule.prefix = defineModule.prefix.slice(0, -2);
134 defineModule.loadLog.push("(End " + currentModule.NAME + ")");
136 loaded[currentModule.NAME] = 1;
137 require(currentModule.NAME, jsmodules);
138 currentModule = currentModule._lastModule;
141 function require_(obj, name, from, targetName) {
143 if (arguments.length === 1)
144 [obj, name] = [{}, obj];
146 let caller = Components.stack.caller;
149 defineModule.loadLog.push((from || "require") + ": loading " + name +
150 " into " + (targetName || obj.NAME || caller.filename + ":" + caller.lineNumber));
152 JSMLoader.load(name + ".jsm", obj);
154 if (!loaded[name] && obj != jsmodules)
155 JSMLoader.load(name + ".jsm", jsmodules);
160 defineModule.dump("loading " + String.quote(name + ".jsm") + "\n");
164 defineModule.dump(" " + (e.filename || e.fileName) + ":" + e.lineNumber + ": " + e + "\n");
168 defineModule("base", {
169 // sed -n 's/^(const|var|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt
223 this.lazyRequire("cache", ["cache"]);
224 this.lazyRequire("config", ["config"]);
225 this.lazyRequire("messages", ["_", "Messages"]);
226 this.lazyRequire("promises", ["Task", "promises"]);
227 this.lazyRequire("services", ["services"]);
228 this.lazyRequire("storage", ["File"]);
229 this.lazyRequire("util", ["FailedAssertion", "util"]);
232 literal.locations = {};
233 function literal(/* comment */) {
234 let { caller } = Components.stack;
235 while (caller && caller.language != 2)
236 caller = caller.caller;
238 let file = caller.filename.replace(/.* -> /, "");
239 let key = "literal:" + file + ":" + caller.lineNumber;
240 return cache.get(key, function() {
241 let source = literal.files[file] || File.readURL(file);
242 literal.files[file] = source;
244 let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" +
245 ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source);
251 * Iterates over the names of all of the top-level properties of an
252 * object or, if prototypes is given, all of the properties in the
253 * prototype chain below the top.
255 * @param {object} obj The object to inspect.
256 * @param {boolean} properties Whether to inspect the prototype chain
258 * @returns {Generator}
260 function prototype(obj)
261 obj.__proto__ || Object.getPrototypeOf(obj) ||
262 XPCNativeWrapper.unwrap(obj).__proto__ ||
263 Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj));
265 function properties(obj, prototypes) {
267 let seen = RealSet(["dactylPropertyNames"]);
270 if ("dactylPropertyNames" in obj && !prototypes)
271 for (let key in values(obj.dactylPropertyNames))
272 if (key in obj && !seen.add(key))
277 function props(obj) {
280 return Object.getOwnPropertyNames(obj);
283 if (e.result === Cr.NS_ERROR_FAILURE) {
284 // This is being thrown for PDF.js content windows,
286 let filter = function filter(prop) {
291 util.reportError("Filtering properties for " +
292 String.quote(obj) + ", " +
293 "error checking presence of " +
294 String.quote(prop) + ": " + e);
298 return array.uniq([k for (k in obj)].concat(
299 Object.getOwnPropertyNames(
300 XPCNativeWrapper.unwrap(obj))
309 for (; obj; obj = prototypes && prototype(obj)) {
310 for (let key of props(obj))
311 if (!prototypes || !seen.add(key) && obj != orig)
316 function iterOwnProperties(obj) {
317 for (let prop in properties(obj))
318 yield [prop, Object.getOwnPropertyDescriptor(obj, prop)];
321 function deprecated(alternative, fn) {
323 return Class.Property(iter(fn).map(([k, v]) => [k,
324 callable(v) ? deprecated(alternative, v)
329 func = callable(fn) ? fn
330 : function () this[fn].apply(this, arguments);
332 function deprecatedMethod() {
333 let obj = !this ? "" :
334 this.className ? this.className + "#" :
335 this.constructor.className ? this.constructor.className + "#" :
338 deprecated.warn(func,
339 obj + (fn.realName || fn.name || name || "").replace(/__/g, "."),
341 return func.apply(this, arguments);
344 deprecatedMethod.realName = func.name;
346 return callable(fn) ? deprecatedMethod : Class.Property({
347 get: function () deprecatedMethod,
348 init: function (prop) { name = prop; }
351 deprecated.warn = function warn(func, name, alternative, frame) {
352 if (!func.seenCaller)
353 func.seenCaller = RealSet([
354 "resource://dactyl/javascript.jsm",
355 "resource://dactyl/util.jsm"
358 if (!(loaded.util && util && loaded.config && config.protocolLoaded)) {
359 dump("DACTYL: deprecated method called too early [" + [name, alternative] + "]:\n" + Error().stack + "\n\n");
363 frame = frame || Components.stack.caller.caller;
365 let filename = util.fixURI(frame.filename || "unknown");
366 if (!func.seenCaller.add(filename))
367 util.dactyl(func).warn([util.urlPath(filename), frame.lineNumber, " "].join(":")
368 + _("warn.deprecated", name, alternative));
372 * Iterates over all of the top-level, iterable property names of an
375 * @param {object} obj The object to inspect.
376 * @returns {Generator}
378 function keys(obj) iter(function keys() {
379 if (isinstance(obj, ["Map"]))
380 for (let [k, v] of obj)
384 if (hasOwnProperty(obj, k))
389 * Iterates over all of the top-level, iterable property values of an
392 * @param {object} obj The object to inspect.
393 * @returns {Generator}
395 function values(obj) iter(function values() {
396 if (isinstance(obj, ["Map"]))
397 for (let [k, v] of obj)
399 else if (isinstance(obj, ["Generator", "Iterator", Iter]))
402 else if (iter.iteratorProp in obj)
407 if (hasOwnProperty(obj, k))
411 var forEach = deprecated("iter.forEach", function forEach() iter.forEach.apply(iter, arguments));
412 var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments));
415 let Set_add = RealSet.prototype.add;
416 RealSet.prototype.add = function RealSet_add(val) {
417 let res = this.has(val);
418 Set_add.apply(this, arguments);
422 RealSet.prototype.difference = function RealSet_difference(set) {
423 return RealSet(i for (i of this) if (!set.has(i)));
426 RealSet.prototype.intersection = function RealSet_intersection(set) {
427 return RealSet(i for (i of this) if (set.has(i)));
430 RealSet.prototype.union = function RealSet_union(set) {
431 let res = RealSet(this);
432 for (let item of set)
438 * Utility for managing sets of strings. Given an array, returns an
439 * object with one key for each value thereof.
441 * @param {[string]} ary @optional
444 this.Set = deprecated("RealSet", function Set(ary) {
447 for (let val in values(ary))
452 * Adds an element to a set and returns true if the element was
453 * previously contained.
455 * @param {object} set The set.
456 * @param {string} key The key to add.
459 Set.add = deprecated("RealSet#add",
460 curry(function Set__add(set, key) {
461 if (isinstance(set, ["Set"])) {
462 let res = set.has(key);
467 let res = this.has(set, key);
472 * Returns true if the given set contains the given key.
474 * @param {object} set The set.
475 * @param {string} key The key to check.
478 Set.has = deprecated("hasOwnProperty or Set#has",
479 curry(function Set__has(set, key) {
480 if (isinstance(set, ["Set"]))
483 return hasOwnProperty(set, key) &&
484 propertyIsEnumerable(set, key);
487 * Returns a new set containing the members of the first argument which
488 * do not exist in any of the other given arguments.
490 * @param {object} set The set.
493 Set.subtract = deprecated("RealSet#difference",
494 function set_subtract(set) {
495 set = update({}, set);
496 for (let i = 1; i < arguments.length; i++)
497 for (let k in keys(arguments[i]))
503 * Removes an element from a set and returns true if the element was
504 * previously contained.
506 * @param {object} set The set.
507 * @param {string} key The key to remove.
510 Set.remove = deprecated("RealSet#delete",
511 curry(function Set__remove(set, key) {
512 if (isinstance(set, ["Set"]))
513 return set.delete(key);
515 let res = set.has(set, key);
521 deprecated.warn(set, "set", "Set");
522 return Set.apply(this, arguments);
524 Object.keys(Set).forEach(function (meth) {
525 set[meth] = function proxy() {
526 deprecated.warn(proxy, "set." + meth, "Set." + meth);
527 return Set[meth].apply(Set, arguments);
532 * Curries a function to the given number of arguments. Each
533 * call of the resulting function returns a new function. When
534 * a call does not contain enough arguments to satisfy the
535 * required number, the resulting function is another curried
536 * function with previous arguments accumulated.
538 * function foo(a, b, c) [a, b, c].join(" ");
539 * curry(foo)(1, 2, 3) -> "1 2 3";
540 * curry(foo)(4)(5, 6) -> "4 5 6";
541 * curry(foo)(7)(8)(9) -> "7 8 9";
543 * @param {function} fn The function to curry.
544 * @param {integer} length The number of arguments expected.
547 * @param {object} self The 'this' value for the returned function. When
548 * omitted, the value of 'this' from the first call to the function is
552 function curry(fn, length, self, acc) {
558 // Close over function with 'this'
559 function close(self, fn) (...args) => fn.apply(self, args);
564 function curried(...args) {
565 // The curried result should preserve 'this'
566 if (args.length == 0)
567 return close(self || this, curried);
569 let args = acc.concat(args);
571 if (args.length >= length)
572 return fn.apply(self || this, args);
574 return curry(fn, length, self || this, args);
576 curried.realName = fn.realName || fn.name;
580 var bind = function bind(meth, self, ...args)
581 let (func = callable(meth) ? meth : self[meth])
582 func.bind.apply(func, [self].concat(args));
585 * Returns true if both arguments are functions and
586 * (targ() instanceof src) would also return true.
588 * @param {function} targ
589 * @param {function} src
592 function isSubclass(targ, src) {
593 return src === targ ||
594 targ && typeof targ === "function" && targ.prototype instanceof src;
598 * Returns true if *object* is an instance of *interfaces*. If *interfaces* is
599 * an array, returns true if *object* is an instance of any element of
600 * *interfaces*. If *interfaces* is the object form of a primitive type,
601 * returns true if *object* is a non-boxed version of the type, i.e., if
602 * (typeof object == "string"), isinstance(object, String) is true. Finally, if
603 * *interfaces* is a string, returns true if ({}.toString.call(object) ==
604 * "[object <interfaces>]").
606 * @param {object} object The object to check.
607 * @param {constructor|[constructor|string]} interfaces The types to check *object* against.
610 var isinstance_types = {
616 function isinstance(object, interfaces) {
620 return Array.concat(interfaces).some(function isinstance_some(iface) {
621 if (typeof iface === "string") {
622 if (objproto.toString.call(object) === "[object " + iface + "]")
625 else if (typeof object === "object" && "isinstance" in object && object.isinstance !== isinstance) {
626 if (object.isinstance(iface))
630 if (object instanceof iface)
632 var type = isinstance_types[typeof object];
633 if (type && isSubclass(iface, type))
641 * Returns true if obj is a non-null object.
643 function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
646 * Returns true if and only if its sole argument is an
647 * instance of the builtin Array type. The array may come from
648 * any window, frame, namespace, or execution context, which
649 * is not the case when using (obj instanceof Array).
652 // This is bloody stupid.
653 function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array";
656 * Returns true if and only if its sole argument is an
657 * instance of the builtin Generator type. This includes
658 * functions containing the 'yield' statement and generator
659 * statements such as (x for (x in obj)).
661 function isGenerator(val) objproto.toString.call(val) == "[object Generator]";
664 * Returns true if and only if its sole argument is a String,
665 * as defined by the builtin type. May be constructed via
666 * String(foo) or new String(foo) from any window, frame,
667 * namespace, or execution context, which is not the case when
668 * using (obj instanceof String) or (typeof obj == "string").
670 function isString(val) objproto.toString.call(val) == "[object String]";
673 * Returns true if and only if its sole argument may be called
674 * as a function. This includes classes and function objects.
676 function callable(val) typeof val === "function" && !(val instanceof Ci.nsIDOMElement);
678 function call(fn, self, ...args) {
679 fn.apply(self, args);
684 * Memoizes an object property value.
686 * @param {object} obj The object to add the property to.
687 * @param {string} key The property name.
688 * @param {function} getter The function which will return the initial
689 * value of the property.
691 function memoize(obj, key, getter) {
692 if (arguments.length == 1) {
693 let res = update(Object.create(obj), obj);
694 for (let prop of Object.getOwnPropertyNames(obj)) {
695 let get = __lookupGetter__.call(obj, prop);
697 memoize(res, prop, get);
703 Object.defineProperty(obj, key, {
707 get: function g_replaceProperty() {
709 Class.replaceProperty(this.instance || this, key, null);
710 return Class.replaceProperty(this.instance || this, key, getter.call(this, key));
717 set: function s_replaceProperty(val)
718 Class.replaceProperty(this.instance || this, key, val)
722 obj[key] = getter.call(obj, key);
726 let sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance());
727 sandbox.__proto__ = this;
730 * Updates an object with the properties of another object. Getters
731 * and setters are copied as expected. Moreover, any function
732 * properties receive new 'supercall' and 'superapply' properties,
733 * which will call the identically named function in target's
736 * let a = { foo: function (arg) "bar " + arg }
737 * let b = { __proto__: a }
738 * update(b, { foo: function foo() foo.supercall(this, "baz") });
740 * a.foo("foo") -> "bar foo"
741 * b.foo() -> "bar baz"
743 * @param {Object} target The object to update.
744 * @param {Object} src The source object from which to update target.
745 * May be provided multiple times.
746 * @returns {Object} Returns its updated first argument.
748 function update(target) {
749 for (let i = 1; i < arguments.length; i++) {
750 let src = arguments[i];
751 Object.getOwnPropertyNames(src || {}).forEach(function (k) {
752 let desc = Object.getOwnPropertyDescriptor(src, k);
753 if (desc.value instanceof Class.Property)
754 desc = desc.value.init(k, target) || desc.value;
757 if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) {
758 let func = desc.value.wrapped || desc.value;
759 if (!func.superapply) {
760 func.__defineGetter__("super", function get_super() Object.getPrototypeOf(target)[k]);
761 func.superapply = function superapply(self, args)
762 let (meth = Object.getPrototypeOf(target)[k])
763 meth && meth.apply(self, args);
764 func.supercall = function supercall(self, ...args)
765 func.superapply(self, args);
768 Object.defineProperty(target, k, desc);
779 * Constructs a new Class. Arguments marked as optional must be
780 * either entirely elided, or they must have the exact type
783 * @param {string} name The class's as it will appear when toString
784 * is called, as well as in stack traces.
786 * @param {function} base The base class for this module. May be any
790 * @param {Object} prototype The prototype for instances of this
791 * object. The object itself is copied and not used as a prototype
793 * @param {Object} classProperties The class properties for the new
794 * module constructor. More than one may be provided.
797 * @returns {function} The constructor for the resulting class.
799 function Class(...args) {
801 if (isString(args[0]))
802 var name = args.shift();
803 var superclass = Class;
804 if (callable(args[0]))
805 superclass = args.shift();
807 var Constructor = eval(String.replace('\n\
808 (function constructor(PARAMS) { \n\
809 var self = Object.create(Constructor.prototype); \n\
810 self.instance = self; \n\
811 self.globalInstance = self; \n\
813 if ("_metaInit_" in self && self._metaInit_) \n\
814 self._metaInit_.apply(self, arguments); \n\
816 var res = self.init.apply(self, arguments); \n\
817 return res !== undefined ? res : self; \n\
819 "constructor", (name || superclass.className).replace(/\W/g, "_"))
821 /^function .*?\((.*?)\)/
822 .exec(args[0] && args[0].init || Class.prototype.init)[1]
823 .replace(/\b(self|res|Constructor)\b/g, "$1_")));
825 Constructor.className = name || superclass.className || superclass.name;
827 if ("init" in superclass.prototype)
828 Constructor.__proto__ = superclass;
830 let superc = superclass;
831 superclass = function Shim() {};
832 Class.extend(superclass, superc, {
835 superclass.__proto__ = superc;
838 Class.extend(Constructor, superclass, args[0]);
839 memoize(Constructor, "bound", Class.makeClosure);
840 if (Iter && array) // Hack. :/
841 Object.defineProperty(Constructor, "closure",
842 deprecated("bound", { get: function closure() this.bound }));
843 update(Constructor, args[1]);
845 Constructor.__proto__ = superclass;
847 args.slice(2).forEach(function (obj) {
850 update(Constructor.prototype, obj);
855 Class.objectGlobal = function (object) {
857 return Cu.getGlobalForObject(object);
865 * @class Class.Property
866 * A class which, when assigned to a property in a Class's prototype
867 * or class property object, defines that property's descriptor
868 * rather than its value. If the desc object has an init property, it
869 * will be called with the property's name before the descriptor is
872 * @param {Object} desc The property descriptor.
874 Class.Property = function Property(desc) update(
875 Object.create(Property.prototype), desc || { configurable: true, writable: true });
876 Class.Property.prototype.init = function () {};
878 * Extends a subclass with a superclass. The subclass's
879 * prototype is replaced with a new object, which inherits
880 * from the superclass's prototype, {@see update}d with the
881 * members of *overrides*.
883 * @param {function} subclass
884 * @param {function} superclass
885 * @param {Object} overrides @optional
887 Class.extend = function extend(subclass, superclass, overrides) {
888 subclass.superclass = superclass;
890 subclass.prototype = Object.create(superclass.prototype);
891 update(subclass.prototype, overrides);
892 subclass.prototype.constructor = subclass;
893 subclass.prototype._class_ = subclass;
895 if (superclass.prototype.constructor === objproto.constructor)
896 superclass.prototype.constructor = superclass;
900 * Memoizes the value of a class property to the value returned by
901 * the passed function the first time the property is accessed.
903 * @param {function(string)} getter The function which returns the
905 * @returns {Class.Property}
907 Class.Memoize = function Memoize(getter, wait)
911 init: function (key) {
915 // Crazy, yeah, I know. -- Kris
916 this.get = function replace() {
917 let obj = this.instance || this;
918 Object.defineProperty(obj, key, {
919 configurable: true, enumerable: false,
920 get: function get() {
921 util.waitFor(() => done);
926 Task.spawn(function () {
928 for (var res in getter.call(obj)) {
929 if (wait !== undefined)
930 yield promises.sleep(wait);
933 Class.replaceProperty(obj, key, res);
940 this.get = function g_Memoize() {
941 let obj = this.instance || this;
943 Class.replaceProperty(obj, key, null);
944 return Class.replaceProperty(obj, key, getter.call(this, key));
951 this.set = function s_Memoize(val) Class.replaceProperty(this.instance || this, key, val);
955 Class.memoize = deprecated("Class.Memoize", function memoize() Class.Memoize.apply(this, arguments));
958 * Updates the given object with the object in the target class's
961 Class.Update = function Update(obj)
966 init: function (key, target) {
967 this.value = update({}, target[key], obj);
971 Class.replaceProperty = function replaceProperty(obj, prop, value) {
972 Object.defineProperty(obj, prop, { configurable: true, enumerable: true, value: value, writable: true });
975 Class.toString = function toString() "[class " + this.className + "]";
978 * Initializes new instances of this class. Called automatically
979 * when new instances are created.
981 init: function c_init() {},
984 set instance(val) Class.replaceProperty(this, "instance", val),
986 withSavedValues: function withSavedValues(names, callback, self) {
987 let vals = names.map(name => this[name]);
989 return callback.call(self || this);
992 names.forEach((name, i) => { this[name] = vals[i]; });
996 toString: function C_toString() {
997 if (this.toStringParams)
998 var params = "(" + this.toStringParams.map(m => (isArray(m) ? "[" + m + "]" :
999 isString(m) ? m.quote() : String(m)))
1001 return "[instance " + this.constructor.className + (params || "") + "]";
1005 * Executes *callback* after *timeout* milliseconds. The value of
1006 * 'this' is preserved in the invocation of *callback*.
1008 * @param {function} callback The function to call after *timeout*
1009 * @param {number} timeout The time, in milliseconds, to wait
1010 * before calling *callback*.
1011 * @returns {nsITimer} The timer which backs this timeout.
1013 timeout: function timeout(callback, timeout) {
1014 let timeout_notify = (timer) => {
1016 util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"]))
1018 this.timeouts.splice(this.timeouts.indexOf(timer), 1);
1020 callback.call(this);
1024 util.dump("Error invoking timer callback registered at " +
1025 [frame.filename, frame.lineNumber, ""].join(":"));
1026 util.reportError(e);
1033 let frame = Components.stack.caller;
1034 let timer = services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
1035 this.timeouts.push(timer);
1041 * Updates this instance with the properties of the given objects.
1042 * Like the update function, but with special semantics for
1043 * localized properties.
1045 update: function update() {
1046 // XXX: Duplication.
1048 for (let i = 0; i < arguments.length; i++) {
1049 let src = arguments[i];
1050 Object.getOwnPropertyNames(src || {}).forEach((k) => {
1051 let desc = Object.getOwnPropertyDescriptor(src, k);
1052 if (desc.value instanceof Class.Property)
1053 desc = desc.value.init(k, this) || desc.value;
1055 if (typeof desc.value === "function") {
1056 let func = desc.value.wrapped || desc.value;
1057 if (!func.superapply) {
1058 func.__defineGetter__("super", () => Object.getPrototypeOf(this)[k]);
1060 func.superapply = function superapply(self, args) {
1061 let meth = Object.getPrototypeOf(self)[k];
1062 return meth && meth.apply(self, args);
1065 func.supercall = function supercall(self, ...args) {
1066 return func.superapply(self, args);
1072 if ("value" in desc && (this.localizedProperties.has(k) || this.magicalProperties.has(k)))
1073 this[k] = desc.value;
1075 Object.defineProperty(this, k, desc);
1083 localizedProperties: RealSet(),
1084 magicalProperties: RealSet()
1086 for (let name in properties(Class.prototype)) {
1087 let desc = Object.getOwnPropertyDescriptor(Class.prototype, name);
1088 desc.enumerable = false;
1089 Object.defineProperty(Class.prototype, name, desc);
1092 var closureHooks = {
1093 get: function closure_get(target, prop) {
1094 if (hasOwnProperty(target._closureCache, prop))
1095 return target._closureCache[prop];
1097 let p = target[prop]
1099 return target._closureCache[prop] = p.bind(target);
1104 getOwnPropertyNames: function getOwnPropertyNames(target) {
1105 return [k for (k in properties(target, true))];
1108 getOwnPropertyDescriptor: function getOwnPropertyDescriptor(target, prop) {
1111 configurable: false,
1113 get value() self.get(target, prop)
1119 Class.makeClosure = function makeClosure() {
1120 this._closureCache = {};
1122 return new Proxy(this, closureHooks);
1124 memoize(Class.prototype, "bound", Class.makeClosure);
1127 * A base class generator for classes which implement XPCOM interfaces.
1129 * @param {nsIIID|[nsIJSIID]} interfaces The interfaces which the class
1131 * @param {Class} superClass A super class. @optional
1134 function XPCOM(interfaces, superClass) {
1135 interfaces = Array.concat(interfaces);
1137 let shim = XPCOMShim(interfaces);
1139 let res = Class("XPCOM(" + interfaces + ")", superClass || Class,
1141 v === undefined || callable(v) ? stub : v]
1142 for ([k, v] in Iterator(shim))).toObject(),
1143 { QueryInterface: XPCOMUtils.generateQI(interfaces) }));
1147 function XPCOMShim(interfaces) {
1148 let ip = services.InterfacePointer({
1149 QueryInterface: function (iid) {
1150 if (iid.equals(Ci.nsISecurityCheckedComponent))
1151 throw Cr.NS_ERROR_NO_INTERFACE;
1154 getHelperForLanguage: function () null,
1155 getInterfaces: function (count) { count.value = 0; }
1157 return (interfaces || []).reduce((shim, iface) => shim.QueryInterface(Ci[iface]),
1160 let stub = Class.Property({
1163 value: function stub() null,
1168 * An abstract base class for classes that wish to inherit from Error.
1170 var ErrorBase = Class("ErrorBase", Error, {
1172 init: function EB_init(message, level=0) {
1173 let error = Error(message);
1174 update(this, error);
1175 this.stack = error.stack;
1176 this.message = message;
1178 let frame = Components.stack;
1179 for (let i = 0; i < this.level + level; i++) {
1180 frame = frame.caller;
1181 this.stack = this.stack.replace(/^.*\n/, "");
1183 this.fileName = frame.filename;
1184 this.lineNumber = frame.lineNumber;
1186 toString: function () String(this.message)
1190 * An Error subclass to throw in order to stop sourcing a plugin without
1191 * printing a stack trace.
1193 var Finished = Class("Finished", ErrorBase);
1196 * Constructs a new Module class and instantiates an instance into the current
1197 * module global object.
1199 * @param {string} name The name of the instance.
1200 * @param {Object} prototype The instance prototype.
1201 * @param {Object} classProperties Properties to be applied to the class constructor.
1204 function Module(name, prototype, ...args) {
1206 let init = callable(prototype) ? 2 : 1;
1207 let proto = callable(prototype) ? args[0] : prototype;
1209 proto._metaInit_ = function () {
1210 module.prototype._metaInit_ = null;
1211 currentModule[name.toLowerCase()] = this;
1214 const module = Class.apply(Class, [name, prototype, ...args.slice(0, init)]);
1215 let instance = module();
1216 module.className = name.toLowerCase();
1218 instance.INIT = update(Object.create(Module.INIT),
1221 currentModule[module.className] = instance;
1222 defineModule.modules.push(instance);
1226 if (typeof e === "string")
1229 dump(e.fileName + ":" + e.lineNumber + ": " + e + "\n" + (e.stack || Error().stack));
1233 init: function Module_INIT_init(dactyl, modules, window) {
1234 let args = arguments;
1237 for (let local = this.Local; local; local = local.super)
1240 if (locals.length) {
1241 let module = this, objs = {};
1242 for (let i in locals) {
1243 module = objs[i] = Object.create(module);
1244 module.modules = modules;
1246 module.isLocalModule = true;
1248 modules.jsmodules[this.constructor.className] = module;
1249 locals.reverse().forEach((fn, i) => { update(objs[i], fn.apply(module, args)); });
1251 memoize(module, "closure", Class.makeClosure);
1252 module.instance = module;
1256 modules.dactyl.registerObservers(module);
1264 * Creates a new Struct constructor, used for creating objects with
1265 * a fixed set of named members. Each argument should be the name of
1266 * a member in the resulting objects. These names will correspond to
1267 * the arguments passed to the resultant constructor. Instances of
1268 * the new struct may be treated very much like arrays, and provide
1269 * many of the same methods.
1271 * const Point = Struct("x", "y", "z");
1272 * let p1 = Point(x, y, z);
1274 * @returns {function} The constructor for the new Struct.
1276 function Struct(...args) {
1277 if (/^[A-Z]/.test(args[0]))
1278 var className = args.shift();
1280 const Struct = Class(className || "Struct", StructBase, {
1281 length: args.length,
1282 members: array.toObject(args.map((v, k) => [v, k]))
1284 args.forEach(function (name, i) {
1285 Struct.prototype.__defineGetter__(name, function () this[i]);
1286 Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1290 var StructBase = Class("StructBase", Array, {
1291 init: function struct_init() {
1292 for (let i = 0; i < arguments.length; i++)
1293 if (arguments[i] != undefined)
1294 this[i] = arguments[i];
1297 get toStringParams() this,
1299 clone: function struct_clone() this.constructor.apply(null, this.slice()),
1301 bound: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "bound")),
1302 closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1304 get: function struct_get(key, val) this[this.members[key]],
1305 set: function struct_set(key, val) this[this.members[key]] = val,
1307 toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1309 // Iterator over our named members
1310 __iterator__: function struct__iterator__() {
1312 return ([k, self[k]] for (k in keys(self.members)))
1315 fromArray: function fromArray(ary) {
1316 if (!(ary instanceof this))
1317 ary.__proto__ = this.prototype;
1322 * Sets a lazily constructed default value for a member of
1323 * the struct. The value is constructed once, the first time
1324 * it is accessed and memoized thereafter.
1326 * @param {string} key The name of the member for which to
1327 * provide the default value.
1328 * @param {function} val The function which is to generate
1329 * the default value.
1331 defaultValue: function defaultValue(key, val) {
1332 let i = this.prototype.members[key];
1333 this.prototype.__defineGetter__(i, function () (this[i] = val.call(this)));
1334 this.prototype.__defineSetter__(i, function (value)
1335 Class.replaceProperty(this, i, value));
1339 localize: function localize(key, defaultValue) {
1340 let i = this.prototype.members[key];
1341 Object.defineProperty(this.prototype, i, Messages.Localized(defaultValue).init(key, this.prototype));
1346 var Timer = Class("Timer", {
1347 init: function init(minInterval, maxInterval, callback, self=this) {
1348 this._timer = services.Timer();
1349 this.callback = callback;
1351 this.minInterval = minInterval;
1352 this.maxInterval = maxInterval;
1357 notify: function notify(timer, force) {
1359 if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1362 this._timer.cancel();
1364 // minInterval is the time between the completion of the command and the next firing
1365 this.doneAt = Date.now() + this.minInterval;
1367 this.callback.call(this.self, this.arg);
1370 if (typeof util === "undefined")
1371 dump(JSMLoader.name + ": " + e + "\n" + (e.stack || Error().stack));
1373 util.reportError(e);
1376 this.doneAt = Date.now() + this.minInterval;
1380 tell: function tell(arg) {
1381 if (arguments.length > 0)
1384 let now = Date.now();
1385 if (this.doneAt == -1)
1386 this._timer.cancel();
1388 let timeout = this.minInterval;
1389 if (now > this.doneAt && this.doneAt > -1)
1391 else if (this.latest)
1392 timeout = Math.min(timeout, this.latest - now);
1394 this.latest = now + this.maxInterval;
1396 this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1400 reset: function reset() {
1401 this._timer.cancel();
1405 flush: function flush(force) {
1406 if (this.doneAt == -1 || force)
1407 this.notify(null, true);
1412 * Idempotent function which returns the UTF-8 encoded value of an
1413 * improperly-decoded string.
1415 * @param {string} str
1418 function UTF8(str) {
1420 return decodeURIComponent(escape(str));
1427 function octal(decimal) parseInt(decimal, 8);
1430 * Iterates over an arbitrary object. The following iterator types are
1431 * supported, and work as a user would expect:
1433 * • nsIDOMNodeIterator
1434 * • mozIStorageStatement
1436 * Additionally, the following array-like objects yield a tuple of the
1437 * form [index, element] for each contained element:
1439 * • nsIDOMHTMLCollection
1440 * • nsIDOMNodeList
1442 * and the following likewise yield one element of the form
1443 * [name, element] for each contained element:
1445 * • nsIDOMNamedNodeMap
1447 * Duck typing is implemented for any other type. If the object
1448 * contains the "enumerator" property, iter is called on that. If the
1449 * property is a function, it is called first. If it contains the
1450 * property "getNext" along with either "hasMoreItems" or "hasMore", it
1451 * is iterated over appropriately.
1453 * For all other cases, this function behaves exactly like the Iterator
1456 * @param {object} obj
1457 * @param {nsIJSIID} iface The interface to which to query all elements.
1458 * @returns {Generator}
1460 iter.iteratorProp = "@@iterator" in [] ? "@@iterator" : "iterator";
1461 function iter(obj, iface) {
1462 if (arguments.length == 2 && iface instanceof Ci.nsIJSIID)
1463 return iter(obj).map(item => item.QueryInterface(iface));
1465 let args = arguments;
1466 let res = Iterator(obj);
1468 if (args.length > 1)
1469 res = (function () {
1470 for (let i = 0; i < args.length; i++)
1471 for (let j in iter(args[i]))
1474 else if (isinstance(obj, ["Iterator", "Generator", "Array"]))
1476 else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
1477 res = array.iterItems(obj);
1478 else if (iter.iteratorProp in obj && callable(obj[iter.iteratorProp]) && !("__iterator__" in obj))
1479 res = (x for (x of obj));
1480 else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1481 while (obj.constructor instanceof ctypes.PointerType)
1483 if (obj.constructor instanceof ctypes.ArrayType)
1484 res = array.iterItems(obj);
1485 else if (obj.constructor instanceof ctypes.StructType)
1486 res = (function () {
1487 for (let prop in values(obj.constructor.fields))
1488 yield let ([name, type] = Iterator(prop).next()) [name, obj[name]];
1493 else if (Ci.nsIDOMNamedNodeMap && obj instanceof Ci.nsIDOMNamedNodeMap ||
1494 Ci.nsIDOMMozNamedAttrMap && obj instanceof Ci.nsIDOMMozNamedAttrMap)
1495 res = (function () {
1496 for (let i = 0; i < obj.length; i++)
1497 yield [obj.name, obj];
1499 else if (obj instanceof Ci.mozIStorageStatement)
1500 res = (function (obj) {
1501 while (obj.executeStep())
1505 else if ("getNext" in obj) {
1506 if ("hasMoreElements" in obj)
1507 res = (function () {
1508 while (obj.hasMoreElements())
1509 yield obj.getNext();
1511 else if ("hasMore" in obj)
1512 res = (function () {
1513 while (obj.hasMore())
1514 yield obj.getNext();
1517 else if ("enumerator" in obj) {
1518 if (callable(obj.enumerator))
1519 return iter(obj.enumerator());
1520 return iter(obj.enumerator);
1525 toArray: function toArray(iter) array(iter).array,
1527 // See array.prototype for API docs.
1528 toObject: function toObject(iter) {
1530 for (let [k, v] in iter)
1531 if (v instanceof Class.Property)
1532 Object.defineProperty(obj, k, v.init(k, obj) || v);
1538 compact: function compact(iter) (item for (item in iter) if (item != null)),
1540 every: function every(iter, pred, self) {
1541 pred = pred || util.identity;
1542 for (let elem of iter)
1543 if (!pred.call(self, elem))
1547 some: function every(iter, pred, self) {
1548 pred = pred || util.identity;
1549 for (let elem of iter)
1550 if (pred.call(self, elem))
1555 filter: function filter(iter, pred, self) {
1556 for (let elem of iter)
1557 if (pred.call(self, elem))
1562 * Iterates over an iterable object and calls a callback for each
1565 * @param {object} iter The iterator.
1566 * @param {function} fn The callback.
1567 * @param {object} self The this object for *fn*.
1569 forEach: function forEach(iter, func, self) {
1570 for (let val of iter)
1571 func.call(self, val);
1574 indexOf: function indexOf(iter, elem) {
1576 for (let item of iter) {
1584 * Returns the array that results from applying *func* to each property of
1587 * @param {Object} obj
1588 * @param {function} func
1591 map: function map(iter, func, self) {
1593 yield func.call(self, i);
1597 * Returns the nth member of the given array that matches the
1600 nth: function nth(iter, pred, n, self) {
1601 if (typeof pred === "number")
1602 [pred, n] = [() => true, pred]; // Hack.
1604 for (let elem of iter)
1605 if (pred.call(self, elem) && n-- === 0)
1611 * Analog of Array.find method. Returns the first item in the
1612 * iterator for which `pred` returns true.
1614 find: function find(iter, pred, self) {
1615 for (let elem of iter)
1616 if (pred.call(self, elem))
1621 sort: function sort(iter, fn, self)
1622 array(this.toArray(iter).sort(fn, self)),
1624 uniq: function uniq(iter) {
1625 let seen = RealSet();
1626 for (let item of iter)
1627 if (!seen.add(item))
1632 * Zips the contents of two arrays. The resulting array is the length of
1633 * ary1, with any shortcomings of ary2 replaced with null strings.
1635 * @param {Array} ary1
1636 * @param {Array} ary2
1639 zip: function zip(iter1, iter2) {
1641 yield [iter1.next(), iter2.next()];
1643 catch (e if e instanceof StopIteration) {}
1647 const Iter = Class("Iter", {
1648 init: function init(iter) {
1650 if ("__iterator__" in iter)
1651 this.iter = iter.__iterator__();
1653 if (this.iter.finalize)
1654 this.finalize = function finalize() this.iter.finalize.apply(this.iter, arguments);
1657 next: function next() this.iter.next(),
1659 send: function send() this.iter.send.apply(this.iter, arguments),
1661 __iterator__: function () this.iter
1665 function arrayWrap(fn) {
1666 function wrapper() {
1667 let res = fn.apply(this, arguments);
1670 if (isinstance(res, ["Iterator", "Generator"]))
1674 wrapper.wrapped = fn;
1679 * Array utility methods.
1681 var array = Class("array", Array, {
1682 init: function (ary) {
1683 if (isinstance(ary, ["Iterator", "Generator"]) || "__iterator__" in ary)
1684 ary = [k for (k in ary)];
1685 else if (ary.length)
1686 ary = Array.slice(ary);
1689 return new Proxy(ary, {
1690 get: function array_get(target, prop) {
1691 if (prop in array && callable(array[prop]))
1692 return arrayWrap(array[prop].bind(array, target));
1694 if (prop == "array")
1697 let p = target[prop];
1698 if (!/^\d+$/.test(prop) &&
1699 prop != "toString" &&
1700 prop != "toSource" &&
1702 return arrayWrap(p);
1710 * Converts an array to an object. As in lisp, an assoc is an
1711 * array of key-value pairs, which maps directly to an object,
1713 * [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1715 * @param {[Array]} assoc
1716 * @... {string} 0 - Key
1719 toObject: function toObject(assoc) {
1721 assoc.forEach(function ([k, v]) {
1722 if (v instanceof Class.Property)
1723 Object.defineProperty(obj, k, v.init(k, obj) || v);
1731 * Compacts an array, removing all elements that are null or undefined:
1732 * ["foo", null, "bar", undefined] -> ["foo", "bar"]
1734 * @param {Array} ary
1737 compact: function compact(ary) ary.filter(item => item != null),
1740 * Returns true if each element of ary1 is equal to the
1741 * corresponding element in ary2.
1743 * @param {Array} ary1
1744 * @param {Array} ary2
1745 * @returns {boolean}
1747 equals: function (ary1, ary2)
1748 ary1.length === ary2.length && Array.every(ary1, (e, i) => e === ary2[i]),
1751 * Flattens an array, such that all elements of the array are
1752 * joined into a single array:
1753 * [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
1755 * @param {Array} ary
1758 flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1761 * Returns an Iterator for an array's values.
1763 * @param {Array} ary
1764 * @returns {Iterator(Object)}
1766 iterValues: function iterValues(ary) {
1767 for (let i = 0; i < ary.length; i++)
1772 * Returns an Iterator for an array's indices and values.
1774 * @param {Array} ary
1775 * @returns {Iterator([{number}, {Object}])}
1777 iterItems: function iterItems(ary) {
1778 let length = ary.length;
1779 for (let i = 0; i < length; i++)
1784 * Returns the nth member of the given array that matches the
1787 nth: function nth(ary, pred, n, self) {
1788 for (let elem in values(ary))
1789 if (pred.call(self, elem) && n-- === 0)
1795 * Filters out all duplicates from an array. If *unsorted* is false, the
1796 * array is sorted before duplicates are removed.
1798 * @param {Array} ary
1799 * @param {boolean} unsorted
1802 uniq: function uniq(ary, unsorted) {
1805 for (let item in values(ary))
1806 if (res.indexOf(item) == -1)
1810 for (let [, item] in Iterator(ary.sort())) {
1811 if (item != last || !res.length)
1820 * Zips the contents of two arrays. The resulting array is the length of
1821 * ary1, with any shortcomings of ary2 replaced with null strings.
1823 * @param {Array} ary1
1824 * @param {Array} ary2
1827 zip: function zip(ary1, ary2) {
1829 for (let [i, item] in Iterator(ary1))
1830 res.push([item, i in ary2 ? ary2[i] : ""]);
1835 /* Make Minefield not explode, because Minefield exploding is not fun. */
1836 let iterProto = Iter.prototype;
1837 Object.keys(iter).forEach(function (k) {
1838 iterProto[k] = function (...args) {
1839 let res = iter[k].apply(iter, [this].concat(args));
1840 if (isinstance(res, ["Iterator", "Generator"]))
1846 Object.keys(array).forEach(function (k) {
1847 if (!(k in iterProto))
1848 iterProto[k] = function (...args) {
1849 let res = array[k].apply(array, [this.toArray()].concat(args));
1850 if (isinstance(res, ["Iterator", "Generator"]))
1858 Object.getOwnPropertyNames(Array.prototype).forEach(function (k) {
1859 if (!(k in iterProto) && callable(Array.prototype[k]))
1860 iterProto[k] = function () {
1861 let ary = iter(this).toArray();
1862 let res = ary[k].apply(ary, arguments);
1869 Object.defineProperty(Class.prototype, "closure",
1870 deprecated("bound", { get: function closure() this.bound }));
1874 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1876 // vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: