diff --git a/.hgignore b/.hgignore index e55fa075..9cb94cb8 100644 --- a/.hgignore +++ b/.hgignore @@ -13,6 +13,7 @@ syntax: glob */contrib/vim/*.vba */bak/* downloads/* +.git/* *.py[co] diff --git a/common/components/commandline-handler.js b/common/components/commandline-handler.js index fa60f73c..3083af0f 100644 --- a/common/components/commandline-handler.js +++ b/common/components/commandline-handler.js @@ -48,5 +48,6 @@ if (XPCOMUtils.generateNSGetFactory) const NSGetFactory = XPCOMUtils.generateNSGetFactory([CommandLineHandler]); else const NSGetModule = XPCOMUtils.generateNSGetModule([CommandLineHandler]); +var EXPORTED_SYMBOLS = ["NSGetFactory"]; // vim: set fdm=marker sw=4 ts=4 et: diff --git a/common/components/protocols.js b/common/components/protocols.js index 0bedf607..b68df555 100644 --- a/common/components/protocols.js +++ b/common/components/protocols.js @@ -202,5 +202,6 @@ if (XPCOMUtils.generateNSGetFactory) const NSGetFactory = XPCOMUtils.generateNSGetFactory([AboutHandler, ChromeData, Dactyl, Shim]); else const NSGetModule = XPCOMUtils.generateNSGetModule([AboutHandler, ChromeData, Dactyl, Shim]); +var EXPORTED_SYMBOLS = ["NSGetFactory"]; // vim: set fdm=marker sw=4 ts=4 et: diff --git a/common/content/buffer.js b/common/content/buffer.js index 456062e8..04dc93ed 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -147,6 +147,40 @@ const Buffer = Module("buffer", { let elem = event.originalTarget; buffer.viewSource([elem.getAttribute("href"), Number(elem.getAttribute("line"))]) }; + + this.replaceProgressListener(this.progressListener); + }, + + cleanup: function () { + for (let prop in properties(this.progressListener)) + if (!this.progressListener.__lookupGetter__(prop) && + !callable(this.progressListener[prop])) + this.origProgressListener[prop] = this.progressListener[prop] + + this.replaceProgressListener(this.origProgressListener); + }, + + replaceProgressListener: function (newListener) { + // I hate this whole hack. --Kris + let obj = window.XULBrowserWindow, getter; + for (let prop in properties(obj)) + if ((getter = obj.__lookupGetter__(prop)) && !obj.__lookupSetter__(prop)) { + newListener.__defineGetter__(prop, getter); + delete obj[prop]; + } + + this.origProgressListener = window.XULBrowserWindow; + try { + config.browser.removeProgressListener(window.XULBrowserWindow); + } + catch (e) {} // Why? --djk + + config.browser.addProgressListener(newListener, Ci.nsIWebProgress.NOTIFY_ALL); + window.XULBrowserWindow = newListener; + window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner + .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIXULWindow) + .XULBrowserWindow = newListener; }, destroy: function () { @@ -1502,26 +1536,6 @@ const Buffer = Module("buffer", { }; }, events: function () { - try { - config.browser.removeProgressListener(window.XULBrowserWindow); - } - catch (e) {} // Why? --djk - - // I hate this whole hack. --Kris - let obj = window.XULBrowserWindow, getter; - for (let p in properties(obj)) - if ((getter = obj.__lookupGetter__(p)) && !obj.__lookupSetter__(p)) { - this.progressListener.__defineGetter__(p, getter); - delete obj[p]; - } - - config.browser.addProgressListener(this.progressListener, Ci.nsIWebProgress.NOTIFY_ALL); - window.XULBrowserWindow = this.progressListener; - window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner - .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIXULWindow) - .XULBrowserWindow = this.progressListener; - events.addSessionListener(config.browser, "DOMContentLoaded", this.closure.onDOMContentLoaded, true); events.addSessionListener(config.browser, "load", this.closure.onPageLoad, true); events.addSessionListener(config.browser, "scroll", this.closure._updateBufferPosition, false); diff --git a/common/content/commandline.js b/common/content/commandline.js index 3386d9ee..739cda81 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -67,6 +67,7 @@ const CommandWidgets = Class("CommandWidgets", { getGroup: function (value) { if (this.command && !options.get("guioptions").has("M")) return this.statusbar; + let statusElem = this.statusbar.message; if (value && statusElem.editor.rootElement.scrollWidth > statusElem.scrollWidth) return this.commandbar; @@ -84,6 +85,14 @@ const CommandWidgets = Class("CommandWidgets", { return this.commandbar; } }); + + let fontSize = util.computedStyle(document.getElementById(config.mainWindowId)).fontSize; + styles.registerSheet("chrome://dactyl/skin/dactyl.css"); + styles.system.add("font-size", "chrome://dactyl/content/buffer.xhtml", + "body { font-size: " + fontSize + "; }"); + }, + cleanup: function cleanup() { + styles.unregisterSheet("chrome://dactyl/skin/dactyl.css"); }, addElement: function (obj) { const self = this; @@ -1742,12 +1751,6 @@ const CommandLine = Module("commandline", { host && (!item.domains || !item.domains.some(function (d) util.isSubdomain(d, host)))); } }); - }, - styles: function () { - let fontSize = util.computedStyle(document.getElementById(config.mainWindowId)).fontSize; - styles.registerSheet("chrome://dactyl/skin/dactyl.css"); - styles.system.add("font-size", "chrome://dactyl/content/buffer.xhtml", - "body { font-size: " + fontSize + "; }"); } }); diff --git a/common/content/completion.js b/common/content/completion.js index 5c5d72e9..c58c604f 100644 --- a/common/content/completion.js +++ b/common/content/completion.js @@ -530,9 +530,9 @@ const CompletionContext = Class("CompletionContext", { // of the given string which also matches the current // item's text. let len = substring.length; - let i = 0, m, n = len; + let i = 0, n = len; while (n) { - m = Math.floor(n / 2); + let m = Math.floor(n / 2); let keep = compare(fixCase(item.text), substring.substring(0, i + m)); if (!keep) len = i + m - 1; diff --git a/common/content/dactyl.js b/common/content/dactyl.js index a10c5f19..39319659 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -37,6 +37,19 @@ const Dactyl = Module("dactyl", { }; }, + cleanup: function () { + delete window.dactyl; + delete window.liberator; + }, + + destroy: function () { + autocommands.trigger("LeavePre", {}); + storage.saveAll(); + dactyl.triggerObserver("shutdown", null); + util.dump("All dactyl modules destroyed\n"); + autocommands.trigger("Leave", {}); + }, + /** @property {string} The name of the current user profile. */ profileName: Class.memoize(function () { // NOTE: services.profile.selectedProfile.name doesn't return @@ -52,14 +65,6 @@ const Dactyl = Module("dactyl", { return "unknown"; }), - destroy: function () { - autocommands.trigger("LeavePre", {}); - storage.saveAll(); - dactyl.triggerObserver("shutdown", null); - util.dump("All dactyl modules destroyed\n"); - autocommands.trigger("Leave", {}); - }, - /** * @property {number} The current main mode. * @see modes#mainModes diff --git a/common/content/io.js b/common/content/io.js index 5cd87618..29731ead 100644 --- a/common/content/io.js +++ b/common/content/io.js @@ -13,20 +13,21 @@ plugins.contexts = {}; function Script(file) { let self = set.has(plugins, file.path) && plugins[file.path]; if (self) { - if (self.onUnload) + if (set.has(self, "onUnload")) self.onUnload(); } else { - self = { __proto__: plugins }; - plugins[file.path] = self; - self.NAME = file.leafName.replace(/\..*/, "").replace(/-([a-z])/g, function (m, n1) n1.toUpperCase()); - self.PATH = file.path; - self.CONTEXT = self; + self = update({ __proto__: plugins }, { + NAME: file.leafName.replace(/\..*/, "").replace(/-([a-z])/g, function (m, n1) n1.toUpperCase()), + PATH: file.path, + CONTEXT: self + }); + Class.replaceProperty(plugins, file.path, self); // This belongs elsewhere if (io.getRuntimeDirectories("plugins").some( function (dir) dir.contains(file, false))) - plugins[self.NAME] = self; + Class.replaceProperty(plugins, self.NAME, self); } plugins.contexts[file.path] = self; return self; @@ -67,6 +68,13 @@ const IO = Module("io", { services.downloadManager.addListener(this.downloadListener); }, + destroy: function () { + services.downloadManager.removeListener(this.downloadListener); + for (let [, plugin] in Iterator(plugins.contexts)) + if (plugin.onUnload) + plugin.onUnload(); + }, + // TODO: there seems to be no way, short of a new component, to change // the process's CWD - see https://bugzilla.mozilla.org/show_bug.cgi?id=280953 /** @@ -111,13 +119,6 @@ const IO = Module("io", { return this.cwd; }, - destroy: function () { - services.downloadManager.removeListener(this.downloadListener); - for (let [, plugin] in Iterator(plugins.contexts)) - if (plugin.onUnload) - plugin.onUnload(); - }, - /** * @property {function} File class. * @final diff --git a/common/content/tabs.js b/common/content/tabs.js index 9500fdb0..5337abc9 100644 --- a/common/content/tabs.js +++ b/common/content/tabs.js @@ -36,6 +36,15 @@ const Tabs = Module("tabs", { }; }, + cleanup: function cleanup() { + for (let [i, tab] in Iterator(this.allTabs)) { + function node(clas) document.getAnonymousElementByAttribute(tab, "class", clas); + for (let elem in values(["dactyl-tab-icon-number", "dactyl-tab-number"].map(node))) + if (elem) + elem.parentNode.parentNode.removeChild(elem.parentNode); + } + }, + _updateTabCount: function () { if (dactyl.has("Gecko2")) for (let [i, tab] in Iterator(this.visibleTabs)) { @@ -43,15 +52,15 @@ const Tabs = Module("tabs", { if (!node("dactyl-tab-number")) { let nodes = {}; let dom = util.xmlToDom(.*, document, nodes); let img = node("tab-icon-image"); img.parentNode.appendChild(dom); - tab.__defineGetter__("ordinal", function () Number(nodes.icon.value)); - tab.__defineSetter__("ordinal", function (i) nodes.icon.value = nodes.label.textContent = i); + tab.__defineGetter__("dactylOrdinal", function () Number(nodes.icon.value)); + tab.__defineSetter__("dactylOrdinal", function (i) nodes.icon.value = nodes.label.textContent = i); } - tab.ordinal = i + 1; + tab.dactylOrdinal = i + 1; } statusline.updateTabCount(true); }, diff --git a/common/modules/base.jsm b/common/modules/base.jsm index c08cd304..f0b4048c 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -98,6 +98,7 @@ let loaded = {}; let currentModule; function defineModule(name, params) { let module = Cu.getGlobalForObject ? Cu.getGlobalForObject(params) : params.__parent__; + defineModule.globals.push(module); module.NAME = name; module.EXPORTED_SYMBOLS = params.exports || []; defineModule.loadLog.push("defineModule " + name); @@ -114,6 +115,7 @@ function defineModule(name, params) { currentModule = module; } +defineModule.globals = []; defineModule.loadLog = []; Object.defineProperty(defineModule.loadLog, "push", { value: function (val) { defineModule.dump(val + "\n"); this[this.length] = val; } @@ -150,8 +152,10 @@ 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"); + loaded[currentModule.NAME] = 1; } @@ -177,8 +181,8 @@ defineModule("base", { "call", "callable", "ctypes", "curry", "debuggerProperties", "defineModule", "deprecated", "endModule", "forEach", "isArray", "isGenerator", "isinstance", "isObject", "isString", "isSubclass", "iter", "iterAll", - "keys", "memoize", "octal", "properties", "set", "update", "values", - "withCallerGlobal" + "keys", "memoize", "octal", "properties", "require", "set", "update", + "values", "withCallerGlobal" ], use: ["services", "util"] }); diff --git a/common/modules/bookmarkcache.jsm b/common/modules/bookmarkcache.jsm index e3d972b6..e230a077 100644 --- a/common/modules/bookmarkcache.jsm +++ b/common/modules/bookmarkcache.jsm @@ -42,6 +42,10 @@ const BookmarkCache = Module("BookmarkCache", XPCOM(Ci.nsINavBookmarkObserver), services.bookmarks.addObserver(this, false); }, + cleanup: function cleanup() { + services.bookmarks.removeObserver(this); + }, + __iterator__: function () (val for ([, val] in Iterator(bookmarkcache.bookmarks))), get bookmarks() Class.replaceProperty(this, "bookmarks", this.load()), diff --git a/common/modules/prefs.jsm b/common/modules/prefs.jsm index d5c935e3..63ebac52 100644 --- a/common/modules/prefs.jsm +++ b/common/modules/prefs.jsm @@ -28,6 +28,10 @@ const Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference this.restore(); }, + cleanup: function cleanup() { + this._branch.removeObserver("", this); + }, + observe: { "nsPref:changed": function (subject, data) { let observers = this._observers[data]; diff --git a/common/modules/sanitizer.jsm b/common/modules/sanitizer.jsm index 6bcfd7ee..13274585 100644 --- a/common/modules/sanitizer.jsm +++ b/common/modules/sanitizer.jsm @@ -12,6 +12,8 @@ // FIXME: // - finish 1.9.0 support if we're going to support sanitizing in Melodactyl +try { + Components.utils.import("resource://dactyl/base.jsm"); defineModule("sanitizer", { exports: ["Range", "Sanitizer", "sanitizer"], @@ -641,6 +643,6 @@ const Sanitizer = Module("sanitizer", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakR endModule(); -// catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);} +} catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);} // vim: set fdm=marker sw=4 ts=4 et ft=javascript: diff --git a/common/modules/services.jsm b/common/modules/services.jsm index 7be520f6..38212cf8 100644 --- a/common/modules/services.jsm +++ b/common/modules/services.jsm @@ -8,8 +8,7 @@ try { Components.utils.import("resource://dactyl/base.jsm"); defineModule("services", { - exports: ["Services", "services"], - use: ["util"] + exports: ["AddonManager", "Services", "services"] }); /** @@ -72,6 +71,9 @@ const Services = Module("Services", { this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", Ci.nsIXMLHttpRequest); this.addClass("ZipReader", "@mozilla.org/libjar/zip-reader;1", Ci.nsIZipReader, "open"); this.addClass("ZipWriter", "@mozilla.org/zipwriter;1", Ci.nsIZipWriter); + + if (!this.extensionManager) + Components.utils.import("resource://gre/modules/AddonManager.jsm"); }, _create: function (classes, ifaces, meth, init, args) { @@ -85,7 +87,7 @@ const Services = Module("Services", { return res; } catch (e) { - util.dump("Service creation failed for '" + classes + "': " + e + "\n"); + dump("dactyl: Service creation failed for '" + classes + "': " + e + "\n"); return null; } }, @@ -105,8 +107,12 @@ const Services = Module("Services", { if (name in this && ifaces && !this.__lookupGetter__(name) && !(this[name] instanceof Ci.nsISupports)) throw TypeError(); this.__defineGetter__(name, function () { + let res = self._create(class_, ifaces, meth); + if (!res) + return null; + delete this[name]; - return this[name] = self._create(class_, ifaces, meth); + return this[name] = res; }); }, @@ -140,10 +146,6 @@ const Services = Module("Services", { get: function (name) this[name], }, { }, { - init: function (dactyl, modules) { - if (!modules.AddonManager && !this.get("extensionManager")) - Components.utils.import("resource://gre/modules/AddonManager.jsm", modules); - }, javascript: function (dactyl, modules) { modules.JavaScript.setCompleter(this.get, [function () [[k, v] for ([k, v] in Iterator(services)) if (v instanceof Ci.nsISupports)]]); modules.JavaScript.setCompleter(this.create, [function () [[c, ""] for (c in services.classes)]]); diff --git a/common/modules/storage.jsm b/common/modules/storage.jsm index 730e3dde..6e06377b 100644 --- a/common/modules/storage.jsm +++ b/common/modules/storage.jsm @@ -142,25 +142,39 @@ const ObjectStore = Class("ObjectStore", StoreBase, { __iterator__: function () Iterator(this._object), }); -var keys = {}; -var observers = {}; - const Storage = Module("Storage", { alwaysReload: {}, + init: function () { + this.cleanup(); + }, + + cleanup: function () { + for (let key in keys(this.keys)) + delete this[key]; + for (let ary in values(this.observers)) + for (let obj in values(ary)) + if (obj.ref && obj.ref.get()) + delete obj.ref.get().dactylStorageRefs; + + this.keys = {}; + this.observers = {}; + }, + newObject: function newObject(key, constructor, params) { if (params == null || !isObject(params)) throw Error("Invalid argument type"); - if (!(key in keys) || params.reload || this.alwaysReload[key]) { + if (!(key in this.keys) || params.reload || this.alwaysReload[key]) { if (key in this && !(params.reload || this.alwaysReload[key])) throw Error(); let load = function () loadData(key, params.store, params.type || myObject); - keys[key] = new constructor(key, params.store, load, params); - keys[key].timer = new Timer(1000, 10000, function () storage.save(key)); - this.__defineGetter__(key, function () keys[key]); + + this.keys[key] = new constructor(key, params.store, load, params); + this.keys[key].timer = new Timer(1000, 10000, function () storage.save(key)); + this.__defineGetter__(key, function () this.keys[key]); } - return keys[key]; + return this.keys[key]; }, newMap: function newMap(key, options) { @@ -182,38 +196,36 @@ const Storage = Module("Storage", { callbackRef = { get: function () callback }; } this.removeDeadObservers(); - if (!(key in observers)) - observers[key] = []; - if (!observers[key].some(function (o) o.callback.get() == callback)) - observers[key].push({ ref: ref && Cu.getWeakReference(ref), callback: callbackRef }); + if (!(key in this.observers)) + this.observers[key] = []; + if (!this.observers[key].some(function (o) o.callback.get() == callback)) + this.observers[key].push({ ref: ref && Cu.getWeakReference(ref), callback: callbackRef }); }, removeObserver: function (key, callback) { this.removeDeadObservers(); - if (!(key in observers)) + if (!(key in this.observers)) return; - observers[key] = observers[key].filter(function (elem) elem.callback.get() != callback); - if (observers[key].length == 0) + this.observers[key] = this.observers[key].filter(function (elem) elem.callback.get() != callback); + if (this.observers[key].length == 0) delete obsevers[key]; }, removeDeadObservers: function () { - for (let [key, ary] in Iterator(observers)) { - observers[key] = ary = ary.filter(function (o) o.callback.get() && (!o.ref || o.ref.get() && o.ref.get().dactylStorageRefs)); + for (let [key, ary] in Iterator(this.observers)) { + this.observers[key] = ary = ary.filter(function (o) o.callback.get() && (!o.ref || o.ref.get() && o.ref.get().dactylStorageRefs)); if (!ary.length) - delete observers[key]; + delete this.observers[key]; } }, - get observers() observers, - fireEvent: function fireEvent(key, event, arg) { this.removeDeadObservers(); - if (key in observers) + if (key in this.observers) // Safe, since we have our own Array object here. - for each (let observer in observers[key]) + for each (let observer in this.observers[key]) observer.callback.get()(key, event, arg); - if (key in keys) + if (key in this.keys) this[key].timer.tell(); }, @@ -223,11 +235,11 @@ const Storage = Module("Storage", { }, save: function save(key) { - saveData(keys[key]); + saveData(this.keys[key]); }, saveAll: function storeAll() { - for each (let obj in keys) + for each (let obj in this.keys) saveData(obj); }, @@ -237,7 +249,7 @@ const Storage = Module("Storage", { if (val && !this._privateMode) this.saveAll(); if (!val && this._privateMode) - for (let key in keys) + for (let key in this.keys) this.load(key); return this._privateMode = Boolean(val); } diff --git a/common/modules/styles.jsm b/common/modules/styles.jsm index aebf4401..c23a7c65 100644 --- a/common/modules/styles.jsm +++ b/common/modules/styles.jsm @@ -79,6 +79,11 @@ const Hive = Class("Hive", { this.names = {}; }, + cleanup: function cleanup() { + for (let sheet in values(this.sheets)) + sheet.enabled = false; + }, + __iterator__: function () Iterator(this.sheets), get sites() array(this.sheets).map(function (s) s.sites).flatten().uniq().array, @@ -214,6 +219,11 @@ const Styles = Module("Styles", { this.system = Hive(); }, + cleanup: function cleanup() { + for each (let hive in [this.user, this.system]) + hive.cleanup(); + }, + __iterator__: function () Iterator(this.user.sheets.concat(this.system.sheets)), _proxy: function (name, args) diff --git a/common/modules/util.jsm b/common/modules/util.jsm index 15cfcb82..153c9b0a 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -54,6 +54,16 @@ const Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) this.overlays = {}; }, + cleanup: function cleanup() { + for (let win in iter(services.windowMediator.getEnumerator(null))) { + for (let elem in values(win.document.dactylOverlayElements || [])) + if (elem.parentNode) + elem.parentNode.removeChild(elem); + delete win.document.dactylOverlayElements; + delete win.document.dactylOverlays; + } + }, + // FIXME: Only works for Pentadactyl get activeWindow() services.windowMediator.getMostRecentWindow("navigator:browser"), dactyl: update(function dactyl(obj) { @@ -90,12 +100,13 @@ const Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) let observers = obj.observe; function register(meth) { services.observer[meth](obj, "quit-application", true); + services.observer[meth](obj, "dactyl-unload", true); for (let target in keys(observers)) services.observer[meth](obj, target, true); } Class.replaceProperty(obj, "observe", function (subject, target, data) { - if (target == "quit-application") + if (target == "quit-application" || target == "dactyl-unload") register("removeObserver"); if (observers[target]) observers[target].call(obj, subject, data); @@ -838,7 +849,7 @@ const Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) let tag = "<" + [namespaced(elem)].concat( [namespaced(a) + "=" + template.highlight(a.value, true) for ([i, a] in array.iterItems(elem.attributes))]).join(" "); - return tag + (hasChildren ? "/>" : ">..."); + return tag + (!hasChildren ? "/>" : ">..."); } catch (e) { return {}.toString.call(elem); @@ -899,42 +910,88 @@ const Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) observe: { "toplevel-window-ready": function (window, data) { window.addEventListener("DOMContentLoaded", wrapCallback(function listener(event) { - window.removeEventListener("DOMContentLoaded", listener.wrapper, true); - - if (event.originalTarget !== window.document) - return; - - let obj = util.overlays[window.document.documentURI]; - if (obj) { - obj = obj(window); - - function overlay(key, fn) { - for (let [elem, xml] in Iterator(obj[key] || {})) - if (elem = window.document.getElementById(elem)) - fn(elem, util.xmlToDom(xml, window.document)); - } - - overlay("before", function (elem, dom) elem.parentNode.insertBefore(dom, elem)); - overlay("after", function (elem, dom) elem.parentNode.insertBefore(dom, elem.nextSibling)); - overlay("append", function (elem, dom) elem.appendChild(dom)); - overlay("prepend", function (elem, dom) elem.insertBefore(dom, elem.firstChild)); - if (obj.init) - obj.init(window, event); - - if (obj.load) - window.document.addEventListener("load", function (event) { - if (event.originalTarget === event.target) - obj.load(window, event); - }, true); + if (event.originalTarget === window.document) { + window.removeEventListener("DOMContentLoaded", listener.wrapper, true); + util._loadOverlays(window); } }), true) } }, + _loadOverlays: function _loadOverlays(window) { + if (!window.dactylOverlays) + window.dactylOverlays = []; + + util.dump("load overlays", window.document.documentURI); + + for each (let obj in util.overlays[window.document.documentURI] || []) { + if (window.dactylOverlays.indexOf(obj) >= 0) + continue; + window.dactylOverlays.push(obj); + this._loadOverlay(window, obj(window)); + } + }, + _loadOverlay: function _loadOverlay(window, obj) { + let doc = window.document; + if (!doc.dactylOverlayElements) + doc.dactylOverlayElements = []; + + util.dump("load overlay", doc.documentURI, String(obj).substr(0, 60)); + + function overlay(key, fn) { + if (obj[key]) { + let iterator = Iterator(obj[key]); + if (!isObject(obj[key])) + iterator = ([elem.@id, elem.elements(), elem.@*::*.(function::name() != "id")] for each (elem in obj[key])); + + for (let [elem, xml, attr] in iterator) { + if (elem = doc.getElementById(elem)) { + let node = util.xmlToDom(xml, doc, obj.objects); + for (let n in array.iterValues(node.childNodes)) + doc.dactylOverlayElements.push(n); + fn(elem, node); + for each (let attr in attr || []) // FIXME: Cleanup... + elem.setAttributeNS(attr.namespace(), attr.localName(), attr); + } + } + } + } + + overlay("before", function (elem, dom) elem.parentNode.insertBefore(dom, elem)); + overlay("after", function (elem, dom) elem.parentNode.insertBefore(dom, elem.nextSibling)); + overlay("append", function (elem, dom) elem.appendChild(dom)); + overlay("prepend", function (elem, dom) elem.insertBefore(dom, elem.firstChild)); + if (obj.init) + obj.init(window); + + if (obj.load) + if (doc.readyState === "complete") + obj.load(window); + else + doc.addEventListener("load", wrapCallback(function load(event) { + if (event.originalTarget === event.target) { + doc.removeEventListener("load", load.wrapper, true); + obj.load(window, event); + } + }), true); + }, + overlayWindow: function (url, fn) { - Array.concat(url).forEach(function (url) { - this.overlays[url] = fn; - }, this); + if (url instanceof Ci.nsIDOMWindow) + util._loadOverlay(url, fn); + else { + Array.concat(url).forEach(function (url) { + if (!this.overlays[url]) + this.overlays[url] = []; + this.overlays[url].push(fn); + }, this); + + for (let win in iter(services.windowMediator.getEnumerator(null))) + if (["interactive", "complete"].indexOf(win.document.readyState) >= 0) + this._loadOverlays(win); + else + this.observe(win, "toplevel-window-ready"); + } }, /** @@ -1085,7 +1142,11 @@ const Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) this.dump(""); } catch (e) { - this.dump(e); + try { + this.dump(String(error)); + this.dump(error.stack) + } + catch (e) { dump(e + "\n"); } } },