diff --git a/mozilla/extensions/venkman/js/venkman-service.js b/mozilla/extensions/venkman/js/venkman-service.js index 77207d36a26..8294af74e17 100644 --- a/mozilla/extensions/venkman/js/venkman-service.js +++ b/mozilla/extensions/venkman/js/venkman-service.js @@ -36,13 +36,41 @@ /* components defined in this file */ const CLINE_SERVICE_CTRID = "@mozilla.org/commandlinehandler/general-startup;1?type=venkman"; -const CATMAN_CTRID = "@mozilla.org/categorymanager;1"; const CLINE_SERVICE_CID = Components.ID("{18269616-1dd2-11b2-afa8-b612439bda27}"); +const JSDPROT_HANDLER_CTRID = + "@mozilla.org/network/protocol;1?name=x-jsd"; +const JSDPROT_HANDLER_CID = + Components.ID("{12ec790d-304e-4525-89a9-3e723d489d14}"); -const nsICmdLineHandler = Components.interfaces.nsICmdLineHandler; -const nsICategoryManager = Components.interfaces.nsICategoryManager; -const nsISupports = Components.interfaces.nsISupports; +/* components used by this file */ +const CATMAN_CTRID = "@mozilla.org/categorymanager;1"; +const STRING_STREAM_CTRID = "@mozilla.org/io/string-input-stream;1"; +const MEDIATOR_CTRID = + "@mozilla.org/appshell/window-mediator;1"; +const SIMPLEURI_CTRID = "@mozilla.org/network/simple-uri;1"; + +const nsIWindowMediator = Components.interfaces.nsIWindowMediator; +const nsICmdLineHandler = Components.interfaces.nsICmdLineHandler; +const nsICategoryManager = Components.interfaces.nsICategoryManager; +const nsIProtocolHandler = Components.interfaces.nsIProtocolHandler; +const nsIURI = Components.interfaces.nsIURI; +const nsIURL = Components.interfaces.nsIURL; +const nsIStringInputStream = Components.interfaces.nsIStringInputStream; +const nsIChannel = Components.interfaces.nsIChannel; +const nsIRequest = Components.interfaces.nsIRequest; +const nsIProgressEventSink = Components.interfaces.nsIProgressEventSink; +const nsISupports = Components.interfaces.nsISupports; + +function findDebuggerWindow () +{ + var windowManager = + Components.classes[MEDIATOR_CTRID].getService(nsIWindowMediator); + + var window = windowManager.getMostRecentWindow("mozapp:venkman"); + + return window; +} /* Command Line handler service */ function CLineService() @@ -50,17 +78,17 @@ function CLineService() CLineService.prototype.commandLineArgument = "-venkman"; CLineService.prototype.prefNameForStartup = "general.startup.venkman"; -CLineService.prototype.chromeUrlForTask="chrome://venkman/content"; -CLineService.prototype.helpText = "Start with JavaScript debugger"; -CLineService.prototype.handlesArgs=false; -CLineService.prototype.defaultArgs =""; -CLineService.prototype.openWindowWithArgs=false; +CLineService.prototype.chromeUrlForTask = "chrome://venkman/content"; +CLineService.prototype.helpText = "Start with JavaScript Debugger."; +CLineService.prototype.handlesArgs = false; +CLineService.prototype.defaultArgs = ""; +CLineService.prototype.openWindowWithArgs = false; /* factory for command line handler service (CLineService) */ var CLineFactory = new Object(); CLineFactory.createInstance = -function (outer, iid) { +function clf_create (outer, iid) { if (outer != null) throw Components.results.NS_ERROR_NO_AGGREGATION; @@ -70,6 +98,277 @@ function (outer, iid) { return new CLineService(); } +/* x-jsd: protocol handler */ + +const JSD_DEFAULT_PORT = 2206; /* Dana's apartment number. */ + +/* protocol handler factory object */ +var JSDProtocolHandlerFactory = new Object(); + +JSDProtocolHandlerFactory.createInstance = +function jsdhf_create (outer, iid) { + if (outer != null) + throw Components.results.NS_ERROR_NO_AGGREGATION; + + if (!iid.equals(nsIProtocolHandler) && !iid.equals(nsISupports)) + throw Components.results.NS_ERROR_INVALID_ARG; + + return new JSDProtocolHandler(); +} + +function JSDURI (spec, charset) +{ + this.spec = this.prePath = spec; + this.charset = this.originCharset = charset; +} + +JSDURI.prototype.QueryInterface = +function jsdch_qi (iid) +{ + + if (!iid.equals(nsIURI) && !iid.equals(nsIURL) && + !iid.equals(nsISupports)) + throw Components.results.NS_ERROR_NO_INTERFACE; + + return this; +} + +JSDURI.prototype.scheme = "x-jsd"; + + +JSDURI.prototype.fileBaseName = +JSDURI.prototype.fileExtension = +JSDURI.prototype.filePath = +JSDURI.prototype.param = +JSDURI.prototype.query = +JSDURI.prototype.ref = +JSDURI.prototype.directory = +JSDURI.prototype.fileName = +JSDURI.prototype.username = +JSDURI.prototype.password = +JSDURI.prototype.hostPort = +JSDURI.prototype.path = +JSDURI.prototype.asciiHost = +JSDURI.prototype.userPass = ""; + +JSDURI.prototype.port = JSD_DEFAULT_PORT; + +JSDURI.prototype.schemeIs = +function jsduri_schemeis (scheme) +{ + return scheme.toLowerCase() == "x-jsd"; +} + +JSDURI.prototype.getCommonBaseSpec = +function jsduri_commonbase (uri) +{ + return "x-jsd:"; +} + +JSDURI.prototype.getRelativeSpec = +function jsduri_commonbase (uri) +{ + return uri; +} + +JSDURI.prototype.equals = +function jsduri_equals (uri) +{ + return uri.spec == this.spec; +} + +JSDURI.prototype.clone = +function jsduri_clone () +{ + return new JSDURI (this.spec); +} + +JSDURI.prototype.resolve = +function jsduri_resolve(path) +{ + //dump ("resolve " + path + " from " + this.spec + "\n"); + if (path[0] == "#") + return this.spec + path; + + return path; +} + +function JSDProtocolHandler() +{ + /* nothing here */ +} + +JSDProtocolHandler.prototype.scheme = "x-jsd"; +JSDProtocolHandler.prototype.defaultPort = JSD_DEFAULT_PORT; +JSDProtocolHandler.prototype.protocolFlags = nsIProtocolHandler.URI_NORELATIVE; + +JSDProtocolHandler.prototype.allowPort = +function jsdph_allowport (aPort, aScheme) +{ + return false; +} + +JSDProtocolHandler.prototype.newURI = +function jsdph_newuri (spec, charset, baseURI) +{ + if (baseURI) + { + debug ("-*- jsdHandler: aBaseURI passed to newURI, bailing.\n"); + return null; + } + + var clazz = Components.classes[SIMPLEURI_CTRID]; + var uri = clazz.createInstance(nsIURI); + uri.spec = spec; + return uri; +} + +JSDProtocolHandler.prototype.newChannel = +function jsdph_newchannel (uri) +{ + return new JSDChannel (uri); +} + +function JSDChannel (uri) +{ + this.URI = uri; + this.originalURI = uri; + this._isPending = true; + var clazz = Components.classes[STRING_STREAM_CTRID]; + this.stringStream = clazz.createInstance(nsIStringInputStream); +} + +JSDChannel.prototype.QueryInterface = +function jsdch_qi (iid) +{ + + if (!iid.equals(nsIChannel) && !iid.equals(nsIRequest) && + !iid.equals(nsISupports)) + throw Components.results.NS_ERROR_NO_INTERFACE; + + return this; +} + +/* nsIChannel */ +JSDChannel.prototype.loadAttributes = null; +JSDChannel.prototype.contentType = "text/html"; +JSDChannel.prototype.contentLength = -1; +JSDChannel.prototype.owner = null; +JSDChannel.prototype.loadGroup = null; +JSDChannel.prototype.notificationCallbacks = null; +JSDChannel.prototype.securityInfo = null; + +JSDChannel.prototype.open = +function jsdch_open() +{ + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; +} + +JSDChannel.prototype.asyncOpen = +function jsdch_aopen (streamListener, context) +{ + this.streamListener = streamListener; + this.context = context; + if (this.loadGroup) + this.loadGroup.addRequest (this, null); + + var window = findDebuggerWindow(); + var ary = this.URI.spec.match (/x-jsd:([^:]+)/); + var exception; + + if (window && "console" in window && ary) + { + try + { + window.asyncOpenJSDURL (this, streamListener, context); + return; + } + catch (ex) + { + exception = ex; + } + } + + var str = + "ErrorCould not load <" + + this.URI.spec + ">
"; + + if (!ary) + { + str += "Error parsing uri."; + } + else if (exception) + { + str += "Internal error: " + exception + "
" + 
+            exception.stack;
+    }
+    else
+    {
+        str += "Debugger is not running.";
+    }
+    
+    str += "";
+    
+    this.respond (str);
+}
+
+JSDChannel.prototype.respond =
+function jsdch_respond (str)
+{
+    this.streamListener.onStartRequest (this, this.context);
+
+    var len = str.length;
+    this.stringStream.setData (str, len);
+    this.streamListener.onDataAvailable (this, this.context,
+                                         this.stringStream, 0, len);
+    this.streamListener.onStopRequest (this, this.context,
+                                       Components.results.NS_OK);
+    if (this.loadGroup)
+        this.loadGroup.removeRequest (this, null, Components.results.NS_OK);
+    this._isPending = false;    
+}
+
+/* nsIRequest */
+JSDChannel.prototype.isPending =
+function jsdch_ispending ()
+{
+    return this._isPending;
+}
+
+JSDChannel.prototype.status = Components.results.NS_OK;
+
+JSDChannel.prototype.cancel =
+function jsdch_cancel (status)
+{
+    if (this._isPending)
+    {
+        this._isPending = false;
+        this.streamListener.onStopRequest (this, this.context, status);
+        if (this.loadGroup)
+        {
+            try
+            {
+                this.loadGroup.removeRequest (this, null, status);
+            }
+            catch (ex)
+            {
+                debug ("we're not in the load group?\n");
+            }
+        }
+    }
+    
+    this.status = status;
+}
+
+JSDChannel.prototype.suspend =
+JSDChannel.prototype.resume =
+function jsdch_notimpl ()
+{
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/*****************************************************************************/
+
 var Module = new Object();
 
 Module.registerSelf =
@@ -77,7 +376,8 @@ function (compMgr, fileSpec, location, type)
 {
     debug("*** Registering -venkman handler.\n");
     
-    compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+    compMgr =
+        compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
 
     compMgr.registerFactoryLocation(CLINE_SERVICE_CID,
                                     "Venkman CommandLine Service",
@@ -90,7 +390,26 @@ function (compMgr, fileSpec, location, type)
 	catman.addCategoryEntry("command-line-argument-handlers",
                             "venkman command line handler",
                             CLINE_SERVICE_CTRID, true, true);
-    
+
+    debug("*** Registering x-jsd protocol handler.\n");
+    compMgr.registerFactoryLocation(JSDPROT_HANDLER_CID,
+                                    "x-jsd protocol handler",
+                                    JSDPROT_HANDLER_CTRID, 
+                                    fileSpec, 
+                                    location,
+                                    type);
+    try
+    {
+        const JSD_CTRID = "@mozilla.org/js/jsd/debugger-service;1";
+        const jsdIDebuggerService = Components.interfaces.jsdIDebuggerService;
+        var jsds = Components.classes[JSD_CTRID].getService(jsdIDebuggerService);
+        jsds.initAtStartup = true;
+    }
+    catch (ex)
+    {
+        debug ("*** ERROR initializing debugger service");
+        debug (ex);
+    }
 }
 
 Module.unregisterSelf =
@@ -109,11 +428,8 @@ function (compMgr, cid, iid) {
     if (cid.equals(CLINE_SERVICE_CID))
         return CLineFactory;
 
-    if (cid.equals(IRCCNT_HANDLER_CID))
-        return IRCContentHandlerFactory;
-
-    if (cid.equals(IRCPROT_HANDLER_CID))
-        return IRCProtocolHandlerFactory;
+    if (cid.equals(JSDPROT_HANDLER_CID))
+        return JSDProtocolHandlerFactory;
     
     if (!iid.equals(Components.interfaces.nsIFactory))
         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
diff --git a/mozilla/extensions/venkman/resources/content/command-manager.js b/mozilla/extensions/venkman/resources/content/command-manager.js
index 6d0557198db..88cf64c8be5 100644
--- a/mozilla/extensions/venkman/resources/content/command-manager.js
+++ b/mozilla/extensions/venkman/resources/content/command-manager.js
@@ -33,7 +33,7 @@ function getAccessKey (str)
     return str[i + 1];
 }
 
-function CommandRecord (name, func, usage, help, label, flags)
+function CommandRecord (name, func, usage, help, label, flags, keystr)
 {
     this.name = name;
     this.func = func;
@@ -41,11 +41,13 @@ function CommandRecord (name, func, usage, help, label, flags)
     this.scanUsage();
     this.help = help;
     this.label = label ? label : name;
+    this.labelstr = label.replace ("&", "");
     this.flags = flags;
     this._enabled = true;
-    this.key = null;
-    this.keystr = MSG_VAL_NA;
-    this.uiElements = new Object();
+    this.keyNodes = new Array();
+    this.keystr = keystr;
+    this.uiElements = new Array();
+
 }
 
 CommandRecord.prototype.__defineGetter__ ("enabled", cr_getenable);
@@ -57,7 +59,7 @@ function cr_getenable ()
 CommandRecord.prototype.__defineSetter__ ("enabled", cr_setenable);
 function cr_setenable (state)
 {
-    for (var i in this.uiElements)
+    for (var i = 0; i < this.uiElements.length; ++i)
     {
         if (state)
             this.uiElements[i].removeAttribute ("disabled");
@@ -96,7 +98,7 @@ function cr_scanusage()
     var capNext = false;
     
     this._usage = spec;
-    this.argNames = new Array();    
+    this.argNames = new Array();
 
     for (var i = 0; i < len; ++i)
     {
@@ -141,7 +143,7 @@ function cr_getdocs(flagFormatter)
     var str;
     str  = getMsg(MSN_DOC_COMMANDLABEL,
                   [this.label.replace("&", ""), this.name]) + "\n";
-    str += getMsg(MSN_DOC_KEY, this.keystr) + "\n";
+    str += getMsg(MSN_DOC_KEY, this.keystr ? this.keystr : MSG_VAL_NA) + "\n";
     str += getMsg(MSN_DOC_SYNTAX, [this.name, this.usage]) + "\n";
     str += MSG_DOC_NOTES + "\n";
     str += (flagFormatter ? flagFormatter(this.flags) : this.flags) + "\n\n";
@@ -152,151 +154,137 @@ function cr_getdocs(flagFormatter)
 
 CommandRecord.prototype.argNames = new Array();
 
-function CommandManager ()
+function CommandManager (defaultBundle)
 {
     this.commands = new Object();
+    this.defaultBundle = defaultBundle;
+}
+
+CommandManager.prototype.defineCommands =
+function cmgr_defcmds (cmdary)
+{
+    var len = cmdary.length;
+    var commands = new Object();
+    var bundle =  ("stringBundle" in cmdary ?
+                   cmdary.stringBundle : 
+                   this.defaultBundle);
+    
+    for (var i = 0; i < len; ++i)
+    {
+        var name  = cmdary[i][0];
+        var func  = cmdary[i][1];
+        var flags = cmdary[i][2];
+        var usage = getMsgFrom(bundle, "cmd." + name + ".params", null, "");
+
+        var helpDefault;
+        var labelDefault = name;
+        var aliasFor;
+        if (flags & CMD_NO_HELP)
+            helpDefault = MSG_NO_HELP;
+
+        if (typeof func == "string")
+        {
+            var ary = func.match(/(\S+)/);
+            if (ary)
+                aliasFor = ary[1];
+            helpDefault = getMsg (MSN_DEFAULT_ALIAS_HELP, func); 
+            labelDefault = getMsgFrom (bundle,
+                                       "cmd." + aliasFor + ".label", null, name);
+        }
+
+        var label = getMsgFrom(bundle,
+                               "cmd." + name + ".label", null, labelDefault);
+        var help  = getMsgFrom(bundle,
+                               "cmd." + name + ".help", null, helpDefault);
+        var keystr = getMsgFrom (bundle, "cmd." + name + ".key", null, "");
+        var command = new CommandRecord (name, func, usage, help, label, flags,
+                                         keystr);
+        if (aliasFor)
+            command.aliasFor = aliasFor;
+        this.addCommand(command);
+        commands[name] = command;
+    }
+
+    return commands;
+}
+
+CommandManager.prototype.installKeys =
+function cmgr_instkeys (document, commands)
+{
+    var parentElem = document.getElementById("dynamic-keys");
+    if (!parentElem)
+    {
+        parentElem = document.createElement("keyset");
+        parentElem.setAttribute ("id", "dynamic-keys");
+        document.firstChild.appendChild (parentElem);
+    }
+
+    if (!commands)
+        commands = this.commands;
+    
+    for (var c in commands)
+        this.installKey (parentElem, commands[c]);
 }
 
 /**
- * Internal use only.
+ * Create a  node relative to a DOM node.  Usually called once per command,
+ * per document, so that accelerator keys work in all application windows.
  *
- * |showPopup| is called from the "onpopupshowing" event of menus
- * managed by the CommandManager.  If a command is disabled, represents a command
- * that cannot be "satisfied" by the current command context |cx|, or has an
- * "enabledif" attribute that eval()s to false, then the menuitem is disabled.
- * In addition "checkedif" and "visibleif" attributes are eval()d and
- * acted upon accordingly.
+ * @param parentElem  A reference to the DOM node which should contain the new
+ *                     node.
+ * @param command     reference to the CommandRecord to install.
  */
-CommandManager.showPopup =
-function cmgr_showpop (id)
+CommandManager.prototype.installKey =
+function cmgr_instkey (parentElem, command)
 {
-    /* returns true if the command context has the properties required to
-     * execute the command associated with |menuitem|.
-     */
-    function satisfied()
+    if (!command.keystr)
+        return;
+
+    var ary = command.keystr.match (/(.*\s)?([\S]+)$/);
+    if (!ASSERT(ary, "couldn't parse key string ``" + command.keystr +
+                "'' for command ``" + command.name + "''"))
     {
-        if (menuitem.hasAttribute("isSeparator"))
-            return true;
-
-        if (!("commandManager" in cx))
-        {
-            dd ("no commandManager in cx");
-            return false;
-        }
-        
-        var name = menuitem.getAttribute("commandname");
-        if (!ASSERT (name in cx.commandManager.commands,
-                     "menu contains unknown command '" + name + "'"))
-            return false;
-
-        var command = cx.commandManager.commands[name];
-
-        var rv = cx.commandManager.isCommandSatisfied(cx, command);
-        delete cx.parseError;
-        return rv;
+        return;
     }
     
-    /* Convenience function for "enabledif", etc, attributes. */
-    function has (prop)
-    {
-        return (prop in cx);
-    }
+    var key = document.createElement ("key");
+    key.setAttribute ("id", "key:" + command.name);
+    key.setAttribute ("oncommand", "dispatch('" + command.name + "');");
+    key.setAttribute ("modifiers", ary[1]);
+    if (ary[2].indexOf("VK_") == 0)
+        key.setAttribute ("keycode", ary[2]);
+    else
+        key.setAttribute ("key", ary[2]);
     
-    /* evals the attribute named |attr| on the node |node|. */
-    function evalIfAttribute (node, attr)
+    parentElem.appendChild(key);
+    command.keyNodes.push(key);
+}
+
+CommandManager.prototype.uninstallKeys =
+function cmgr_uninstkeys (commands)
+{
+    if (!commands)
+        commands = this.commands;
+    
+    for (var c in commands)
+        this.uninstallKey (commands[c]);
+}
+
+CommandManager.prototype.uninstallKey =
+function cmgr_uninstkey (command)
+{
+    for (var i in command.keyNodes)
     {
-        var ex;
-        var expr = node.getAttribute(attr);
-        if (!expr)
-            return true;
-        
-        expr = expr.replace (/\Wor\W/gi, " || ");
-        expr = expr.replace (/\Wand\W/gi, " && ");
         try
         {
-            return eval("(" + expr + ")");
+            /* document may no longer exist in a useful state. */
+            command.keyNodes[i].parentNode.removeChild(command.keyNodes[i]);
         }
         catch (ex)
         {
-            dd ("caught exception evaling '" + node.getAttribute("id") + "'.'" +
-                attr + "'\n" + ex);
+            dd ("*** caught exception uninstalling key node: " + ex);
         }
-        return true;
     }
-    
-    var cx;
-    
-    /* If the host provided a |contextFunction|, use it now.  Remember the
-     * return result as this.cx for use if something from this menu is actually
-     * dispatched.  this.cx is deleted in |hidePopup|. */
-    if (typeof this.contextFunction == "function")
-        cx = this.cx = this.contextFunction (id);
-    else
-        cx = new Object();
-
-    /* make this a member of cx so the |this| value is useful to attriutes. */
-
-    var popup = document.getElementById (id);
-    var menuitem = popup.firstChild;
-
-    do
-    {
-        /* should it be visible? */
-        if (menuitem.hasAttribute("visibleif"))
-        {
-            if (evalIfAttribute(menuitem, "visibleif"))
-                menuitem.removeAttribute ("hidden");
-            else
-            {
-                menuitem.setAttribute ("hidden", "true");
-                continue;
-            }
-        }
-        
-        /* ok, it's visible, maybe it should be disabled? */
-        if (satisfied())
-        {
-            if (menuitem.hasAttribute("enabledif"))
-            {
-                if (evalIfAttribute(menuitem, "enabledif"))
-                    menuitem.removeAttribute ("disabled");
-                else
-                    menuitem.setAttribute ("disabled", "true");
-            }
-            else
-                menuitem.removeAttribute ("disabled");
-        }
-        else
-        {
-            menuitem.setAttribute ("disabled", "true");
-        }
-        
-        /* should it have a check? */
-        if (menuitem.hasAttribute("checkedif"))
-        {
-            if (evalIfAttribute(menuitem, "checkedif"))
-                menuitem.setAttribute ("checked", "true");
-            else
-                menuitem.removeAttribute ("checked");
-        }
-        
-    } while ((menuitem = menuitem.nextSibling));
-
-    return true;
-}
-
-/**
- * Internal use only.
- *
- * |hidePopup| is called from the "onpopuphiding" event of menus
- * managed by the CommandManager.  Here we just clean up the context, which
- * would have (and may have) become the event object used by any command
- * dispatched while the menu was open.
- */
-CommandManager.hidePopup =
-function cmgr_hidepop (id)
-{
-    delete this.cx;
 }
 
 /**
@@ -308,255 +296,120 @@ function cmgr_add (command)
     this.commands[command.name] = command;
 }
 
-/**
- * Set the accelerator key used to trigger a command.  Multiple keys can be
- * assigned to a command, but only the last one will appear in the documentation
- * entry for the command.
- * @param parent   ID of the keyset to add the  to.
- * @param command  reference to the CommandRecord which should be dispatced.
- * @param keystr   String representation of the key, in the format
- *                  (|), where the parameter names
- *                 represent attributes on the  object.
- */
-CommandManager.prototype.setKey =
-function cmgr_add (parent, command, keystr)
+CommandManager.prototype.removeCommands = 
+function cmgr_removes (commands)
 {
-    var parentElem = document.getElementById(parent);
-    if (!ASSERT(parentElem, "setKey: couldn't get parent '" + parent +
-                "' for " + command.name))
-        return;
+    for (var c in commands)
+        this.removeCommand(commands[c]);
+}
 
-    var ary = keystr.match (/(.*\s)?([\S]+)$/);
-    var key = document.createElement ("key");
-    key.setAttribute ("id", "key:" + command.name);
-    key.setAttribute ("oncommand",
-                      "dispatch('" + command.name + "');");
-    key.setAttribute ("modifiers", ary[1]);
-    if (ary[2].indexOf("VK_") == 0)
-        key.setAttribute ("keycode", ary[2]);
+CommandManager.prototype.removeCommand = 
+function cmgr_remove (command)
+{
+    delete this.commands[command.name];
+}
+
+/**
+ * Register a hook for a particular command name.  |id| is a human readable
+ * identifier for the hook, and can be used to unregister the hook.  If you
+ * re-use a hook id, the previous hook function will be replaced.
+ * If |before| is |true|, the hook will be called *before* the command executes,
+ * if |before| is |false|, or not specified, the hook will be called *after*
+ * the command executes.
+ */
+CommandManager.prototype.addHook =
+function cmgr_hook (commandName, func, id, before)
+{
+    if (!ASSERT(commandName in this.commands,
+                "Unknown command '" + commandName + "'"))
+    {
+        return;
+    }
+    
+    var command = this.commands[commandName];
+    
+    if (before)
+    {
+        if (!("beforeHookNames" in command))
+            command.beforeHookNames = new Array();
+        if (!("beforeHooks" in command))
+            command.beforeHooks = new Object();
+        if (id in command.beforeHooks)
+        {
+            arrayRemoveAt(command.beforeHookNames,
+                          command.beforeHooks[id][id + "_hookIndex"]);
+        }
+        command.beforeHooks[id] = func;
+        command.beforeHookNames.push(id);
+    }
     else
-        key.setAttribute ("key", ary[2]);
-    
-    parentElem.appendChild(key);
-    command.key = key;
-    command.keystr = keystr;
+    {
+        if (!("afterHookNames" in command))
+            command.afterHookNames = new Array();
+        if (!("afterHooks" in command))
+            command.afterHooks = new Object();
+        func[id + "_hookIndex"] = command.afterHookNames.length
+        command.afterHooks[id] = func;
+        command.afterHookNames.push(id);
+    }
 }
 
-/**
- * Internal use only.
- *
- * Registers event handlers on a given menu.
- */
-CommandManager.prototype.hookPopup =
-function cmgr_hookpop (id)
+CommandManager.prototype.addHooks =
+function cmgr_hooks (hooks, prefix)
 {
-    var element = document.getElementById (id);
-    element.setAttribute ("onpopupshowing",
-                          "return CommandManager.showPopup('" + id + "');");
-    element.setAttribute ("onpopuphiding",
-                          "return CommandManager.hidePopup();");
+    if (!prefix)
+        prefix = "";
+    
+    for (var h in hooks)
+    {
+        this.addHook(h, hooks[h], prefix + ":" + h, 
+                     ("before" in hooks[h]) ? hooks[h].before : false);
+    }
 }
 
-/**
- * Appends a sub-menu to an existing menu.
- * @param parent  ID of the parent menu to add this submenu to.
- * @param id      ID of the sub-menu to add.
- * @param label   Text to use for this sub-menu.  The & character can be
- *                used to indicate the accesskey.
- * @param attribs Object containing CSS attributes to set on the element.
- */
-CommandManager.prototype.appendSubMenu =
-function cmgr_addsmenu (parent, id, label, attribs)
+CommandManager.prototype.removeHooks =
+function cmgr_remhooks (hooks, prefix)
 {
-    var menu = document.getElementById (id);
-    if (!menu)
-    {
-        var parentElem = document.getElementById(parent + "-popup");
-        if (!parentElem)
-            parentElem = document.getElementById(parent);
-        
-        if (!ASSERT(parentElem, "addSubMenu: couldn't get parent '" + parent +
-                    "' for " + id))
-            return;
+    if (!prefix)
+        prefix = "";
     
-        menu = document.createElement ("menu");
-        menu.setAttribute ("id", id);
-        parentElem.appendChild(menu);
-    }
-    
-    menu.setAttribute ("accesskey", getAccessKey(label));
-    menu.setAttribute ("label", label.replace("&", ""));
-    menu.setAttribute ("isSeparator", true);
-    var menupopup = document.createElement ("menupopup");
-    menupopup.setAttribute ("id", id + "-popup");
-    if (typeof attribs == "object")
+    for (var h in hooks)
     {
-        for (var p in attribs)
-            menupopup.setAttribute (p, attribs[p]);
+        this.removeHook(h, prefix + ":" + h, 
+                        ("before" in hooks[h]) ? hooks[h].before : false);
     }
-
-    menu.appendChild(menupopup);    
-    this.hookPopup (id + "-popup");
 }
 
-/**
- * Appends a popup to an existing popupset.
- * @param parent  ID of the popupset to add this popup to.
- * @param id      ID of the popup to add.
- * @param label   Text to use for this popup.  Popup menus don't normally have
- *                labels, but we set a "label" attribute anyway, in case
- *                the host wants it for some reason.  Any "&" characters will
- *                be stripped.
- * @param attribs Object containing CSS attributes to set on the element.
- */
-CommandManager.prototype.appendPopupMenu =
-function cmgr_addsmenu (parent, id, label, attribs)
+CommandManager.prototype.removeHook =
+function cmgr_unhook (commandName, id, before)
 {
-    var parentElem = document.getElementById (parent);
-    if (!ASSERT(parentElem, "addPopupMenu: couldn't get parent '" + parent +
-                "' for " + id))
-        return;
+    var command = this.commands[commandName];
 
-    var popup = document.createElement ("popup");
-    popup.setAttribute ("label", label.replace("&", ""));
-    popup.setAttribute ("id", id);
-    if (typeof attribs == "object")
+    if (before)
     {
-        for (var p in attribs)
-            popup.setAttribute (p, attribs[p]);
+        arrayRemoveAt(command.beforeHookNames,
+                      command.beforeHooks[id][id + "_hookIndex"]);
+        delete command.beforeHooks[id][id + "_hookIndex"];
+        delete command.beforeHooks[id];
+        if (keys(command.beforeHooks).length == 0)
+        {
+            delete command.beforeHookNames;
+            delete command.beforeHooks;
+        }
+    }
+    else
+    {
+        arrayRemoveAt(command.afterHookNames,
+                      command.afterHooks[id][id + "_hookIndex"]);
+        delete command.afterHooks[id][id + "_hookIndex"];
+        delete command.afterHooks[id];
+        if (command.afterHookNames.length == 0)
+        {
+            delete command.afterHookNames;
+            delete command.afterHooks;
+        }
     }
 
-    parentElem.appendChild(popup);    
-    this.hookPopup (id);
-}
-
-/**
- * Appends a menuitem to an existing menu or popup.
- * @param parent  ID of the popup to add this menuitem to.
- * @param command A reference to the CommandRecord this menu item will represent.
- * @param attribs Object containing CSS attributes to set on the element.
- */
-CommandManager.prototype.appendMenuItem =
-function cmgr_addmenu (parent, command, attribs)
-{
-    if (command == "-")
-    {
-        this.appendMenuSeparator(parent, attribs);
-        return;
-    }
-    
-    var parentElem = document.getElementById(parent + "-popup");
-    if (!parentElem)
-        parentElem = document.getElementById(parent);
-    
-    if (!ASSERT(parentElem, "appendMenuItem: couldn't get parent '" + parent +
-                "' for " + command.name))
-        return;
-    
-    var menuitem = document.createElement ("menuitem");
-    var id = parent + ":" + command.name;
-    menuitem.setAttribute ("id", id);
-    menuitem.setAttribute ("commandname", command.name);
-    menuitem.setAttribute ("key", "key:" + command.name);
-    menuitem.setAttribute ("accesskey", getAccessKey(command.label));
-    menuitem.setAttribute ("label", command.label.replace("&", ""));
-    menuitem.setAttribute ("oncommand",
-                           "dispatch('" + command.name + "');");
-    if (typeof attribs == "object")
-    {
-        for (var p in attribs)
-            menuitem.setAttribute (p, attribs[p]);
-    }
-    
-    command.uiElements[id] = menuitem;
-    parentElem.appendChild (menuitem);
-}
-
-/**
- * Appends a menuseparator to an existing menu or popup.
- * @param parent  ID of the popup to add this menuitem to.
- */
-CommandManager.prototype.appendMenuSeparator =
-function cmgr_addsep (parent)
-{
-    var parentElem = document.getElementById(parent + "-popup");
-    if (!parentElem)
-        parentElem = document.getElementById(parent);
-    
-    if (!ASSERT(parentElem, "appendMenuSeparator: couldn't get parent '" + 
-                parent + "'"))
-        return;
-    
-    var menuitem = document.createElement ("menuseparator");
-    menuitem.setAttribute ("isSeparator", true);
-    if (typeof attribs == "object")
-    {
-        for (var p in attribs)
-            menuitem.setAttribute (p, attribs[p]);
-    }
-    parentElem.appendChild (menuitem);
-}
-
-/**
- * Appends a toolbaritem to an existing box element.
- * @param parent  ID of the box to add this toolbaritem to.
- * @param command A reference to the CommandRecord this toolbaritem will
- *                represent.
- * @param attribs Object containing CSS attributes to set on the element.
- */
-CommandManager.prototype.appendToolbarItem =
-function cmgr_addtb (parent, command, attribs)
-{
-    if (command == "-")
-    {
-        this.appendToolbarSeparator(parent, attribs);
-        return;
-    }
-
-    var parentElem = document.getElementById(parent);
-    
-    if (!ASSERT(parentElem, "appendToolbarItem: couldn't get parent '" + parent +
-                "' for " + command.name))
-        return;
-    
-    var tbitem = document.createElement ("toolbarbutton");
-    // separate toolbar id's with a "-" character, because : intereferes with css
-    var id = parent + "-" + command.name;
-    tbitem.setAttribute ("id", id);
-    tbitem.setAttribute ("class", "toolbarbutton-1");
-    tbitem.setAttribute ("label", command.label.replace("&", ""));
-    tbitem.setAttribute ("oncommand",
-                         "dispatch('" + command.name + "');");
-    if (typeof attribs == "object")
-    {
-        for (var p in attribs)
-            tbitem.setAttribute (p, attribs[p]);
-    }
-    
-    command.uiElements[id] = tbitem;
-    parentElem.appendChild (tbitem);
-}
-
-/**
- * Appends a toolbarseparator to an existing box.
- * @param parent  ID of the box to add this toolbarseparator to.
- */
-CommandManager.prototype.appendToolbarSeparator =
-function cmgr_addmenu (parent)
-{
-    var parentElem = document.getElementById(parent);    
-    if (!ASSERT(parentElem, "appendToolbarSeparator: couldn't get parent '" + 
-                parent + "'"))
-        return;
-    
-    var tbitem = document.createElement ("toolbarseparator");
-    tbitem.setAttribute ("isSeparator", true);
-    if (typeof attribs == "object")
-    {
-        for (var p in attribs)
-            tbitem.setAttribute (p, attribs[p]);
-    }
-    parentElem.appendChild (tbitem);
 }
 
 /**
@@ -574,8 +427,8 @@ function cmgr_list (partialName, flags)
      * all commands if |partialName| is not specified */
     function compare (a, b)
     {
-        a = a.label.toLowerCase().replace("&", "");
-        b = b.label.toLowerCase().replace("&", "");
+        a = a.labelstr.toLowerCase();
+        b = b.labelstr.toLowerCase();
 
         if (a == b)
             return 0;
@@ -723,7 +576,7 @@ function parse_parseargsraw (e)
         }
     }
         
-    if (e.inputData)
+    if ("inputData" in e && e.inputData)
     {
         /* if data has been provided, parse it */
         e.unparsedData = e.inputData;
@@ -792,7 +645,7 @@ function parse_parseargsraw (e)
  * Returns true if |e| has the properties required to call the command |command|.
  * If |command| is not provided, |e.command| is used instead.
  * @param e        Event object to test against the command.
- * @param command  Command to text.
+ * @param command  Command to test.
  */
 CommandManager.prototype.isCommandSatisfied =
 function cmgr_isok (e, command)
@@ -821,7 +674,7 @@ function cmgr_isok (e, command)
 }
 
 /**
- * Internally use only.
+ * Internal use only.
  * See parseArguments above and the |argTypes| object below.
  *
  * Parses the next argument by calling an appropriate parser function, or the
@@ -868,7 +721,7 @@ function at_alias (list, type)
  * Parses an integer, stores result in |e[name]|.
  */
 CommandManager.prototype.argTypes["int"] =
-function parse_number (e, name)
+function parse_int (e, name)
 {
     var ary = e.unparsedData.match (/(\d+)(?:\s+(.*))?$/);
     if (!ary)
@@ -886,7 +739,7 @@ function parse_number (e, name)
  * Stores result in |e[name]|.
  */
 CommandManager.prototype.argTypes["word"] =
-function parse_number (e, name)
+function parse_word (e, name)
 {
     var ary = e.unparsedData.match (/(\S+)(?:\s+(.*))?$/);
     if (!ary)
@@ -905,13 +758,13 @@ function parse_number (e, name)
  * Stores result in |e[name]|.
  */
 CommandManager.prototype.argTypes["state"] =
-function parse_number (e, name)
+function parse_state (e, name)
 {
     var ary =
         e.unparsedData.match (/(true|on|yes|1|false|off|no|0)(?:\s+(.*))?$/i);
     if (!ary)
         return false;
-    if (ary[1].toLowerCase().search(/true|on|yes|1/i) != -1)
+    if (ary[1].search(/true|on|yes|1/i) != -1)
         e[name] = true;
     else
         e[name] = false;
@@ -929,7 +782,7 @@ function parse_number (e, name)
  * Stores result in |e[name]|.
  */
 CommandManager.prototype.argTypes["toggle"] =
-function parse_number (e, name)
+function parse_toggle (e, name)
 {
     var ary = e.unparsedData.match
         (/(toggle|true|on|yes|1|false|off|no|0)(?:\s+(.*))?$/i);
@@ -954,7 +807,7 @@ function parse_number (e, name)
  * Stores result in |e[name]|.
  */
 CommandManager.prototype.argTypes["rest"] =
-function parse_number (e, name)
+function parse_rest (e, name)
 {
     e[name] = e.unparsedData;
     e.unparsedData = "";
diff --git a/mozilla/extensions/venkman/resources/content/html-consts.js b/mozilla/extensions/venkman/resources/content/html-consts.js
index 0a988e9d272..283df86d636 100644
--- a/mozilla/extensions/venkman/resources/content/html-consts.js
+++ b/mozilla/extensions/venkman/resources/content/html-consts.js
@@ -92,6 +92,11 @@ function htmlBR(attribs)
     return HTML("html:br", attribs, argumentsAsArray(arguments, 1));
 }
 
+function htmlWBR(attribs)
+{
+    return HTML("html:wbr", attribs, argumentsAsArray(arguments, 1));
+}
+    
 function htmlImg(attribs, src)
 {
     var img = HTML("html:img", attribs, argumentsAsArray(arguments, 2));
diff --git a/mozilla/extensions/venkman/resources/content/menu-manager.js b/mozilla/extensions/venkman/resources/content/menu-manager.js
new file mode 100644
index 00000000000..4ba1a2ea8fe
--- /dev/null
+++ b/mozilla/extensions/venkman/resources/content/menu-manager.js
@@ -0,0 +1,497 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/ 
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License. 
+ *
+ * The Original Code is The JavaScript Debugger
+ * 
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation
+ * Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License (the "GPL"), in which case the
+ * provisions of the GPL are applicable instead of those above.
+ * If you wish to allow use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the MPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL.  If you do not delete
+ * the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * Contributor(s):
+ *  Robert Ginda, , original author
+ *
+ */
+
+function MenuManager (commandManager, menuSpecs, contextFunction, commandStr)
+{
+    var menuManager = this;
+
+    this.commandManager = commandManager;
+    this.menuSpecs = menuSpecs;
+    this.contextFunction = contextFunction;
+    this.commandStr = commandStr;
+
+    this.onPopupShowing =
+        function mmgr_onshow (event) { return menuManager.showPopup (event); };
+    this.onPopupHiding =
+        function mmgr_onhide (event) { return menuManager.hidePopup (event); };
+}
+
+/**
+ * Internal use only.
+ *
+ * Registers event handlers on a given menu.
+ */
+MenuManager.prototype.hookPopup =
+function mmgr_hookpop (node)
+{
+    node.addEventListener ("popupshowing", this.onPopupShowing, false);
+    node.addEventListener ("popuphiding",  this.onPopupHiding, false);
+}
+
+/**
+ * Internal use only.
+ *
+ * |showPopup| is called from the "onpopupshowing" event of menus
+ * managed by the CommandManager.  If a command is disabled, represents a command
+ * that cannot be "satisfied" by the current command context |cx|, or has an
+ * "enabledif" attribute that eval()s to false, then the menuitem is disabled.
+ * In addition "checkedif" and "visibleif" attributes are eval()d and
+ * acted upon accordingly.
+ */
+MenuManager.prototype.showPopup =
+function mmgr_showpop (event)
+{
+    //dd ("showPopup {");
+    /* returns true if the command context has the properties required to
+     * execute the command associated with |menuitem|.
+     */
+    function satisfied()
+    {
+        if (menuitem.hasAttribute("isSeparator"))
+            return true;
+
+        if (!("menuManager" in cx))
+        {
+            dd ("no menuManager in cx");
+            return false;
+        }
+        
+        var name = menuitem.getAttribute("commandname");
+        var commandManager = cx.menuManager.commandManager;
+        var commands = commandManager.commands;
+
+        if (!ASSERT (name in commands,
+                     "menu contains unknown command '" + name + "'"))
+        {
+            return false;
+        }
+
+        var rv = commandManager.isCommandSatisfied(cx, commands[name]);
+        delete cx.parseError;
+        return rv;
+    };
+    
+    /* Convenience function for "enabledif", etc, attributes. */
+    function has (prop)
+    {
+        return (prop in cx);
+    };
+    
+    /* evals the attribute named |attr| on the node |node|. */
+    function evalIfAttribute (node, attr)
+    {
+        var ex;
+        var expr = node.getAttribute(attr);
+        if (!expr)
+            return true;
+        
+        expr = expr.replace (/\Wand\W/gi, " && ");
+
+        try
+        {
+            return eval("(" + expr + ")");
+        }
+        catch (ex)
+        {
+            dd ("caught exception evaling '" + node.getAttribute("id") + "'.'" +
+                attr + "'\n" + ex);
+        }
+        return true;
+    };
+    
+    var cx;
+    var popup = event.originalTarget;
+    var menuitem = popup.firstChild;
+
+    
+    /* If the host provided a |contextFunction|, use it now.  Remember the
+     * return result as this.cx for use if something from this menu is actually
+     * dispatched.  this.cx is deleted in |hidePopup|. */
+    if (typeof this.contextFunction == "function")
+    {
+        cx = this.cx = this.contextFunction (popup.getAttribute("menuName"),
+                                             event);
+    }
+    else
+    {
+        cx = this.cx = { menuManager: this, originalEvent: event };
+    }
+
+    do
+    {
+        /* should it be visible? */
+        if (menuitem.hasAttribute("visibleif"))
+        {
+            if (evalIfAttribute(menuitem, "visibleif"))
+                menuitem.removeAttribute ("hidden");
+            else
+            {
+                menuitem.setAttribute ("hidden", "true");
+                continue;
+            }
+        }
+        
+        /* ok, it's visible, maybe it should be disabled? */
+        if (satisfied())
+        {
+            if (menuitem.hasAttribute("enabledif"))
+            {
+                if (evalIfAttribute(menuitem, "enabledif"))
+                    menuitem.removeAttribute ("disabled");
+                else
+                    menuitem.setAttribute ("disabled", "true");
+            }
+            else
+                menuitem.removeAttribute ("disabled");
+        }
+        else
+        {
+            menuitem.setAttribute ("disabled", "true");
+        }
+        
+        /* should it have a check? */
+        if (menuitem.hasAttribute("checkedif"))
+        {
+            if (evalIfAttribute(menuitem, "checkedif"))
+                menuitem.setAttribute ("checked", "true");
+            else
+                menuitem.removeAttribute ("checked");
+        }
+        
+    } while ((menuitem = menuitem.nextSibling));
+
+    //dd ("}");
+    
+    return true;
+}
+
+/**
+ * Internal use only.
+ *
+ * |hidePopup| is called from the "onpopuphiding" event of menus
+ * managed by the CommandManager.  Nothing to do here anymore.
+ * We used to just clean up this.cx, but that's a problem for nested
+ * menus.
+ */
+MenuManager.prototype.hidePopup =
+function mmgr_hidepop (id)
+{
+    return true;
+}
+
+/**
+ * Appends a sub-menu to an existing menu.
+ * @param parentNode  DOM Node to insert into
+ * @param beforeNode  DOM Node already contained by parentNode, to insert before
+ * @param id      ID of the sub-menu to add.
+ * @param label   Text to use for this sub-menu.  The & character can be
+ *                used to indicate the accesskey.
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendSubMenu =
+function mmgr_addsmenu (parentNode, beforeNode, menuName, domId, label, attribs)
+{
+    var document = parentNode.ownerDocument;
+    
+    /* sometimes the menu is already there, for overlay purposes. */
+    var menu = document.getElementById(domId);
+    
+    if (!menu)
+    {
+        menu = document.createElement ("menu");
+        menu.setAttribute ("id", domId);
+        parentNode.insertBefore(menu, beforeNode);
+    }
+
+    var menupopup = menu.firstChild;
+    
+    if (!menupopup)
+    {
+        menupopup = document.createElement ("menupopup");
+        menupopup.setAttribute ("id", domId + "-popup");
+        menu.appendChild(menupopup);    
+        menupopup = menu.firstChild;
+    }
+
+    menupopup.setAttribute ("menuName", menuName);
+
+    menu.setAttribute ("accesskey", getAccessKey(label));
+    menu.setAttribute ("label", label.replace("&", ""));
+    menu.setAttribute ("isSeparator", true);
+
+    if (typeof attribs == "object")
+    {
+        for (var p in attribs)
+            menu.setAttribute (p, attribs[p]);
+    }
+
+    this.hookPopup (menupopup);
+
+    return menupopup;
+}
+
+/**
+ * Appends a popup to an existing popupset.
+ * @param parentNode  DOM Node to insert into
+ * @param beforeNode  DOM Node already contained by parentNode, to insert before
+ * @param id      ID of the popup to add.
+ * @param label   Text to use for this popup.  Popup menus don't normally have
+ *                labels, but we set a "label" attribute anyway, in case
+ *                the host wants it for some reason.  Any "&" characters will
+ *                be stripped.
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendPopupMenu =
+function mmgr_addpmenu (parentNode, beforeNode, menuName, id, label, attribs)
+{
+    var document = parentNode.ownerDocument;
+    var popup = document.createElement ("popup");
+    popup.setAttribute ("id", id);
+    if (label)
+        popup.setAttribute ("label", label.replace("&", ""));
+    if (typeof attribs == "object")
+    {
+        for (var p in attribs)
+            popup.setAttribute (p, attribs[p]);
+    }
+
+    popup.setAttribute ("menuName", menuName);
+
+    parentNode.insertBefore(popup, beforeNode);
+    this.hookPopup (popup);
+
+    return popup;
+}
+
+/**
+ * Appends a menuitem to an existing menu or popup.
+ * @param parentNode  DOM Node to insert into
+ * @param beforeNode  DOM Node already contained by parentNode, to insert before
+ * @param command A reference to the CommandRecord this menu item will represent.
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendMenuItem =
+function mmgr_addmenu (parentNode, beforeNode, commandName, attribs)
+{
+    var menuManager = this;    
+    
+    var document = parentNode.ownerDocument;
+    if (commandName == "-")
+        return this.appendMenuSeparator(parentNode, beforeNode, attribs);
+    
+    var parentId = parentNode.getAttribute("id");
+    
+    if (!ASSERT(commandName in this.commandManager.commands,
+                "unknown command " + commandName + " targeted for " +
+                parentId))
+    {
+        return null;
+    }
+
+    var command = this.commandManager.commands[commandName];
+    var menuitem = document.createElement ("menuitem");
+    menuitem.setAttribute ("id", parentId + ":" + commandName);
+    menuitem.setAttribute ("commandname", command.name);
+    menuitem.setAttribute ("key", "key:" + command.name);
+    menuitem.setAttribute ("accesskey", getAccessKey(command.label));
+    menuitem.setAttribute ("label", command.label.replace("&", ""));
+    menuitem.setAttribute ("oncommand", this.commandStr);
+
+    if (typeof attribs == "object")
+    {
+        for (var p in attribs)
+            menuitem.setAttribute (p, attribs[p]);
+    }
+    
+    command.uiElements.push(menuitem);
+    parentNode.insertBefore (menuitem, beforeNode);
+
+    return menuitem;
+}
+
+/**
+ * Appends a menuseparator to an existing menu or popup.
+ * @param parentNode  DOM Node to insert into
+ * @param beforeNode  DOM Node already contained by parentNode, to insert before
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendMenuSeparator =
+function mmgr_addsep (parentNode, beforeNode, attribs)
+{
+    var document = parentNode.ownerDocument;
+    var menuitem = document.createElement ("menuseparator");
+    menuitem.setAttribute ("isSeparator", true);
+    if (typeof attribs == "object")
+    {
+        for (var p in attribs)
+            menuitem.setAttribute (p, attribs[p]);
+    }
+    parentNode.insertBefore (menuitem, beforeNode);
+
+    return menuitem;
+}
+
+/**
+ * Appends a toolbaritem to an existing box element.
+ * @param parentNode  DOM Node to insert into
+ * @param beforeNode  DOM Node already contained by parentNode, to insert before
+ * @param command A reference to the CommandRecord this toolbaritem will
+ *                represent.
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendToolbarItem =
+function mmgr_addtb (parentNode, beforeNode, commandName, attribs)
+{
+    if (commandName == "-")
+        return this.appendToolbarSeparator(parentNode, beforeNode, attribs);
+
+    var parentId = parentNode.getAttribute("id");
+    
+    if (!ASSERT(commandName in this.commandManager.commands,
+                "unknown command " + commandName + " targeted for " +
+                parentId))
+    {
+        return null;
+    }
+    
+    var command = this.commandManager.commands[commandName];
+    var document = parentNode.ownerDocument;    
+    var tbitem = document.createElement ("toolbarbutton");
+
+    var id = parentNode.getAttribute("id") + ":" + commandName;
+    tbitem.setAttribute ("id", id);
+    tbitem.setAttribute ("class", "toolbarbutton-1");
+    tbitem.setAttribute ("label", command.label.replace("&", ""));
+    tbitem.setAttribute ("oncommand",
+                         "dispatch('" + commandName + "');");
+    if (typeof attribs == "object")
+    {
+        for (var p in attribs)
+            tbitem.setAttribute (p, attribs[p]);
+    }
+    
+    command.uiElements.push(tbitem);
+    parentNode.insertBefore (tbitem, beforeNode);
+
+    return tbitem;
+}
+
+/**
+ * Appends a toolbarseparator to an existing box.
+ * @param parentNode  DOM Node to insert into
+ * @param beforeNode  DOM Node already contained by parentNode, to insert before
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendToolbarSeparator =
+function mmgr_addmenu (parentNode, beforeNode, attribs)
+{
+    var document = parentNode.ownerDocument;    
+    var tbitem = document.createElement ("toolbarseparator");
+    tbitem.setAttribute ("isSeparator", true);
+    if (typeof attribs == "object")
+    {
+        for (var p in attribs)
+            tbitem.setAttribute (p, attribs[p]);
+    }
+    parentNode.appendChild (tbitem);
+
+    return tbitem;
+}
+
+/**
+ * Creates menu DOM nodes from a menu specification.
+ * @param parentNode  DOM Node to insert into
+ * @param beforeNode  DOM Node already contained by parentNode, to insert before
+ * @param menuSpec    array of menu items
+ */
+MenuManager.prototype.createMenu =
+function mmgr_newmenu (parentNode, beforeNode, menuName, domId, attribs)
+{
+    if (typeof domId == "undefined")
+        domId = menuName;
+    
+    if (!ASSERT(menuName in this.menuSpecs, "unknown menu name " + menuName))
+        return null;
+
+    var menuSpec = this.menuSpecs[menuName];
+    
+    var subMenu = this.appendSubMenu (parentNode, beforeNode, menuName, domId,
+                                      menuSpec.label, attribs);
+
+    this.createMenuItems (subMenu, null, menuSpec.items);
+    return subMenu;
+}
+
+MenuManager.prototype.createMenuItems =
+function mmgr_newitems (parentNode, beforeNode, menuItems)
+{
+    function itemAttribs()
+    {
+        return (1 in menuItems[i]) ? menuItems[i][1] : null;
+    };
+
+    var parentId = parentNode.getAttribute("id");
+    
+    for (var i in menuItems)
+    {
+        var itemName = menuItems[i][0];
+        if (itemName[0] == ">")
+        {
+            itemName = itemName.substr(1);
+            if (!ASSERT(itemName in this.menuSpecs,
+                        "unknown submenu " + itemName + " referenced in " +
+                        parentId))
+            {
+                continue;
+            }
+            this.createMenu (parentNode, beforeNode, itemName,
+                             parentId + ":" + itemName, itemAttribs());
+        }
+        else if (itemName in this.commandManager.commands)
+        {
+            this.appendMenuItem (parentNode, beforeNode, itemName,
+                                 itemAttribs());
+        }
+        else if (itemName == "-")
+        {
+            this.appendMenuSeparator (parentNode, beforeNode, itemAttribs());
+        }
+        else
+        {
+            dd ("unknown command " + itemName + " referenced in " + parentId);
+        }
+    }
+        
+}
diff --git a/mozilla/extensions/venkman/resources/content/profile.csv.tpl b/mozilla/extensions/venkman/resources/content/profile.csv.tpl
new file mode 100644
index 00000000000..b21f081ff97
--- /dev/null
+++ b/mozilla/extensions/venkman/resources/content/profile.csv.tpl
@@ -0,0 +1,4 @@
+# full-url, file-name, function-name, start-line, end-line, call-count, recurse-depth, total-time, min-time, max-time, avg-time
+@-item-start
+$full-url, $file-name, $function-name, $start-line, $end-line, $call-count, $recurse-depth, $total-time, $min-time, $max-time, $avg-time
+@-item-end
diff --git a/mozilla/extensions/venkman/resources/content/profile.html.tpl b/mozilla/extensions/venkman/resources/content/profile.html.tpl
index fbfd811f252..6e6c993a020 100644
--- a/mozilla/extensions/venkman/resources/content/profile.html.tpl
+++ b/mozilla/extensions/venkman/resources/content/profile.html.tpl
@@ -70,14 +70,16 @@
     $user-agent
JavaScript Debugger Version: $venkman-agent
+ Sorted By: + $sort-key
- +@-section-start

$section-link

- +@-range-start

$range-min - $range-max ms

@@ -85,7 +87,7 @@ Next File | Previous Range | Next Range ] - +@-item-start @@ -103,12 +105,12 @@ width="$item-above-pct%"> - +@-item-end
- +@-range-end
- +@-section-end
No job is too big, no fee is too big. diff --git a/mozilla/extensions/venkman/resources/content/profile.txt.tpl b/mozilla/extensions/venkman/resources/content/profile.txt.tpl new file mode 100644 index 00000000000..3b9d1330fbb --- /dev/null +++ b/mozilla/extensions/venkman/resources/content/profile.txt.tpl @@ -0,0 +1,30 @@ + + Venkman Profile Report + +Created .......... $full-date +User Agent ....... $user-agent +Debugger Version . $venkman-agent +Sorted By ........ $sort-key + +================================================================================= +@-section-start +$section-number <$full-url> + +@-range-start + $file-name: $range-min - $range-max milliseconds +@-item-start + Function Name: $function-name (Lines $start-line - $end-line) + Total Calls: $call-count (max recurse $recurse-depth) + Total Time: $total-time (min/max/avg $min-time/$max-time/$avg-time) + +@-item-end + ------------------------------------------------------------------------------- + +@-range-end +================================================================================= + +@-section-end + + +Thanks for using Venkman, the Mozilla JavaScript Debugger. + diff --git a/mozilla/extensions/venkman/resources/content/profile.xml.tpl b/mozilla/extensions/venkman/resources/content/profile.xml.tpl new file mode 100644 index 00000000000..8303db94fb5 --- /dev/null +++ b/mozilla/extensions/venkman/resources/content/profile.xml.tpl @@ -0,0 +1,43 @@ + + + +@-section-start +
+@-range-start + +@-item-start + +@-item-end + +@-range-end +
+@-section-end +
diff --git a/mozilla/extensions/venkman/resources/content/tests/tree.js b/mozilla/extensions/venkman/resources/content/tests/tree.js index 2fa87c7183f..75a077f59d3 100644 --- a/mozilla/extensions/venkman/resources/content/tests/tree.js +++ b/mozilla/extensions/venkman/resources/content/tests/tree.js @@ -33,8 +33,7 @@ * */ -var sampleShare = new Object(); -var sampleTree = new TreeOView(sampleShare); +var sampleTree = new XULTreeView(); function SampleRecord (name, gender) { @@ -44,7 +43,7 @@ function SampleRecord (name, gender) this.gender = gender; } -SampleRecord.prototype = new TreeOViewRecord(sampleShare); +SampleRecord.prototype = new XULTreeViewRecord(sampleTree.share); sampleTree.childData.appendChild (new SampleRecord ("vinnie", "male")); var betty = new SampleRecord ("betty", "female"); @@ -108,7 +107,6 @@ function nativeFrameTest() { function compare(a, b) { - debugger; if (a > b) return 1; @@ -126,6 +124,7 @@ function dbg() { var a = 0; dbg2(); + nativeFrameTest(); var c = 0; } @@ -141,7 +140,33 @@ function dbg2() var fun = dbg; var obj = new Object(); debugger; - guessThis(); + + try + { + guessThis(); + throwSomething(); + } + catch (ex) + { + dd ("caught " + ex); + } + + var rv = returnSomething(); + dd ("returned " + rv); +} + +function throwSomething() +{ + var str = "this is a test"; + var obj = { message: "this is only a test" }; + throw "momma from the train"; +} + +function returnSomething() +{ + var str = "this is a test"; + var obj = { message: "this is only a test" }; + return "your library books on time!"; } function dt() @@ -178,4 +203,3 @@ function switchTest () break; } } - diff --git a/mozilla/extensions/venkman/resources/content/tests/tree.xul b/mozilla/extensions/venkman/resources/content/tests/tree.xul index 2310e3b922f..cf2568a1213 100644 --- a/mozilla/extensions/venkman/resources/content/tests/tree.xul +++ b/mozilla/extensions/venkman/resources/content/tests/tree.xul @@ -31,7 +31,6 @@ -