diff --git a/common/bootstrap.js b/common/bootstrap.js index 1138197c..1d64596b 100755 --- a/common/bootstrap.js +++ b/common/bootstrap.js @@ -10,12 +10,13 @@ const Cr = Components.results; Cu.import("resource://gre/modules/AddonManager.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); -const io = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); -const resourceProto = io.getProtocolHandler("resource") - .QueryInterface(Ci.nsIResProtocolHandler); +const resourceProto = Services.io.getProtocolHandler("resource") + .QueryInterface(Ci.nsIResProtocolHandler); const categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager); const manager = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); +const storage = Cc["@mozilla.org/fuel/application;1"].getService(Ci.fuelIApplication); function httpGet(url) { let xmlhttp = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); @@ -70,23 +71,27 @@ function FactoryProxy(url, classID) { FactoryProxy.prototype = { QueryInterface: XPCOMUtils.generateQI(Ci.nsIFactory), register: function () { + dump("dactyl: bootstrap: register: " + this.classID + " " + this.contractID + "\n"); manager.registerFactory(this.classID, String(this.classID), this.contractID, this); }, unregister: function () { + dump("dactyl: bootstrap: unregister: " + this.classID + " " + this.contractID + "\n"); manager.unregisterFactory(this.classID, this); }, get module() { - Class.replaceProperty(this, "module", {}); + Object.defineProperty(this, "module", { value: {}, enumerable: true }); Cu.import(this.url, this.module); return this.module; }, - createInstance: function () - let (factory = this.module.NSGetFactory(this.classID)) + createInstance: function (iids) { + dump("dactyl: bootstrap: createInstance: " + this.classID + " " + this.contractID + " " + iids + "\n"); + return let (factory = this.module.NSGetFactory(this.classID)) factory.createInstance.apply(factory, arguments) + } } function init() { @@ -133,6 +138,10 @@ function init() { } } + Cc["@dactyl.googlecode.com/base/xpc-interface-shim"].createInstance() + + Services.obs.notifyObservers(null, "dactyl-rehash", null); + Cu.import("resource://dactyl/base.jsm"); require(global, "prefs"); require(global, "services"); diff --git a/common/content/commandline.js b/common/content/commandline.js index 03ba6ecb..e4ca3534 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -136,7 +136,7 @@ const CommandWidgets = Class("CommandWidgets", { if (this.command && !options.get("guioptions").has("M")) return this.statusbar; let statusElem = this.statusbar.message; - if (value && statusElem.editor.rootElement.scrollWidth > statusElem.scrollWidth) + if (value && statusElem.inputField.editor.rootElement.scrollWidth > statusElem.scrollWidth) return this.commandbar; return this.activeGroup.mode; } @@ -164,9 +164,12 @@ const CommandWidgets = Class("CommandWidgets", { addElement: function (obj) { const self = this; this.elements[obj.name] = obj; + function get(id) obj.getElement ? obj.getElement(id) : document.getElementById(id); + this.active.__defineGetter__(obj.name, function () self.activeGroup[obj.name][obj.name]); this.activeGroup.__defineGetter__(obj.name, function () self.getGroup(obj.name)); + memoize(this.statusbar, obj.name, function () get("dactyl-statusline-field-" + (obj.id || obj.name))); memoize(this.commandbar, obj.name, function () get("dactyl-" + (obj.id || obj.name))); diff --git a/common/content/dactyl.js b/common/content/dactyl.js index 422fb9ad..b5e04249 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -38,6 +38,19 @@ const Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), }; }, + 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", {}); + }, + observe: { "dactyl-cleanup": function () { for (let [, mod] in iter(array(values(modules)).reverse())) @@ -65,20 +78,6 @@ const Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), return "unknown"; }), - cleanup: function () { - delete window.dactyl; - delete window.modules; - 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 {number} The current main mode. * @see modes#mainModes diff --git a/common/modules/overlay.jsm b/common/modules/overlay.jsm index 8036968d..15e9d73e 100644 --- a/common/modules/overlay.jsm +++ b/common/modules/overlay.jsm @@ -24,236 +24,239 @@ const ModuleBase = Class("ModuleBase", { toString: function () "[module " + this.constructor.className + "]" }); -util.overlayWindow("chrome://browser/content/browser.xul", function (window) ({ - init: function (document) { - /** - * @constructor Module - * - * Constructs a new ModuleBase class and makes arrangements for its - * initialization. Arguments marked as optional must be either - * entirely elided, or they must have the exact type specified. - * Loading semantics are as follows: - * - * - A module is guaranteed not to be initialized before any of its - * prerequisites as listed in its {@see ModuleBase#requires} member. - * - A module is considered initialized once it's been instantiated, - * its {@see Class#init} method has been called, and its - * instance has been installed into the top-level {@see modules} - * object. - * - Once the module has been initialized, its module-dependent - * initialization functions will be called as described hereafter. - * @param {string} name The module's name as it will appear in the - * top-level {@see modules} object. - * @param {ModuleBase} base The base class for this module. - * @optional - * @param {Object} prototype The prototype for instances of this - * object. The object itself is copied and not used as a prototype - * directly. - * @param {Object} classProperties The class properties for the new - * module constructor. - * @optional - * @param {Object} moduleInit The module initialization functions - * for the new module. Each function is called as soon as the named module - * has been initialized, but after the module itself. The constructors are - * guaranteed to be called in the same order that the dependent modules - * were initialized. - * @optional - * - * @returns {function} The constructor for the resulting module. - */ - function Module(name) { - let args = Array.slice(arguments); +const Overlay = Module("Overlay", { + init: function () { + util.overlayWindow("chrome://browser/content/browser.xul", function (window) ({ + init: function (document) { + /** + * @constructor Module + * + * Constructs a new ModuleBase class and makes arrangements for its + * initialization. Arguments marked as optional must be either + * entirely elided, or they must have the exact type specified. + * Loading semantics are as follows: + * + * - A module is guaranteed not to be initialized before any of its + * prerequisites as listed in its {@see ModuleBase#requires} member. + * - A module is considered initialized once it's been instantiated, + * its {@see Class#init} method has been called, and its + * instance has been installed into the top-level {@see modules} + * object. + * - Once the module has been initialized, its module-dependent + * initialization functions will be called as described hereafter. + * @param {string} name The module's name as it will appear in the + * top-level {@see modules} object. + * @param {ModuleBase} base The base class for this module. + * @optional + * @param {Object} prototype The prototype for instances of this + * object. The object itself is copied and not used as a prototype + * directly. + * @param {Object} classProperties The class properties for the new + * module constructor. + * @optional + * @param {Object} moduleInit The module initialization functions + * for the new module. Each function is called as soon as the named module + * has been initialized, but after the module itself. The constructors are + * guaranteed to be called in the same order that the dependent modules + * were initialized. + * @optional + * + * @returns {function} The constructor for the resulting module. + */ + function Module(name) { + let args = Array.slice(arguments); - var base = ModuleBase; - if (callable(args[1])) - base = args.splice(1, 1)[0]; - let [, prototype, classProperties, moduleInit] = args; - const module = Class(name, base, prototype, classProperties); + var base = ModuleBase; + if (callable(args[1])) + base = args.splice(1, 1)[0]; + let [, prototype, classProperties, moduleInit] = args; + const module = Class(name, base, prototype, classProperties); - module.INIT = moduleInit || {}; - module.prototype.INIT = module.INIT; - module.requires = prototype.requires || []; - Module.list.push(module); - Module.constructors[name] = module; - return module; - } - Module.list = []; - Module.constructors = {}; + module.INIT = moduleInit || {}; + module.prototype.INIT = module.INIT; + module.requires = prototype.requires || []; + Module.list.push(module); + Module.constructors[name] = module; + return module; + } + Module.list = []; + Module.constructors = {}; - const create = window.Object.create || function (proto) { - let res = window.Object(); - object.__proto__ = proto; - return object; - } + const create = window.Object.create || function (proto) { + let res = window.Object(); + object.__proto__ = proto; + return object; + } - const jsmodules = {}; - const modules = update(create(jsmodules), { + const jsmodules = {}; + const modules = update(create(jsmodules), { - jsmodules: jsmodules, + jsmodules: jsmodules, - get content() this.config.browser.contentWindow || window.content, + get content() this.config.browser.contentWindow || window.content, - window: window, + window: window, - Module: Module, + Module: Module, - load: function load(script) { - for (let [i, base] in Iterator(prefix)) { - try { - services.subscriptLoader.loadSubScript(base + script + ".js", modules, "UTF-8"); - return; - } - catch (e) { - if (typeof e !== "string") { - util.dump("Trying: " + (base + script + ".js") + ":"); + load: function load(script) { + for (let [i, base] in Iterator(prefix)) { + try { + services.subscriptLoader.loadSubScript(base + script + ".js", modules, "UTF-8"); + return; + } + catch (e) { + if (typeof e !== "string") { + util.dump("Trying: " + (base + script + ".js") + ":"); + util.reportError(e); + } + } + } + try { + Cu.import("resource://dactyl/" + script + ".jsm", jsmodules); + } + catch (e) { + util.dump("Loading script " + script + ":"); util.reportError(e); } + }, + + newContext: function newContext(proto) { + let sandbox = Components.utils.Sandbox(window, { sandboxPrototype: proto || modules, wantXrays: false }); + // Hack: + sandbox.Object = jsmodules.Object; + sandbox.Math = jsmodules.Math; + sandbox.__proto__ = proto || modules; + return sandbox; + } + }); + modules.modules = modules; + window.dactyl = { modules: modules }; + + const BASE = "chrome://dactyl/content/"; + + let prefix = [BASE]; + + modules.load("util"); + modules.load("services"); + prefix.unshift("chrome://" + modules.services["dactyl:"].name + "/content/"); + + ["base", + "overlay", + "prefs", + "storage", + "javascript", + "dactyl", + "modes", + "abbreviations", + "autocommands", + "buffer", + "commandline", + "commands", + "completion", + "configbase", + "config", + "editor", + "events", + "finder", + "highlight", + "hints", + "io", + "mappings", + "marks", + "options", + "statusline", + "styles", + "template" + ].forEach(modules.load); + + modules.Config.prototype.scripts.forEach(modules.load); + }, + load: function (document) { + var { modules, Module } = window.dactyl.modules; + delete window.dactyl; + + Module.list.forEach(function (module) { + modules.__defineGetter__(module.className, function () { + delete modules[module.className]; + return load(module.className, null, Components.stack.caller); + }); + }); + + const start = Date.now(); + const deferredInit = { load: [] }; + const seen = set(); + const loaded = set(["init"]); + modules.loaded = loaded; + + function init(module) { + function init(func, mod) + function () defineModule.time(module.className || module.constructor.className, mod, + func, module, + modules.dactyl, modules, window); + + set.add(loaded, module.constructor.className); + for (let [mod, func] in Iterator(module.INIT)) { + if (mod in loaded) + init(func, mod)(); + else { + deferredInit[mod] = deferredInit[mod] || []; + deferredInit[mod].push(init(func, mod)); + } } } - try { - Cu.import("resource://dactyl/" + script + ".jsm", jsmodules); + defineModule.modules.map(init); + + function load(module, prereq, frame) { + if (isString(module)) { + if (!Module.constructors.hasOwnProperty(module)) + modules.load(module); + module = Module.constructors[module]; + } + + try { + if (module.className in loaded) + return; + if (module.className in seen) + throw Error("Module dependency loop."); + set.add(seen, module.className); + + for (let dep in values(module.requires)) + load(Module.constructors[dep], module.className); + + defineModule.loadLog.push("Load" + (isString(prereq) ? " " + prereq + " dependency: " : ": ") + module.className); + if (frame && frame.filename) + defineModule.loadLog.push(" from: " + frame.filename + ":" + frame.lineNumber); + + delete modules[module.className]; + modules[module.className] = defineModule.time(module.className, "init", module); + + init(modules[module.className]); + for (let [, fn] in iter(deferredInit[module.className] || [])) + fn(); + } + catch (e) { + util.dump("Loading " + (module && module.className) + ":"); + util.reportError(e); + } + return modules[module.className]; } - catch (e) { - util.dump("Loading script " + script + ":"); - util.reportError(e); - } - }, - newContext: function newContext(proto) { - let sandbox = Components.utils.Sandbox(window, { sandboxPrototype: proto || modules, wantXrays: false }); - // Hack: - sandbox.Object = jsmodules.Object; - sandbox.Math = jsmodules.Math; - sandbox.__proto__ = proto || modules; - return sandbox; + Module.list.forEach(load); + deferredInit["load"].forEach(call); + modules.times = update({}, defineModule.times); + + util.dump("Loaded in " + (Date.now() - start) + "ms"); + + modules.events.addSessionListener(window, "unload", function onUnload() { + window.removeEventListener("unload", onUnload, false); + for (let [, mod] in iter(modules)) + if (mod instanceof ModuleBase && "destroy" in mod) + mod.destroy(); + }, false); } - }); - modules.modules = modules; - window.dactyl = { modules: modules }; - - const BASE = "chrome://dactyl/content/"; - - let prefix = [BASE]; - - modules.load("util"); - modules.load("services"); - prefix.unshift("chrome://" + modules.services["dactyl:"].name + "/content/"); - - ["base", - "overlay", - "prefs", - "storage", - "javascript", - "dactyl", - "modes", - "abbreviations", - "autocommands", - "buffer", - "commandline", - "commands", - "completion", - "configbase", - "config", - "editor", - "events", - "finder", - "highlight", - "hints", - "io", - "mappings", - "marks", - "options", - "statusline", - "styles", - "template" - ].forEach(modules.load); - - modules.Config.prototype.scripts.forEach(modules.load); - }, - load: function (document) { - var { modules, Module } = window.dactyl.modules; - delete window.dactyl; - - Module.list.forEach(function (module) { - modules.__defineGetter__(module.className, function () { - delete modules[module.className]; - return load(module.className, null, Components.stack.caller); - }); - }); - - const start = Date.now(); - const deferredInit = { load: [] }; - const seen = set(); - const loaded = set(["init"]); - modules.loaded = loaded; - - function init(module) { - function init(func, mod) - function () defineModule.time(module.className || module.constructor.className, mod, - func, module, - modules.dactyl, modules, window); - - set.add(loaded, module.constructor.className); - for (let [mod, func] in Iterator(module.INIT)) { - if (mod in loaded) - init(func, mod)(); - else { - deferredInit[mod] = deferredInit[mod] || []; - deferredInit[mod].push(init(func, mod)); - } - } - } - defineModule.modules.map(init); - - function load(module, prereq, frame) { - if (isString(module)) { - if (!Module.constructors.hasOwnProperty(module)) - modules.load(module); - module = Module.constructors[module]; - } - - try { - if (module.className in loaded) - return; - if (module.className in seen) - throw Error("Module dependency loop."); - set.add(seen, module.className); - - for (let dep in values(module.requires)) - load(Module.constructors[dep], module.className); - - defineModule.loadLog.push("Load" + (isString(prereq) ? " " + prereq + " dependency: " : ": ") + module.className); - if (frame && frame.filename) - defineModule.loadLog.push(" from: " + frame.filename + ":" + frame.lineNumber); - - delete modules[module.className]; - modules[module.className] = defineModule.time(module.className, "init", module); - - init(modules[module.className]); - for (let [, fn] in iter(deferredInit[module.className] || [])) - fn(); - } - catch (e) { - util.dump("Loading " + (module && module.className) + ":"); - util.reportError(e); - } - return modules[module.className]; - } - - Module.list.forEach(load); - deferredInit["load"].forEach(call); - modules.times = update({}, defineModule.times); - - util.dump("Loaded in " + (Date.now() - start) + "ms"); - - modules.events.addSessionListener(window, "unload", function onUnload() { - window.removeEventListener("unload", onUnload, false); - for (let [, mod] in iter(modules)) - if (mod instanceof ModuleBase && "destroy" in mod) - mod.destroy(); - }, false); + })); } -})); - +}); // vim: set fdm=marker sw=4 ts=4 et: diff --git a/common/modules/services.jsm b/common/modules/services.jsm index 38212cf8..d8cad7e0 100644 --- a/common/modules/services.jsm +++ b/common/modules/services.jsm @@ -75,6 +75,7 @@ const Services = Module("Services", { if (!this.extensionManager) Components.utils.import("resource://gre/modules/AddonManager.jsm"); }, + reinit: function () {}, _create: function (classes, ifaces, meth, init, args) { try { diff --git a/common/modules/util.jsm b/common/modules/util.jsm index d06bb0b7..be16ed9e 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -55,10 +55,13 @@ const Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) }, cleanup: function cleanup() { - for (let win in iter(services.windowMediator.getEnumerator(null))) - for (let elem in values(win.document.dactylOverlayElements)) + for (let win in iter(services.windowMediator.getEnumerator(null))) { + for (let elem in values(win.document.dactylOverlayElements || [])) if (elem.get() && elem.get().parentNode) elem.get().parentNode.removeChild(elem.get()); + delete win.document.dactylOverlayElements; + delete win.document.dactylOverlays; + } }, // FIXME: Only works for Pentadactyl @@ -962,6 +965,7 @@ const Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) observe: { "dactyl-cleanup": function () { + util.dump("dactyl: util: observe: dactyl-cleanup"); // Let window cleanup functions run synchronously before we // destroy modules. util.timeout(function () { @@ -969,6 +973,9 @@ const Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) if (module.cleanup) module.cleanup(); + services.observer.addObserver(this, "dactyl-rehash", true); + + /* let getOwnPropertyNames = Object.getOwnPropertyNames; for each (let global in defineModule.globals.reverse()) for each (let k in getOwnPropertyNames(global)) @@ -976,8 +983,17 @@ const Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) delete global[k]; } catch (e) {} + */ }); }, + "dactyl-rehash": function () { + util.dump("dactyl: util: observe: dactyl-rehash"); + for (let module in values(defineModule.modules)) + if (module.reinit) + module.reinit(); + else + module.init(); + }, "toplevel-window-ready": function (window, data) { window.addEventListener("DOMContentLoaded", wrapCallback(function listener(event) { if (event.originalTarget === window.document) { diff --git a/common/skin/dactyl.css b/common/skin/dactyl.css index ae35761b..200c3500 100644 --- a/common/skin/dactyl.css +++ b/common/skin/dactyl.css @@ -1,6 +1,6 @@ @namespace dactyl url("http://vimperator.org/namespaces/liberator"); @namespace html url("http://www.w3.org/1999/xhtml"); -@namespace xul uri("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); +@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* Applied to all content */ [dactyl|activeframe] {