X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=common%2Fmodules%2Fbase.jsm;h=a9318eb414119a78cb118446827cf1f377a93379;hb=3d837eb266a3a01d424192aa4ec1a167366178c5;hp=d07ba1230732b78e0f69eb944b84e8017c91f3fc;hpb=70740024f9c028c1fd63e1a1850ab062ff956054;p=dactyl.git diff --git a/common/modules/base.jsm b/common/modules/base.jsm index d07ba12..a9318eb 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -1,127 +1,38 @@ -// Copyright (c) 2009-2011 by Kris Maglione +// Copyright (c) 2009-2013 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. "use strict"; -var Cc = Components.classes; -var Ci = Components.interfaces; -var Cr = Components.results; -var Cu = Components.utils; +var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); try { var ctypes; - Components.utils.import("resource://gre/modules/ctypes.jsm"); + Cu.import("resource://gre/modules/ctypes.jsm"); } catch (e) {} let objproto = Object.prototype; -let { __lookupGetter__, __lookupSetter__, hasOwnProperty, propertyIsEnumerable } = objproto; +let { __lookupGetter__, __lookupSetter__, __defineGetter__, __defineSetter__, + hasOwnProperty, propertyIsEnumerable } = objproto; if (typeof XPCSafeJSObjectWrapper === "undefined") this.XPCSafeJSObjectWrapper = XPCNativeWrapper; -if (!XPCNativeWrapper.unwrap) - XPCNativeWrapper.unwrap = function unwrap(obj) { - if (hasOwnProperty.call(obj, "wrappedJSObject")) - return obj.wrappedJSObject; - return obj; - }; -if (!Object.create) - Object.create = function create(proto, props) { - let obj = { __proto__: proto }; - for (let k in properties(props || {})) - Object.defineProperty(obj, k, props[k]); - return obj; - }; -if (!Object.defineProperty) - Object.defineProperty = function defineProperty(obj, prop, desc) { - try { - let value = desc.value; - if ("value" in desc) - if (desc.writable && !__lookupGetter__.call(obj, prop) - && !__lookupSetter__.call(obj, prop)) - try { - obj[prop] = value; - } - catch (e if e instanceof TypeError) {} - else { - objproto.__defineGetter__.call(obj, prop, function () value); - if (desc.writable) - objproto.__defineSetter__.call(obj, prop, function (val) { value = val; }); - } +let getGlobalForObject = Cu.getGlobalForObject || function (obj) obj.__parent__; - if ("get" in desc) - objproto.__defineGetter__.call(obj, prop, desc.get); - if ("set" in desc) - objproto.__defineSetter__.call(obj, prop, desc.set); - } - catch (e) { - throw e.stack ? e : Error(e); - } - }; -if (!Object.defineProperties) - Object.defineProperties = function defineProperties(obj, props) { - for (let [k, v] in Iterator(props)) - Object.defineProperty(obj, k, v); - }; -if (!Object.freeze) - Object.freeze = function freeze(obj) {}; -if (!Object.getPropertyDescriptor) - Object.getPropertyDescriptor = function getPropertyDescriptor(obj, prop) { - try { - let desc = { - configurable: true, - enumerable: propertyIsEnumerable.call(obj, prop) - }; - var get = __lookupGetter__.call(obj, prop), - set = __lookupSetter__.call(obj, prop); - if (!get && !set) { - desc.value = obj[prop]; - desc.writable = true; - } - if (get) - desc.get = get; - if (set) - desc.set = set; - return desc; - } - catch (e) { - throw e.stack ? e : Error(e); - } - }; -if (!Object.getOwnPropertyDescriptor) - Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(obj, prop) { - if (hasOwnProperty.call(obj, prop)) - return Object.getPropertyDescriptor(obj, prop); - }; -if (!Object.getOwnPropertyNames) - Object.getOwnPropertyNames = function getOwnPropertyNames(obj, _debugger) { - try { - // This is an ugly and unfortunately necessary hack. - if (hasOwnProperty.call(obj, "__iterator__")) { - var oldIter = obj.__iterator__; - delete obj.__iterator__; - } - let res = [k for (k in obj) if (hasOwnProperty.call(obj, k))]; - if (oldIter !== undefined) { - obj.__iterator__ = oldIter; - res.push("__iterator__"); - } - return res; - } - catch (e) { - throw e.stack ? e : Error(e); - } - }; -if (!Object.getPrototypeOf) - Object.getPrototypeOf = function getPrototypeOf(obj) obj.__proto__; -if (!Object.keys) - Object.keys = function keys(obj) - Object.getOwnPropertyNames(obj).filter(function (k) propertyIsEnumerable.call(obj, k)); +function require(module, target) JSMLoader.load(module, target); -let getGlobalForObject = Cu.getGlobalForObject || function (obj) obj.__parent__; +function lazyRequire(module, names, target) { + for each (let name in names) + memoize(target || this, name, function (name) require(module)[name]); +} + +let jsmodules = { lazyRequire: lazyRequire }; +jsmodules.jsmodules = jsmodules; + +function toString() "[module-global " + this.NAME + "]"; let use = {}; let loaded = {}; @@ -129,21 +40,20 @@ let currentModule; let global = this; function defineModule(name, params, module) { if (!module) - module = getGlobalForObject(params); + module = this; module.NAME = name; module.EXPORTED_SYMBOLS = params.exports || []; - defineModule.loadLog.push("defineModule " + name); + if (!~module.EXPORTED_SYMBOLS.indexOf("File")) + delete module.File; + + defineModule.loadLog.push("[Begin " + name + "]"); + defineModule.prefix += " "; + for (let [, mod] in Iterator(params.require || [])) - require(module, mod); + require(mod, module); - for (let [, mod] in Iterator(params.use || [])) - if (loaded.hasOwnProperty(mod)) - require(module, mod, "use"); - else { - use[mod] = use[mod] || []; - use[mod].push(module); - } + module._lastModule = currentModule; currentModule = module; module.startTime = Date.now(); } @@ -151,29 +61,30 @@ function defineModule(name, params, module) { defineModule.loadLog = []; Object.defineProperty(defineModule.loadLog, "push", { value: function (val) { - if (false) + val = defineModule.prefix + val; + if (true) defineModule.dump(val + "\n"); this[this.length] = Date.now() + " " + val; } }); -defineModule.dump = function dump_() { - let msg = Array.map(arguments, function (msg) { +defineModule.prefix = ""; +defineModule.dump = function dump_(...args) { + let msg = args.map(function (msg) { if (loaded.util && typeof msg == "object") msg = util.objectToString(msg); return msg; }).join(", "); - let name = loaded.config ? config.name : "dactyl"; dump(String.replace(msg, /\n?$/, "\n") - .replace(/^./gm, name + ": $&")); + .replace(/^./gm, JSMLoader.name + ": $&")); } defineModule.modules = []; -defineModule.time = function time(major, minor, func, self) { +defineModule.time = function time(major, minor, func, self, ...args) { let time = Date.now(); if (typeof func !== "function") func = self[func]; try { - var res = func.apply(self, Array.slice(arguments, 4)); + var res = func.apply(self, args); } catch (e) { loaded.util && util.reportError(e); @@ -184,15 +95,15 @@ defineModule.time = function time(major, minor, func, self) { } function endModule() { - defineModule.loadLog.push("endModule " + currentModule.NAME); - - for (let [, mod] in Iterator(use[currentModule.NAME] || [])) - require(mod, currentModule.NAME, "use"); + defineModule.prefix = defineModule.prefix.slice(0, -2); + defineModule.loadLog.push("(End " + currentModule.NAME + ")"); loaded[currentModule.NAME] = 1; + require(currentModule.NAME, jsmodules); + currentModule = currentModule._lastModule; } -function require(obj, name, from) { +function require_(obj, name, from, targetName) { try { if (arguments.length === 1) [obj, name] = [{}, obj]; @@ -200,9 +111,14 @@ function require(obj, name, from) { let caller = Components.stack.caller; if (!loaded[name]) - defineModule.loadLog.push((from || "require") + ": loading " + name + " into " + (obj.NAME || caller.filename + ":" + caller.lineNumber)); + defineModule.loadLog.push((from || "require") + ": loading " + name + + " into " + (targetName || obj.NAME || caller.filename + ":" + caller.lineNumber)); JSMLoader.load(name + ".jsm", obj); + + if (!loaded[name] && obj != jsmodules) + JSMLoader.load(name + ".jsm", jsmodules); + return obj; } catch (e) { @@ -215,25 +131,49 @@ function require(obj, name, from) { } defineModule("base", { - // sed -n 's/^(const|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt + // sed -n 's/^(const|var|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt exports: [ - "ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Module", "JSMLoader", "Object", "Runnable", - "Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMUtils", "XPCSafeJSObjectWrapper", - "array", "bind", "call", "callable", "ctypes", "curry", "debuggerProperties", "defineModule", - "deprecated", "endModule", "forEach", "isArray", "isGenerator", "isinstance", "isObject", - "isString", "isSubclass", "iter", "iterAll", "iterOwnProperties", "keys", "memoize", "octal", - "properties", "require", "set", "update", "values", "withCallerGlobal" - ], - use: ["config", "services", "util"] -}, this); - -function Runnable(self, func, args) { - return { - __proto__: Runnable.prototype, - run: function () { func.apply(self, args || []); } - }; + "ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Finished", "Module", "JSMLoader", + "Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMShim", "XPCOMUtils", + "XPCSafeJSObjectWrapper", "array", "bind", "call", "callable", "ctypes", "curry", + "debuggerProperties", "defineModule", "deprecated", "endModule", "forEach", "isArray", + "isGenerator", "isinstance", "isObject", "isString", "isSubclass", "isXML", "iter", + "iterAll", "iterOwnProperties", "keys", "literal", "memoize", "octal", "properties", + "require", "set", "update", "values", "update_" + ] +}); + +this.lazyRequire("cache", ["cache"]); +this.lazyRequire("config", ["config"]); +this.lazyRequire("messages", ["_", "Messages"]); +this.lazyRequire("services", ["services"]); +this.lazyRequire("storage", ["File"]); +this.lazyRequire("util", ["FailedAssertion", "util"]); + +function literal(/* comment */) { + let { caller } = Components.stack; + while (caller && caller.language != 2) + caller = caller.caller; + + let file = caller.filename.replace(/.* -> /, ""); + let key = "literal:" + file + ":" + caller.line; + + let source = File.readURL(file); + + let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" + + ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source); + return match[1]; + + // Later... + return cache.get(key, function () { + let source = cache.get("literal:" + file, + function () util.httpGet(file).responseText); + + let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" + + ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source); + return match[1]; + }); } -Runnable.prototype.QueryInterface = XPCOMUtils.generateQI([Ci.nsIRunnable]); /** * Returns a list of all of the top-level properties of an object, by @@ -261,7 +201,6 @@ function debuggerProperties(obj) { * @returns {Generator} */ function prototype(obj) - /* Temporary hack: */ typeof obj === "xml" || obj.__proto__ !== obj.__proto__ ? null : obj.__proto__ || Object.getPrototypeOf(obj) || XPCNativeWrapper.unwrap(obj).__proto__ || Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj)); @@ -278,10 +217,42 @@ function properties(obj, prototypes, debugger_) { } catch (e) {} + function props(obj) { + // Grr. + try { + return Object.getOwnPropertyNames(obj); + } + catch (e) { + if (e.result === Cr.NS_ERROR_FAILURE) { + // This is being thrown for PDF.js content windows, + // currently. + let filter = function filter(prop) { + try { + return prop in obj; + } + catch (e) { + util.reportError("Filtering properties for " + + String.quote(obj) + ", " + + "error checking presence of " + + String.quote(prop) + ": " + e); + } + return false; + }; + return array.uniq([k for (k in obj)].concat( + Object.getOwnPropertyNames( + XPCNativeWrapper.unwrap(obj)) + .filter(filter))); + } + else if (!e.stack) { + throw Error(e); + } + } + } + for (; obj; obj = prototypes && prototype(obj)) { try { - if (sandbox.Object.getOwnPropertyNames || !debugger_ || !services.debugger.isOn) - var iter = values(Object.getOwnPropertyNames(obj)); + if (!debugger_ || !services.debugger.isOn) + var iter = (v for each (v in props(obj))); } catch (e) {} if (!iter) @@ -323,15 +294,15 @@ function deprecated(alternative, fn) { deprecated.warn = function warn(func, name, alternative, frame) { if (!func.seenCaller) func.seenCaller = Set([ - "resource://dactyl" + JSMLoader.suffix + "/javascript.jsm", - "resource://dactyl" + JSMLoader.suffix + "/util.jsm" + "resource://dactyl/javascript.jsm", + "resource://dactyl/util.jsm" ]); frame = frame || Components.stack.caller.caller; let filename = util.fixURI(frame.filename || "unknown"); if (!Set.add(func.seenCaller, filename)) util.dactyl(func).warn([util.urlPath(filename), frame.lineNumber, " "].join(":") - + require("messages")._("warn.deprecated", name, alternative)); + + _("warn.deprecated", name, alternative)); } /** @@ -346,6 +317,7 @@ function keys(obj) iter(function keys() { if (hasOwnProperty.call(obj, k)) yield k; }()); + /** * Iterates over all of the top-level, iterable property values of an * object. @@ -354,7 +326,7 @@ function keys(obj) iter(function keys() { * @returns {Generator} */ function values(obj) iter(function values() { - if (isinstance(obj, ["Generator", "Iterator"])) + if (isinstance(obj, ["Generator", "Iterator", Iter])) for (let k in obj) yield k; else @@ -469,18 +441,18 @@ function curry(fn, length, self, acc) { return fn; // Close over function with 'this' - function close(self, fn) function () fn.apply(self, Array.slice(arguments)); + function close(self, fn) function () fn.apply(self, arguments); if (acc == null) acc = []; - return function curried() { - let args = acc.concat(Array.slice(arguments)); - + return function curried(...args) { // The curried result should preserve 'this' - if (arguments.length == 0) + if (args.length == 0) return close(self || this, curried); + let args = acc.concat(args); + if (args.length >= length) return fn.apply(self || this, args); @@ -489,15 +461,14 @@ function curry(fn, length, self, acc) { } if (curry.bind) - var bind = function bind(meth, self) let (func = callable(meth) ? meth : self[meth]) - func.bind.apply(func, Array.slice(arguments, 1)); + var bind = function bind(meth, self, ...args) let (func = callable(meth) ? meth : self[meth]) + func.bind.apply(func, [self].concat(args)); else - var bind = function bind(func, self) { + var bind = function bind(func, self, ...args) { if (!callable(func)) func = self[func]; - let args = Array.slice(arguments, bind.length); - return function bound() func.apply(self, args.concat(Array.slice(arguments))); + return function bound(...args2) func.apply(self, args.concat(args2)); }; /** @@ -561,6 +532,12 @@ function isinstance(object, interfaces) { */ function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports; +/** + * Returns true if obje is an E4X XML object. + * @deprecated + */ +function isXML(obj) typeof obj === "xml"; + /** * Returns true if and only if its sole argument is an * instance of the builtin Array type. The array may come from @@ -594,10 +571,10 @@ function isString(val) objproto.toString.call(val) == "[object String]"; * Returns true if and only if its sole argument may be called * as a function. This includes classes and function objects. */ -function callable(val) typeof val === "function"; +function callable(val) typeof val === "function" && !(val instanceof Ci.nsIDOMElement); -function call(fn) { - fn.apply(arguments[1], Array.slice(arguments, 2)); +function call(fn, self, ...args) { + fn.apply(self, args); return fn; } @@ -611,13 +588,13 @@ function call(fn) { */ function memoize(obj, key, getter) { if (arguments.length == 1) { - obj = update({ __proto__: obj.__proto__ }, obj); - for (let prop in Object.getOwnPropertyNames(obj)) { + let res = update(Object.create(obj), obj); + for each (let prop in Object.getOwnPropertyNames(obj)) { let get = __lookupGetter__.call(obj, prop); if (get) - memoize(obj, prop, get); + memoize(res, prop, get); } - return obj; + return res; } try { @@ -625,9 +602,15 @@ function memoize(obj, key, getter) { configurable: true, enumerable: true, - get: function g_replaceProperty() ( - Class.replaceProperty(this.instance || this, key, null), - Class.replaceProperty(this.instance || this, key, getter.call(this, key))), + get: function g_replaceProperty() { + try { + Class.replaceProperty(this.instance || this, key, null); + return Class.replaceProperty(this.instance || this, key, getter.call(this, key)); + } + catch (e) { + util.reportError(e); + } + }, set: function s_replaceProperty(val) Class.replaceProperty(this.instance || this, key, val) @@ -638,20 +621,8 @@ function memoize(obj, key, getter) { } } -let sandbox = Cu.Sandbox(this); +let sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance()); sandbox.__proto__ = this; -/** - * Wraps a function so that when called, the global object of the caller - * is prepended to its arguments. - */ -// Hack to get around lack of access to caller in strict mode. -var withCallerGlobal = Cu.evalInSandbox(, Cu.Sandbox(this), "1.8"); /** * Updates an object with the properties of another object. Getters @@ -680,18 +651,45 @@ function update(target) { if (desc.value instanceof Class.Property) desc = desc.value.init(k, target) || desc.value; - if (typeof desc.value === "function" && target.__proto__) { - let func = desc.value.wrapped || desc.value; - if (!func.superapply) { - func.__defineGetter__("super", function () Object.getPrototypeOf(target)[k]); - func.superapply = function superapply(self, args) - let (meth = Object.getPrototypeOf(target)[k]) - meth && meth.apply(self, args); - func.supercall = function supercall(self) - func.superapply(self, Array.slice(arguments, 1)); + try { + if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) { + let func = desc.value.wrapped || desc.value; + if (!func.superapply) { + func.__defineGetter__("super", function get_super() Object.getPrototypeOf(target)[k]); + func.superapply = function superapply(self, args) + let (meth = Object.getPrototypeOf(target)[k]) + meth && meth.apply(self, args); + func.supercall = function supercall(self, ...args) + func.superapply(self, args); + } } + Object.defineProperty(target, k, desc); } + catch (e) {} + }); + } + return target; +} +function update_(target) { + for (let i = 1; i < arguments.length; i++) { + let src = arguments[i]; + Object.getOwnPropertyNames(src || {}).forEach(function (k) { + let desc = Object.getOwnPropertyDescriptor(src, k); + if (desc.value instanceof Class.Property) + desc = desc.value.init(k, target) || desc.value; + try { + if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) { + let func = desc.value.wrapped || desc.value; + if (!func.superapply) { + func.__defineGetter__("super", function get_super_() Object.getPrototypeOf(target)[k]); + func.superapply = function super_apply(self, args) + let (meth = Object.getPrototypeOf(target)[k]) + meth && meth.apply(self, args); + func.supercall = function super_call(self, ...args) + func.superapply(self, args); + } + } Object.defineProperty(target, k, desc); } catch (e) {} @@ -723,34 +721,39 @@ function update(target) { * * @returns {function} The constructor for the resulting class. */ -function Class() { +function Class(...args) { - var args = Array.slice(arguments); if (isString(args[0])) var name = args.shift(); var superclass = Class; if (callable(args[0])) superclass = args.shift(); - if (loaded.util && util.haveGecko("6.0a1")) // Bug 657418. + if (loaded.config && (config.haveGecko("5.*", "6.0") || config.haveGecko("6.*"))) // Bug 657418. var Constructor = function Constructor() { - var self = Object.create(Constructor.prototype, { - constructor: { value: Constructor }, - }); + var self = Object.create(Constructor.prototype); self.instance = self; + self.globalInstance = self; + + if ("_metaInit_" in self && self._metaInit_) + self._metaInit_.apply(self, arguments); + var res = self.init.apply(self, arguments); return res !== undefined ? res : self; }; else - var Constructor = eval(String.replace(, + var Constructor = eval(String.replace('\n\ + (function constructor(PARAMS) { \n\ + var self = Object.create(Constructor.prototype); \n\ + self.instance = self; \n\ + self.globalInstance = self; \n\ + \n\ + if ("_metaInit_" in self && self._metaInit_) \n\ + self._metaInit_.apply(self, arguments); \n\ + \n\ + var res = self.init.apply(self, arguments); \n\ + return res !== undefined ? res : self; \n\ + })', "constructor", (name || superclass.className).replace(/\W/g, "_")) .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1] .replace(/\b(self|res|Constructor)\b/g, "$1_"))); @@ -769,10 +772,12 @@ function Class() { } Class.extend(Constructor, superclass, args[0]); + memoize(Constructor, "closure", Class.makeClosure); update(Constructor, args[1]); + Constructor.__proto__ = superclass; - args = args.slice(2); - Array.forEach(args, function (obj) { + + args.slice(2).forEach(function (obj) { if (callable(obj)) obj = obj.prototype; update(Constructor.prototype, obj); @@ -839,7 +844,7 @@ Class.extend = function extend(subclass, superclass, overrides) { * property's value. * @returns {Class.Property} */ -Class.memoize = function memoize(getter, wait) +Class.Memoize = function Memoize(getter, wait) Class.Property({ configurable: true, enumerable: true, @@ -847,6 +852,7 @@ Class.memoize = function memoize(getter, wait) let done = false; if (wait) + // Crazy, yeah, I know. -- Kris this.get = function replace() { let obj = this.instance || this; Object.defineProperty(obj, key, { @@ -871,13 +877,34 @@ Class.memoize = function memoize(getter, wait) return this[key]; }; else - this.get = function replace() { + this.get = function g_Memoize() { let obj = this.instance || this; - Class.replaceProperty(obj, key, null); - return Class.replaceProperty(obj, key, getter.call(this, key)); + try { + Class.replaceProperty(obj, key, null); + return Class.replaceProperty(obj, key, getter.call(this, key)); + } + catch (e) { + util.reportError(e); + } }; - this.set = function replace(val) Class.replaceProperty(this.instance || this, val); + this.set = function s_Memoize(val) Class.replaceProperty(this.instance || this, key, val); + } + }); + +Class.memoize = deprecated("Class.Memoize", function memoize() Class.Memoize.apply(this, arguments)); + +/** + * Updates the given object with the object in the target class's + * prototype. + */ +Class.Update = function Update(obj) + Class.Property({ + configurable: true, + enumerable: true, + writable: true, + init: function (key, target) { + this.value = update({}, target[key], obj); } }); @@ -893,6 +920,9 @@ Class.prototype = { */ init: function c_init() {}, + get instance() ({}), + set instance(val) Class.replaceProperty(this, "instance", val), + withSavedValues: function withSavedValues(names, callback, self) { let vals = names.map(function (name) this[name], this); try { @@ -921,15 +951,18 @@ Class.prototype = { * @returns {nsITimer} The timer which backs this timeout. */ timeout: function timeout(callback, timeout) { - const self = this; - function timeout_notify(timer) { - if (self.stale || + let timeout_notify = (timer) => { + if (this.stale || util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"])) return; - util.trapErrors(callback, self); - } - return services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT); + this.timeouts.splice(this.timeouts.indexOf(timer), 1); + util.trapErrors(callback, this); + }; + let timer = services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT); + this.timeouts.push(timer); + return timer; }, + timeouts: [], /** * Updates this instance with the properties of the given objects. @@ -937,12 +970,11 @@ Class.prototype = { * localized properties. */ update: function update() { - let self = this; // XXX: Duplication. for (let i = 0; i < arguments.length; i++) { let src = arguments[i]; - Object.getOwnPropertyNames(src || {}).forEach(function (k) { + Object.getOwnPropertyNames(src || {}).forEach((k) => { let desc = Object.getOwnPropertyDescriptor(src, k); if (desc.value instanceof Class.Property) desc = desc.value.init(k, this) || desc.value; @@ -950,12 +982,16 @@ Class.prototype = { if (typeof desc.value === "function") { let func = desc.value.wrapped || desc.value; if (!func.superapply) { - func.__defineGetter__("super", function () Object.getPrototypeOf(self)[k]); - func.superapply = function superapply(self, args) - let (meth = Object.getPrototypeOf(self)[k]) - meth && meth.apply(self, args); - func.supercall = function supercall(self) - func.superapply(self, Array.slice(arguments, 1)); + func.__defineGetter__("super", () => Object.getPrototypeOf(this)[k]); + + func.superapply = function superapply(self, args) { + let meth = Object.getPrototypeOf(self)[k]; + return meth && meth.apply(self, args); + }; + + func.supercall = function supercall(self, ...args) { + return func.superapply(self, args); + } } } @@ -968,10 +1004,18 @@ Class.prototype = { catch (e) {} }, this); } + return this; }, + localizedProperties: {}, magicalProperties: {} }; +for (let name in properties(Class.prototype)) { + let desc = Object.getOwnPropertyDescriptor(Class.prototype, name); + desc.enumerable = false; + Object.defineProperty(Class.prototype, name, desc); +} + Class.makeClosure = function makeClosure() { const self = this; function closure(fn) { @@ -1015,16 +1059,35 @@ memoize(Class.prototype, "closure", Class.makeClosure); function XPCOM(interfaces, superClass) { interfaces = Array.concat(interfaces); - let shim = interfaces.reduce(function (shim, iface) shim.QueryInterface(iface), - Cc["@dactyl.googlecode.com/base/xpc-interface-shim"].createInstance()); + let shim = XPCOMShim(interfaces); + + let res = Class("XPCOM(" + interfaces + ")", superClass || Class, + update(iter([k, + v === undefined || callable(v) ? stub : v] + for ([k, v] in Iterator(shim))).toObject(), + { QueryInterface: XPCOMUtils.generateQI(interfaces) })); - let res = Class("XPCOM(" + interfaces + ")", superClass || Class, update( - iter.toObject([k, v === undefined || callable(v) ? function stub() null : v] - for ([k, v] in Iterator(shim))), - { QueryInterface: XPCOMUtils.generateQI(interfaces) })); - shim = interfaces = null; return res; } +function XPCOMShim(interfaces) { + let ip = services.InterfacePointer({ + QueryInterface: function (iid) { + if (iid.equals(Ci.nsISecurityCheckedComponent)) + throw Cr.NS_ERROR_NO_INTERFACE; + return this; + }, + getHelperForLanguage: function () null, + getInterfaces: function (count) { count.value = 0; } + }); + return (interfaces || []).reduce(function (shim, iface) shim.QueryInterface(Ci[iface]), + ip.data); +}; +let stub = Class.Property({ + configurable: true, + enumerable: false, + value: function stub() null, + writable: true +}); /** * An abstract base class for classes that wish to inherit from Error. @@ -1034,7 +1097,7 @@ var ErrorBase = Class("ErrorBase", Error, { init: function EB_init(message, level) { level = level || 0; let error = Error(message); - update(this, error) + update(this, error); this.stack = error.stack; this.message = message; @@ -1049,6 +1112,12 @@ var ErrorBase = Class("ErrorBase", Error, { toString: function () String(this.message) }); +/** + * An Error subclass to throw in order to stop sourcing a plugin without + * printing a stack trace. + */ +var Finished = Class("Finished", ErrorBase); + /** * Constructs a new Module class and instantiates an instance into the current * module global object. @@ -1058,18 +1127,33 @@ var ErrorBase = Class("ErrorBase", Error, { * @param {Object} classProperties Properties to be applied to the class constructor. * @returns {Class} */ -function Module(name, prototype) { - let init = callable(prototype) ? 4 : 3; - const module = Class.apply(Class, Array.slice(arguments, 0, init)); - let instance = module(); - module.className = name.toLowerCase(); - - instance.INIT = update(Object.create(Module.INIT), - arguments[init] || {}); - - currentModule[module.className] = instance; - defineModule.modules.push(instance); - return module; +function Module(name, prototype, ...args) { + try { + let init = callable(prototype) ? 2 : 1; + let proto = callable(prototype) ? args[0] : prototype; + + proto._metaInit_ = function () { + delete module.prototype._metaInit_; + currentModule[name.toLowerCase()] = this; + }; + + const module = Class.apply(Class, [name, prototype, ...args.slice(0, init)]); + let instance = module(); + module.className = name.toLowerCase(); + + instance.INIT = update(Object.create(Module.INIT), + args[init] || {}); + + currentModule[module.className] = instance; + defineModule.modules.push(instance); + return module; + } + catch (e) { + if (typeof e === "string") + e = Error(e); + + dump(e.fileName + ":" + e.lineNumber + ": " + e + "\n" + (e.stack || Error().stack)); + } } Module.INIT = { init: function Module_INIT_init(dactyl, modules, window) { @@ -1088,7 +1172,7 @@ Module.INIT = { module.isLocalModule = true; modules.jsmodules[this.constructor.className] = module; - locals.reverse().forEach(function (fn, i) update(objs[i], fn.apply(module, args))) + locals.reverse().forEach(function (fn, i) update(objs[i], fn.apply(module, args))); memoize(module, "closure", Class.makeClosure); module.instance = module; @@ -1115,13 +1199,9 @@ Module.INIT = { * * @returns {function} The constructor for the new Struct. */ -function Struct() { - if (!/^[A-Z]/.test(arguments[0])) - var args = Array.slice(arguments, 0); - else { - var className = arguments[0]; - args = Array.slice(arguments, 1); - } +function Struct(...args) { + if (/^[A-Z]/.test(args[0])) + var className = args.shift(); const Struct = Class(className || "Struct", StructBase, { length: args.length, @@ -1133,13 +1213,15 @@ function Struct() { }); return Struct; } -let StructBase = Class("StructBase", Array, { +var StructBase = Class("StructBase", Array, { init: function struct_init() { for (let i = 0; i < arguments.length; i++) if (arguments[i] != undefined) this[i] = arguments[i]; }, + get toStringParams() this, + clone: function struct_clone() this.constructor.apply(null, this.slice()), closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")), @@ -1181,7 +1263,7 @@ let StructBase = Class("StructBase", Array, { localize: function localize(key, defaultValue) { let i = this.prototype.members[key]; - Object.defineProperty(this.prototype, i, require("messages").Messages.Localized(defaultValue).init(key, this.prototype)); + Object.defineProperty(this.prototype, i, Messages.Localized(defaultValue).init(key, this.prototype)); return this; } }); @@ -1211,7 +1293,7 @@ var Timer = Class("Timer", { } catch (e) { if (typeof util === "undefined") - dump("dactyl: " + e + "\n" + (e.stack || Error().stack)); + dump(JSMLoader.name + ": " + e + "\n" + (e.stack || Error().stack)); else util.reportError(e); } @@ -1297,9 +1379,13 @@ function octal(decimal) parseInt(decimal, 8); * function. * * @param {object} obj + * @param {nsIJSIID} iface The interface to which to query all elements. * @returns {Generator} */ -function iter(obj) { +function iter(obj, iface) { + if (arguments.length == 2 && iface instanceof Ci.nsIJSIID) + return iter(obj).map(function (item) item.QueryInterface(iface)); + let args = arguments; let res = Iterator(obj); @@ -1326,7 +1412,8 @@ function iter(obj) { } else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList])) res = array.iterItems(obj); - else if (obj instanceof Ci.nsIDOMNamedNodeMap) + else if (Ci.nsIDOMNamedNodeMap && obj instanceof Ci.nsIDOMNamedNodeMap || + Ci.nsIDOMMozNamedAttrMap && obj instanceof Ci.nsIDOMMozNamedAttrMap) res = (function () { for (let i = 0; i < obj.length; i++) yield [obj.name, obj]; @@ -1354,17 +1441,7 @@ function iter(obj) { return iter(obj.enumerator()); return iter(obj.enumerator); } - res.__noSuchMethod__ = function __noSuchMethod__(meth, args) { - if (meth in iter) - var res = iter[meth].apply(iter, [this].concat(args)); - else - res = let (ary = array(this)) - ary[meth] ? ary[meth].apply(ary, args) : ary.__noSuchMethod__(meth, args); - if (isinstance(res, ["Iterator", "Generator"])) - return iter(res); - return res; - }; - return res; + return Iter(res); } update(iter, { toArray: function toArray(iter) array(iter).array, @@ -1478,6 +1555,23 @@ update(iter, { } }); +const Iter = Class("Iter", { + init: function init(iter) { + this.iter = iter; + if ("__iterator__" in iter) + this.iter = iter.__iterator__(); + + if (this.iter.finalize) + this.finalize = function finalize() this.iter.finalize.apply(this.iter, arguments); + }, + + next: function next() this.iter.next(), + + send: function send() this.iter.send.apply(this.iter, arguments), + + __iterator__: function () this.iter +}); + /** * Array utility methods. */ @@ -1501,9 +1595,9 @@ var array = Class("array", Array, { }, array: ary, toString: function () this.array.toString(), - concat: function () this.__noSuchMethod__("concat", Array.slice(arguments)), - filter: function () this.__noSuchMethod__("filter", Array.slice(arguments)), - map: function () this.__noSuchMethod__("map", Array.slice(arguments)) + concat: function (...args) this.__noSuchMethod__("concat", args), + filter: function (...args) this.__noSuchMethod__("filter", args), + map: function (...args) this.__noSuchMethod__("map", args) }; } }, { @@ -1633,8 +1727,42 @@ var array = Class("array", Array, { } }); +/* Make Minefield not explode, because Minefield exploding is not fun. */ +let iterProto = Iter.prototype; +Object.keys(iter).forEach(function (k) { + iterProto[k] = function (...args) { + let res = iter[k].apply(iter, [this].concat(args)); + if (isinstance(res, ["Iterator", "Generator"])) + return Iter(res); + return res; + }; +}); + +Object.keys(array).forEach(function (k) { + if (!(k in iterProto)) + iterProto[k] = function (...args) { + let res = array[k].apply(array, [this.toArray()].concat(args)); + if (isinstance(res, ["Iterator", "Generator"])) + return Iter(res); + if (isArray(res)) + return array(res); + return res; + }; +}); + +Object.getOwnPropertyNames(Array.prototype).forEach(function (k) { + if (!(k in iterProto) && callable(Array.prototype[k])) + iterProto[k] = function () { + let ary = iter(this).toArray(); + let res = ary[k].apply(ary, arguments); + if (isArray(res)) + return array(res); + return res; + }; +}); + endModule(); // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);} -// vim: set fdm=marker sw=4 ts=4 et ft=javascript: +// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: