author | Michael Ratcliffe <mratcliffe@mozilla.com> |
Tue, 14 Aug 2012 15:51:48 +0100 | |
changeset 103442 | ebb525cd2e7018a51b30c4a687fd66505f471828 |
parent 103441 | f4596ef17eed20290eb9b62fb2089272544d4ac9 |
child 103443 | efd1509146d56880a49c04dbac6c937fbb089fab |
push id | 13991 |
push user | ryanvm@gmail.com |
push date | Sun, 26 Aug 2012 02:29:03 +0000 |
treeherder | mozilla-inbound@c4f20a024113 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jwalker |
bugs | 776875 |
milestone | 17.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdAddon.jsm @@ -0,0 +1,290 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", + "resource://gre/modules/AddonManager.jsm"); + +/** + * 'addon' command. + */ +gcli.addCommand({ + name: "addon", + description: gcli.lookup("addonDesc") +}); + +/** + * 'addon list' command. + */ +gcli.addCommand({ + name: "addon list", + description: gcli.lookup("addonListDesc"), + params: [{ + name: 'type', + type: { + name: 'selection', + data: ["dictionary", "extension", "locale", "plugin", "theme", "all"] + }, + defaultValue: 'all', + description: gcli.lookup("addonListTypeDesc"), + }], + exec: function(aArgs, context) { + function representEnabledAddon(aAddon) { + return "<li><![CDATA[" + aAddon.name + "\u2002" + aAddon.version + + getAddonStatus(aAddon) + "]]></li>"; + } + + function representDisabledAddon(aAddon) { + return "<li class=\"gcli-addon-disabled\">" + + "<![CDATA[" + aAddon.name + "\u2002" + aAddon.version + aAddon.version + + "]]></li>"; + } + + function getAddonStatus(aAddon) { + let operations = []; + + if (aAddon.pendingOperations & AddonManager.PENDING_ENABLE) { + operations.push("PENDING_ENABLE"); + } + + if (aAddon.pendingOperations & AddonManager.PENDING_DISABLE) { + operations.push("PENDING_DISABLE"); + } + + if (aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL) { + operations.push("PENDING_UNINSTALL"); + } + + if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL) { + operations.push("PENDING_INSTALL"); + } + + if (aAddon.pendingOperations & AddonManager.PENDING_UPGRADE) { + operations.push("PENDING_UPGRADE"); + } + + if (operations.length) { + return " (" + operations.join(", ") + ")"; + } + return ""; + } + + /** + * Compares two addons by their name. Used in sorting. + */ + function compareAddonNames(aNameA, aNameB) { + return String.localeCompare(aNameA.name, aNameB.name); + } + + /** + * Resolves the promise which is the scope (this) of this function, filling + * it with an HTML representation of the passed add-ons. + */ + function list(aType, aAddons) { + if (!aAddons.length) { + this.resolve(gcli.lookup("addonNoneOfType")); + } + + // Separate the enabled add-ons from the disabled ones. + let enabledAddons = []; + let disabledAddons = []; + + aAddons.forEach(function(aAddon) { + if (aAddon.isActive) { + enabledAddons.push(aAddon); + } else { + disabledAddons.push(aAddon); + } + }); + + let header; + switch(aType) { + case "dictionary": + header = gcli.lookup("addonListDictionaryHeading"); + break; + case "extension": + header = gcli.lookup("addonListExtensionHeading"); + break; + case "locale": + header = gcli.lookup("addonListLocaleHeading"); + break; + case "plugin": + header = gcli.lookup("addonListPluginHeading"); + break; + case "theme": + header = gcli.lookup("addonListThemeHeading"); + case "all": + header = gcli.lookup("addonListAllHeading"); + break; + default: + header = gcli.lookup("addonListUnknownHeading"); + } + + // Map and sort the add-ons, and create an HTML list. + this.resolve(header + + "<ol>" + + enabledAddons.sort(compareAddonNames).map(representEnabledAddon).join("") + + disabledAddons.sort(compareAddonNames).map(representDisabledAddon).join("") + + "</ol>"); + } + + // Create the promise that will be resolved when the add-on listing has + // been finished. + let promise = context.createPromise(); + let types = aArgs.type == "all" ? null : [aArgs.type]; + AddonManager.getAddonsByTypes(types, list.bind(promise, aArgs.type)); + return promise; + } +}); + +// We need a list of addon names for the enable and disable commands. Because +// getting the name list is async we do not add the commands until we have the +// list. +AddonManager.getAllAddons(function addonAsync(aAddons) { + // We listen for installs to keep our addon list up to date. There is no need + // to listen for uninstalls because uninstalled addons are simply disabled + // until restart (to enable undo functionality). + AddonManager.addAddonListener({ + onInstalled: function(aAddon) { + addonNameCache.push({ + name: representAddon(aAddon).replace(/\s/g, "_"), + value: aAddon.name + }); + }, + onUninstalled: function(aAddon) { + let name = representAddon(aAddon).replace(/\s/g, "_"); + + for (let i = 0; i < addonNameCache.length; i++) { + if(addonNameCache[i].name == name) { + addonNameCache.splice(i, 1); + break; + } + } + }, + }); + + /** + * Returns a string that represents the passed add-on. + */ + function representAddon(aAddon) { + let name = aAddon.name + " " + aAddon.version; + return name.trim(); + } + + let addonNameCache = []; + + // The name parameter, used in "addon enable" and "addon disable." + let nameParameter = { + name: "name", + type: { + name: "selection", + lookup: addonNameCache + }, + description: gcli.lookup("addonNameDesc") + }; + + for (let addon of aAddons) { + addonNameCache.push({ + name: representAddon(addon).replace(/\s/g, "_"), + value: addon.name + }); + } + + /** + * 'addon enable' command. + */ + gcli.addCommand({ + name: "addon enable", + description: gcli.lookup("addonEnableDesc"), + params: [nameParameter], + exec: function(aArgs, context) { + /** + * Enables the addon in the passed list which has a name that matches + * according to the passed name comparer, and resolves the promise which + * is the scope (this) of this function to display the result of this + * enable attempt. + */ + function enable(aName, addons) { + // Find the add-on. + let addon = null; + addons.some(function(candidate) { + if (candidate.name == aName) { + addon = candidate; + return true; + } else { + return false; + } + }); + + let name = representAddon(addon); + + if (!addon.userDisabled) { + this.resolve("<![CDATA[" + + gcli.lookupFormat("addonAlreadyEnabled", [name]) + "]]>"); + } else { + addon.userDisabled = false; + // nl-nl: {$1} is ingeschakeld. + this.resolve("<![CDATA[" + + gcli.lookupFormat("addonEnabled", [name]) + "]]>"); + } + } + + let promise = context.createPromise(); + // List the installed add-ons, enable one when done listing. + AddonManager.getAllAddons(enable.bind(promise, aArgs.name)); + return promise; + } + }); + + /** + * 'addon disable' command. + */ + gcli.addCommand({ + name: "addon disable", + description: gcli.lookup("addonDisableDesc"), + params: [nameParameter], + exec: function(aArgs, context) { + /** + * Like enable, but ... you know ... the exact opposite. + */ + function disable(aName, addons) { + // Find the add-on. + let addon = null; + addons.some(function(candidate) { + if (candidate.name == aName) { + addon = candidate; + return true; + } else { + return false; + } + }); + + let name = representAddon(addon); + + if (addon.userDisabled) { + this.resolve("<![CDATA[" + + gcli.lookupFormat("addonAlreadyDisabled", [name]) + "]]>"); + } else { + addon.userDisabled = true; + // nl-nl: {$1} is uitgeschakeld. + this.resolve("<![CDATA[" + + gcli.lookupFormat("addonDisabled", [name]) + "]]>"); + } + } + + let promise = context.createPromise(); + // List the installed add-ons, disable one when done listing. + AddonManager.getAllAddons(disable.bind(promise, aArgs.name)); + return promise; + } + }); + Services.obs.notifyObservers(null, "gcli_addon_commands_ready", null); +});
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdBreak.jsm @@ -0,0 +1,170 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "HUDService", + "resource:///modules/HUDService.jsm"); + +/** + * 'break' command + */ +gcli.addCommand({ + name: "break", + description: gcli.lookup("breakDesc"), + manual: gcli.lookup("breakManual") +}); + + +/** + * 'break list' command + */ +gcli.addCommand({ + name: "break list", + description: gcli.lookup("breaklistDesc"), + returnType: "html", + exec: function(args, context) { + let win = HUDService.currentContext(); + let dbg = win.DebuggerUI.getDebugger(); + if (!dbg) { + return gcli.lookup("breakaddDebuggerStopped"); + } + let breakpoints = dbg.breakpoints; + + if (Object.keys(breakpoints).length === 0) { + return gcli.lookup("breaklistNone"); + } + + let reply = gcli.lookup("breaklistIntro"); + reply += "<ol>"; + for each (let breakpoint in breakpoints) { + let text = gcli.lookupFormat("breaklistLineEntry", + [breakpoint.location.url, + breakpoint.location.line]); + reply += "<li>" + text + "</li>"; + }; + reply += "</ol>"; + return reply; + } +}); + + +/** + * 'break add' command + */ +gcli.addCommand({ + name: "break add", + description: gcli.lookup("breakaddDesc"), + manual: gcli.lookup("breakaddManual") +}); + +/** + * 'break add line' command + */ +gcli.addCommand({ + name: "break add line", + description: gcli.lookup("breakaddlineDesc"), + params: [ + { + name: "file", + type: { + name: "selection", + data: function() { + let win = HUDService.currentContext(); + let dbg = win.DebuggerUI.getDebugger(); + let files = []; + if (dbg) { + let scriptsView = dbg.contentWindow.DebuggerView.Scripts; + for each (let script in scriptsView.scriptLocations) { + files.push(script); + } + } + return files; + } + }, + description: gcli.lookup("breakaddlineFileDesc") + }, + { + name: "line", + type: { name: "number", min: 1, step: 10 }, + description: gcli.lookup("breakaddlineLineDesc") + } + ], + returnType: "html", + exec: function(args, context) { + args.type = "line"; + let win = HUDService.currentContext(); + let dbg = win.DebuggerUI.getDebugger(); + if (!dbg) { + return gcli.lookup("breakaddDebuggerStopped"); + } + var promise = context.createPromise(); + let position = { url: args.file, line: args.line }; + dbg.addBreakpoint(position, function(aBreakpoint, aError) { + if (aError) { + promise.resolve(gcli.lookupFormat("breakaddFailed", [aError])); + return; + } + promise.resolve(gcli.lookup("breakaddAdded")); + }); + return promise; + } +}); + + +/** + * 'break del' command + */ +gcli.addCommand({ + name: "break del", + description: gcli.lookup("breakdelDesc"), + params: [ + { + name: "breakid", + type: { + name: "number", + min: 0, + max: function() { + let win = HUDService.currentContext(); + let dbg = win.DebuggerUI.getDebugger(); + if (!dbg) { + return gcli.lookup("breakaddDebuggerStopped"); + } + return Object.keys(dbg.breakpoints).length - 1; + }, + }, + description: gcli.lookup("breakdelBreakidDesc") + } + ], + returnType: "html", + exec: function(args, context) { + let win = HUDService.currentContext(); + let dbg = win.DebuggerUI.getDebugger(); + if (!dbg) { + return gcli.lookup("breakaddDebuggerStopped"); + } + + let breakpoints = dbg.breakpoints; + let id = Object.keys(dbg.breakpoints)[args.breakid]; + if (!id || !(id in breakpoints)) { + return gcli.lookup("breakNotFound"); + } + + let promise = context.createPromise(); + try { + dbg.removeBreakpoint(breakpoints[id], function() { + promise.resolve(gcli.lookup("breakdelRemoved")); + }); + } catch (ex) { + // If the debugger has been closed already, don't scare the user. + promise.resolve(gcli.lookup("breakdelRemoved")); + } + return promise; + } +});
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdCalllog.jsm @@ -0,0 +1,103 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "HUDService", + "resource:///modules/HUDService.jsm"); + +XPCOMUtils.defineLazyGetter(this, "Debugger", function() { + let JsDebugger = {}; + Components.utils.import("resource://gre/modules/jsdebugger.jsm", JsDebugger); + + let global = Components.utils.getGlobalForObject({}); + JsDebugger.addDebuggerToGlobal(global); + + return global.Debugger; +}); + +let debuggers = []; + +/** + * 'calllog' command + */ +gcli.addCommand({ + name: "calllog", + description: gcli.lookup("calllogDesc") +}) + +/** + * 'calllog start' command + */ +gcli.addCommand({ + name: "calllog start", + description: gcli.lookup("calllogStartDesc"), + + exec: function(args, context) { + let contentWindow = context.environment.contentDocument.defaultView; + + let dbg = new Debugger(contentWindow); + dbg.onEnterFrame = function(frame) { + // BUG 773652 - Make the output from the GCLI calllog command nicer + contentWindow.console.log("Method call: " + this.callDescription(frame)); + }.bind(this); + + debuggers.push(dbg); + + let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab; + HUDService.activateHUDForContext(tab); + + return gcli.lookup("calllogStartReply"); + }, + + callDescription: function(frame) { + let name = "<anonymous>"; + if (frame.callee.name) { + name = frame.callee.name; + } + else { + let desc = frame.callee.getOwnPropertyDescriptor("displayName"); + if (desc && desc.value && typeof desc.value == "string") { + name = desc.value; + } + } + + let args = frame.arguments.map(this.valueToString).join(", "); + return name + "(" + args + ")"; + }, + + valueToString: function(value) { + if (typeof value !== "object" || value === null) { + return uneval(value); + } + return "[object " + value.class + "]"; + } +}); + +/** + * 'calllog stop' command + */ +gcli.addCommand({ + name: "calllog stop", + description: gcli.lookup("calllogStopDesc"), + + exec: function(args, context) { + let numDebuggers = debuggers.length; + if (numDebuggers == 0) { + return gcli.lookup("calllogStopNoLogging"); + } + + for (let dbg of debuggers) { + dbg.onEnterFrame = undefined; + } + debuggers = []; + + return gcli.lookupFormat("calllogStopReply", [ numDebuggers ]); + } +});
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdCmd.jsm @@ -0,0 +1,126 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +let EXPORTED_SYMBOLS = [ "CmdCommands" ]; + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource:///modules/devtools/gcli.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +let prefSvc = "@mozilla.org/preferences-service;1"; +XPCOMUtils.defineLazyGetter(this, "prefBranch", function() { + let prefService = Cc[prefSvc].getService(Ci.nsIPrefService); + return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2); +}); + +XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", + "resource://gre/modules/NetUtil.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "console", + "resource:///modules/devtools/Console.jsm"); + +/** + * A place to store the names of the commands that we have added as a result of + * calling refreshAutoCommands(). Used by refreshAutoCommands to remove the + * added commands. + */ +let commands = []; + +/** + * Exported API + */ +let CmdCommands = { + /** + * Called to look in a directory pointed at by the devtools.commands.dir pref + * for *.mozcmd files which are then loaded. + * @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which + * we eval the script from the .mozcmd file. This should be a chrome window. + */ + refreshAutoCommands: function GC_refreshAutoCommands(aSandboxPrincipal) { + // First get rid of the last set of commands + commands.forEach(function(name) { + gcli.removeCommand(name); + }); + + let dirName = prefBranch.getComplexValue("devtools.commands.dir", + Ci.nsISupportsString).data; + if (dirName == "") { + return; + } + + let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + dir.initWithPath(dirName); + if (!dir.exists() || !dir.isDirectory()) { + throw new Error('\'' + dirName + '\' is not a directory.'); + } + + let en = dir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator); + + while (true) { + let file = en.nextFile; + if (!file) { + break; + } + if (file.leafName.match(/.*\.mozcmd$/) && file.isFile() && file.isReadable()) { + loadCommandFile(file, aSandboxPrincipal); + } + } + }, +}; + +/** + * Load the commands from a single file + * @param nsIFile aFile The file containing the commands that we should read + * @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which + * we eval the script from the .mozcmd file. This should be a chrome window. + */ +function loadCommandFile(aFile, aSandboxPrincipal) { + NetUtil.asyncFetch(aFile, function refresh_fetch(aStream, aStatus) { + if (!Components.isSuccessCode(aStatus)) { + console.error("NetUtil.asyncFetch(" + aFile.path + ",..) failed. Status=" + aStatus); + return; + } + + let source = NetUtil.readInputStreamToString(aStream, aStream.available()); + aStream.close(); + + let sandbox = new Cu.Sandbox(aSandboxPrincipal, { + sandboxPrototype: aSandboxPrincipal, + wantXrays: false, + sandboxName: aFile.path + }); + let data = Cu.evalInSandbox(source, sandbox, "1.8", aFile.leafName, 1); + + if (!Array.isArray(data)) { + console.error("Command file '" + aFile.leafName + "' does not have top level array."); + return; + } + + data.forEach(function(commandSpec) { + gcli.addCommand(commandSpec); + commands.push(commandSpec.name); + }); + }.bind(this)); +} + +/** + * 'cmd' command + */ +gcli.addCommand({ + name: "cmd", + description: gcli.lookup("cmdDesc"), + hidden: true +}); + +/** + * 'cmd refresh' command + */ +gcli.addCommand({ + name: "cmd refresh", + description: gcli.lookup("cmdRefreshDesc"), + hidden: true, + exec: function Command_cmdRefresh(args, context) { + GcliCmdCommands.refreshAutoCommands(context.environment.chromeDocument.defaultView); + } +});
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdConsole.jsm @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "HUDService", + "resource:///modules/HUDService.jsm"); + +/** + * 'console' command + */ +gcli.addCommand({ + name: "console", + description: gcli.lookup("consoleDesc"), + manual: gcli.lookup("consoleManual") +}); + +/** + * 'console clear' command + */ +gcli.addCommand({ + name: "console clear", + description: gcli.lookup("consoleclearDesc"), + exec: function Command_consoleClear(args, context) { + let window = context.environment.contentDocument.defaultView; + let hud = HUDService.getHudByWindow(window); + // hud will be null if the web console has not been opened for this window + if (hud) { + hud.jsterm.clearOutput(); + } + } +}); + +/** + * 'console close' command + */ +gcli.addCommand({ + name: "console close", + description: gcli.lookup("consolecloseDesc"), + exec: function Command_consoleClose(args, context) { + let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab + HUDService.deactivateHUDForContext(tab); + } +}); + +/** + * 'console open' command + */ +gcli.addCommand({ + name: "console open", + description: gcli.lookup("consoleopenDesc"), + exec: function Command_consoleOpen(args, context) { + let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab + HUDService.activateHUDForContext(tab); + } +});
rename from browser/devtools/commandline/GcliCookieCommands.jsm rename to browser/devtools/commandline/CmdCookie.jsm --- a/browser/devtools/commandline/GcliCookieCommands.jsm +++ b/browser/devtools/commandline/CmdCookie.jsm @@ -1,17 +1,21 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; let EXPORTED_SYMBOLS = [ ]; -Components.utils.import("resource:///modules/devtools/gcli.jsm"); +Cu.import("resource:///modules/devtools/gcli.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "console", + "resource:///modules/devtools/Console.jsm"); // We should really be using nsICookieManager so we can read more than just the // key/value of cookies. The difficulty is filtering the cookies that are // relevant to the current page. See // https://github.com/firebug/firebug/blob/master/extension/content/firebug/cookies/cookieObserver.js#L123 // For details on how this is done with Firebug /**
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdDbg.jsm @@ -0,0 +1,136 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +/** + * 'dbg' command + */ +gcli.addCommand({ + name: "dbg", + description: gcli.lookup("dbgDesc"), + manual: gcli.lookup("dbgManual") +}); + + +/** + * 'dbg interrupt' command + */ +gcli.addCommand({ + name: "dbg interrupt", + description: gcli.lookup("dbgInterrupt"), + params: [], + exec: function(args, context) { + let win = context.environment.chromeDocument.defaultView; + let dbg = win.DebuggerUI.getDebugger(); + + if (dbg) { + let controller = dbg.contentWindow.DebuggerController; + let thread = controller.activeThread; + if (!thread.paused) { + thread.interrupt(); + } + } + } +}); + +/** + * 'dbg continue' command + */ +gcli.addCommand({ + name: "dbg continue", + description: gcli.lookup("dbgContinue"), + params: [], + exec: function(args, context) { + let win = context.environment.chromeDocument.defaultView; + let dbg = win.DebuggerUI.getDebugger(); + + if (dbg) { + let controller = dbg.contentWindow.DebuggerController; + let thread = controller.activeThread; + if (thread.paused) { + thread.resume(); + } + } + } +}); + + +/** + * 'dbg step' command + */ +gcli.addCommand({ + name: "dbg step", + description: gcli.lookup("dbgStepDesc"), + manual: gcli.lookup("dbgStepManual") +}); + + +/** + * 'dbg step over' command + */ +gcli.addCommand({ + name: "dbg step over", + description: gcli.lookup("dbgStepOverDesc"), + params: [], + exec: function(args, context) { + let win = context.environment.chromeDocument.defaultView; + let dbg = win.DebuggerUI.getDebugger(); + + if (dbg) { + let controller = dbg.contentWindow.DebuggerController; + let thread = controller.activeThread; + if (thread.paused) { + thread.stepOver(); + } + } + } +}); + +/** + * 'dbg step in' command + */ +gcli.addCommand({ + name: 'dbg step in', + description: gcli.lookup("dbgStepInDesc"), + params: [], + exec: function(args, context) { + let win = context.environment.chromeDocument.defaultView; + let dbg = win.DebuggerUI.getDebugger(); + + if (dbg) { + let controller = dbg.contentWindow.DebuggerController; + let thread = controller.activeThread; + if (thread.paused) { + thread.stepIn(); + } + } + } +}); + +/** + * 'dbg step over' command + */ +gcli.addCommand({ + name: 'dbg step out', + description: gcli.lookup("dbgStepOutDesc"), + params: [], + exec: function(args, context) { + let win = context.environment.chromeDocument.defaultView; + let dbg = win.DebuggerUI.getDebugger(); + + if (dbg) { + let controller = dbg.contentWindow.DebuggerController; + let thread = controller.activeThread; + if (thread.paused) { + thread.stepOut(); + } + } + } +});
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdEcho.jsm @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); + +/** + * 'echo' command + */ +gcli.addCommand({ + name: "echo", + description: gcli.lookup("echoDesc"), + params: [ + { + name: "message", + type: "string", + description: gcli.lookup("echoMessageDesc") + } + ], + returnType: "string", + hidden: true, + exec: function Command_echo(args, context) { + return args.message; + } +});
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdExport.jsm @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); + +/** + * 'export' command + */ +gcli.addCommand({ + name: "export", + description: gcli.lookup("exportDesc"), +}); + +/** + * The 'export html' command. This command allows the user to export the page to + * HTML after they do DOM changes. + */ +gcli.addCommand({ + name: "export html", + description: gcli.lookup("exportHtmlDesc"), + exec: function(args, context) { + let document = context.environment.contentDocument; + let window = document.defaultView; + let page = document.documentElement.outerHTML; + window.open('data:text/plain;charset=utf8,' + encodeURIComponent(page)); + } +});
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdJsb.jsm @@ -0,0 +1,138 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; +const XMLHttpRequest = + Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1"); + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource:///modules/devtools/gcli.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "js_beautify", + "resource:///modules/devtools/Jsbeautify.jsm"); + +/** + * jsb command. + */ +gcli.addCommand({ + name: 'jsb', + description: gcli.lookup('jsbDesc'), + returnValue:'string', + hidden: true, + params: [ + { + name: 'url', + type: 'string', + description: gcli.lookup('jsbUrlDesc'), + manual: 'The URL of the JS to prettify' + }, + { + name: 'indentSize', + type: 'number', + description: gcli.lookup('jsbIndentSizeDesc'), + manual: gcli.lookup('jsbIndentSizeManual'), + defaultValue: 2 + }, + { + name: 'indentChar', + type: { + name: 'selection', + lookup: [{name: "space", value: " "}, {name: "tab", value: "\t"}] + }, + description: gcli.lookup('jsbIndentCharDesc'), + manual: gcli.lookup('jsbIndentCharManual'), + defaultValue: ' ', + }, + { + name: 'preserveNewlines', + type: 'boolean', + description: gcli.lookup('jsbPreserveNewlinesDesc'), + manual: gcli.lookup('jsbPreserveNewlinesManual'), + defaultValue: true + }, + { + name: 'preserveMaxNewlines', + type: 'number', + description: gcli.lookup('jsbPreserveMaxNewlinesDesc'), + manual: gcli.lookup('jsbPreserveMaxNewlinesManual'), + defaultValue: -1 + }, + { + name: 'jslintHappy', + type: 'boolean', + description: gcli.lookup('jsbJslintHappyDesc'), + manual: gcli.lookup('jsbJslintHappyManual'), + defaultValue: false + }, + { + name: 'braceStyle', + type: { + name: 'selection', + data: ['collapse', 'expand', 'end-expand', 'expand-strict'] + }, + description: gcli.lookup('jsbBraceStyleDesc'), + manual: gcli.lookup('jsbBraceStyleManual'), + defaultValue: "collapse" + }, + { + name: 'spaceBeforeConditional', + type: 'boolean', + description: gcli.lookup('jsbSpaceBeforeConditionalDesc'), + manual: gcli.lookup('jsbSpaceBeforeConditionalManual'), + defaultValue: true + }, + { + name: 'unescapeStrings', + type: 'boolean', + description: gcli.lookup('jsbUnescapeStringsDesc'), + manual: gcli.lookup('jsbUnescapeStringsManual'), + defaultValue: false + } + ], + exec: function(args, context) { + let opts = { + indent_size: args.indentSize, + indent_char: args.indentChar, + preserve_newlines: args.preserveNewlines, + max_preserve_newlines: args.preserveMaxNewlines == -1 ? + undefined : args.preserveMaxNewlines, + jslint_happy: args.jslintHappy, + brace_style: args.braceStyle, + space_before_conditional: args.spaceBeforeConditional, + unescape_strings: args.unescapeStrings + } + + let xhr = new XMLHttpRequest(); + + try { + xhr.open("GET", args.url, true); + } catch(e) { + return gcli.lookup('jsbInvalidURL'); + } + + let promise = context.createPromise(); + + xhr.onreadystatechange = function(aEvt) { + if (xhr.readyState == 4) { + if (xhr.status == 200 || xhr.status == 0) { + let browserDoc = context.environment.chromeDocument; + let browserWindow = browserDoc.defaultView; + let browser = browserWindow.gBrowser; + + browser.selectedTab = browser.addTab("data:text/plain;base64," + + browserWindow.btoa(js_beautify(xhr.responseText, opts))); + promise.resolve(); + } + else { + promise.resolve("Unable to load page to beautify: " + args.url + " " + + xhr.status + " " + xhr.statusText); + } + }; + } + xhr.send(null); + return promise; + } +});
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdPagemod.jsm @@ -0,0 +1,264 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); + +/** + * 'pagemod' command + */ +gcli.addCommand({ + name: "pagemod", + description: gcli.lookup("pagemodDesc"), +}); + +/** + * The 'pagemod replace' command. This command allows the user to search and + * replace within text nodes and attributes. + */ +gcli.addCommand({ + name: "pagemod replace", + description: gcli.lookup("pagemodReplaceDesc"), + params: [ + { + name: "search", + type: "string", + description: gcli.lookup("pagemodReplaceSearchDesc"), + }, + { + name: "replace", + type: "string", + description: gcli.lookup("pagemodReplaceReplaceDesc"), + }, + { + name: "ignoreCase", + type: "boolean", + description: gcli.lookup("pagemodReplaceIgnoreCaseDesc"), + }, + { + name: "selector", + type: "string", + description: gcli.lookup("pagemodReplaceSelectorDesc"), + defaultValue: "*:not(script):not(style):not(embed):not(object):not(frame):not(iframe):not(frameset)", + }, + { + name: "root", + type: "node", + description: gcli.lookup("pagemodReplaceRootDesc"), + defaultValue: null, + }, + { + name: "attrOnly", + type: "boolean", + description: gcli.lookup("pagemodReplaceAttrOnlyDesc"), + }, + { + name: "contentOnly", + type: "boolean", + description: gcli.lookup("pagemodReplaceContentOnlyDesc"), + }, + { + name: "attributes", + type: "string", + description: gcli.lookup("pagemodReplaceAttributesDesc"), + defaultValue: null, + }, + ], + exec: function(args, context) { + let document = context.environment.contentDocument; + let searchTextNodes = !args.attrOnly; + let searchAttributes = !args.contentOnly; + let regexOptions = args.ignoreCase ? 'ig' : 'g'; + let search = new RegExp(escapeRegex(args.search), regexOptions); + let attributeRegex = null; + if (args.attributes) { + attributeRegex = new RegExp(args.attributes, regexOptions); + } + + let root = args.root || document; + let elements = root.querySelectorAll(args.selector); + elements = Array.prototype.slice.call(elements); + + let replacedTextNodes = 0; + let replacedAttributes = 0; + + function replaceAttribute() { + replacedAttributes++; + return args.replace; + } + function replaceTextNode() { + replacedTextNodes++; + return args.replace; + } + + for (let i = 0; i < elements.length; i++) { + let element = elements[i]; + if (searchTextNodes) { + for (let y = 0; y < element.childNodes.length; y++) { + let node = element.childNodes[y]; + if (node.nodeType == node.TEXT_NODE) { + node.textContent = node.textContent.replace(search, replaceTextNode); + } + } + } + + if (searchAttributes) { + if (!element.attributes) { + continue; + } + for (let y = 0; y < element.attributes.length; y++) { + let attr = element.attributes[y]; + if (!attributeRegex || attributeRegex.test(attr.name)) { + attr.value = attr.value.replace(search, replaceAttribute); + } + } + } + } + + return gcli.lookupFormat("pagemodReplaceResult", + [elements.length, replacedTextNodes, + replacedAttributes]); + } +}); + +/** + * 'pagemod remove' command + */ +gcli.addCommand({ + name: "pagemod remove", + description: gcli.lookup("pagemodRemoveDesc"), +}); + + +/** + * The 'pagemod remove element' command. + */ +gcli.addCommand({ + name: "pagemod remove element", + description: gcli.lookup("pagemodRemoveElementDesc"), + params: [ + { + name: "search", + type: "string", + description: gcli.lookup("pagemodRemoveElementSearchDesc"), + }, + { + name: "root", + type: "node", + description: gcli.lookup("pagemodRemoveElementRootDesc"), + defaultValue: null, + }, + { + name: 'stripOnly', + type: 'boolean', + description: gcli.lookup("pagemodRemoveElementStripOnlyDesc"), + }, + { + name: 'ifEmptyOnly', + type: 'boolean', + description: gcli.lookup("pagemodRemoveElementIfEmptyOnlyDesc"), + }, + ], + exec: function(args, context) { + let document = context.environment.contentDocument; + let root = args.root || document; + let elements = Array.prototype.slice.call(root.querySelectorAll(args.search)); + + let removed = 0; + for (let i = 0; i < elements.length; i++) { + let element = elements[i]; + let parentNode = element.parentNode; + if (!parentNode || !element.removeChild) { + continue; + } + if (args.stripOnly) { + while (element.hasChildNodes()) { + parentNode.insertBefore(element.childNodes[0], element); + } + } + if (!args.ifEmptyOnly || !element.hasChildNodes()) { + element.parentNode.removeChild(element); + removed++; + } + } + + return gcli.lookupFormat("pagemodRemoveElementResultMatchedAndRemovedElements", + [elements.length, removed]); + } +}); + +/** + * The 'pagemod remove attribute' command. + */ +gcli.addCommand({ + name: "pagemod remove attribute", + description: gcli.lookup("pagemodRemoveAttributeDesc"), + params: [ + { + name: "searchAttributes", + type: "string", + description: gcli.lookup("pagemodRemoveAttributeSearchAttributesDesc"), + }, + { + name: "searchElements", + type: "string", + description: gcli.lookup("pagemodRemoveAttributeSearchElementsDesc"), + }, + { + name: "root", + type: "node", + description: gcli.lookup("pagemodRemoveAttributeRootDesc"), + defaultValue: null, + }, + { + name: "ignoreCase", + type: "boolean", + description: gcli.lookup("pagemodRemoveAttributeIgnoreCaseDesc"), + }, + ], + exec: function(args, context) { + let document = context.environment.contentDocument; + + let root = args.root || document; + let regexOptions = args.ignoreCase ? 'ig' : 'g'; + let attributeRegex = new RegExp(args.searchAttributes, regexOptions); + let elements = root.querySelectorAll(args.searchElements); + elements = Array.prototype.slice.call(elements); + + let removed = 0; + for (let i = 0; i < elements.length; i++) { + let element = elements[i]; + if (!element.attributes) { + continue; + } + + var attrs = Array.prototype.slice.call(element.attributes); + for (let y = 0; y < attrs.length; y++) { + let attr = attrs[y]; + if (attributeRegex.test(attr.name)) { + element.removeAttribute(attr.name); + removed++; + } + } + } + + return gcli.lookupFormat("pagemodRemoveAttributeResult", + [elements.length, removed]); + } +}); + +/** + * Make a given string safe to use in a regular expression. + * + * @param string aString + * The string you want to use in a regex. + * @return string + * The equivalent of |aString| but safe to use in a regex. + */ +function escapeRegex(aString) { + return aString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); +}
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdRestart.jsm @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +/** + * Restart command + * + * @param boolean nocache + * Disables loading content from cache upon restart. + * + * Examples : + * >> restart + * - restarts browser immediately + * >> restart --nocache + * - restarts immediately and starts Firefox without using cache + */ +gcli.addCommand({ + name: "restart", + description: gcli.lookup("restartFirefoxDesc"), + params: [ + { + name: "nocache", + type: "boolean", + defaultValue: false, + description: gcli.lookup("restartFirefoxNocacheDesc") + } + ], + returnType: "string", + exec: function Restart(args, context) { + let canceled = Cc["@mozilla.org/supports-PRBool;1"] + .createInstance(Ci.nsISupportsPRBool); + Services.obs.notifyObservers(canceled, "quit-application-requested", "restart"); + if (canceled.data) { + return gcli.lookup("restartFirefoxRequestCancelled"); + } + + // disable loading content from cache. + if (args.nocache) { + Services.appinfo.invalidateCachesOnRestart(); + } + + // restart + Cc['@mozilla.org/toolkit/app-startup;1'] + .getService(Ci.nsIAppStartup) + .quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart); + return gcli.lookup("restartFirefoxRestarting"); + } +});
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/CmdScreenshot.jsm @@ -0,0 +1,135 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "LayoutHelpers", + "resource:///modules/devtools/LayoutHelpers.jsm"); + +/** + * 'screenshot' command + */ +gcli.addCommand({ + name: "screenshot", + description: gcli.lookup("screenshotDesc"), + manual: gcli.lookup("screenshotManual"), + returnType: "string", + params: [ + { + name: "filename", + type: "string", + description: gcli.lookup("screenshotFilenameDesc"), + manual: gcli.lookup("screenshotFilenameManual") + }, + { + name: "delay", + type: { name: "number", min: 0 }, + defaultValue: 0, + description: gcli.lookup("screenshotDelayDesc"), + manual: gcli.lookup("screenshotDelayManual") + }, + { + name: "fullpage", + type: "boolean", + defaultValue: false, + description: gcli.lookup("screenshotFullPageDesc"), + manual: gcli.lookup("screenshotFullPageManual") + }, + { + name: "node", + type: "node", + defaultValue: null, + description: gcli.lookup("inspectNodeDesc"), + manual: gcli.lookup("inspectNodeManual") + } + ], + exec: function Command_screenshot(args, context) { + var document = context.environment.contentDocument; + if (args.delay > 0) { + var promise = context.createPromise(); + document.defaultView.setTimeout(function Command_screenshotDelay() { + let reply = this.grabScreen(document, args.filename); + promise.resolve(reply); + }.bind(this), args.delay * 1000); + return promise; + } + else { + return this.grabScreen(document, args.filename, args.fullpage, args.node); + } + }, + grabScreen: + function Command_screenshotGrabScreen(document, filename, fullpage, node) { + let window = document.defaultView; + let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); + let left = 0; + let top = 0; + let width; + let height; + + if (!fullpage) { + if (!node) { + left = window.scrollX; + top = window.scrollY; + width = window.innerWidth; + height = window.innerHeight; + } else { + let rect = LayoutHelpers.getRect(node, window); + top = rect.top; + left = rect.left; + width = rect.width; + height = rect.height; + } + } else { + width = window.innerWidth + window.scrollMaxX; + height = window.innerHeight + window.scrollMaxY; + } + canvas.width = width; + canvas.height = height; + + let ctx = canvas.getContext("2d"); + ctx.drawWindow(window, left, top, width, height, "#fff"); + + let data = canvas.toDataURL("image/png", ""); + let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + + // Check there is a .png extension to filename + if (!filename.match(/.png$/i)) { + filename += ".png"; + } + + // If the filename is relative, tack it onto the download directory + if (!filename.match(/[\\\/]/)) { + let downloadMgr = Cc["@mozilla.org/download-manager;1"] + .getService(Ci.nsIDownloadManager); + let tempfile = downloadMgr.userDownloadsDirectory; + tempfile.append(filename); + filename = tempfile.path; + } + + try { + file.initWithPath(filename); + } catch (ex) { + return "Error saving to " + filename; + } + + let ioService = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + + let Persist = Ci.nsIWebBrowserPersist; + let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"] + .createInstance(Persist); + persist.persistFlags = Persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES | + Persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION; + + let source = ioService.newURI(data, "UTF8", null); + persist.saveURI(source, null, null, null, null, file); + + return "Saved to " + filename; + } +});
rename from browser/devtools/commandline/GcliCommands.jsm rename to browser/devtools/commandline/Commands.jsm --- a/browser/devtools/commandline/GcliCommands.jsm +++ b/browser/devtools/commandline/Commands.jsm @@ -1,1549 +1,25 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -let EXPORTED_SYMBOLS = [ "GcliCommands" ]; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -const XMLHttpRequest = - Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1"); - -Cu.import("resource:///modules/devtools/gcli.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "HUDService", - "resource:///modules/HUDService.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "LayoutHelpers", - "resource:///modules/devtools/LayoutHelpers.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "console", - "resource://gre/modules/devtools/Console.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", - "resource://gre/modules/AddonManager.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "js_beautify", - "resource:///modules/devtools/Jsbeautify.jsm"); - -XPCOMUtils.defineLazyGetter(this, "Debugger", function() { - let JsDebugger = {}; - Components.utils.import("resource://gre/modules/jsdebugger.jsm", JsDebugger); - - let global = Components.utils.getGlobalForObject({}); - JsDebugger.addDebuggerToGlobal(global); - - return global.Debugger; -}); - -let prefSvc = "@mozilla.org/preferences-service;1"; -XPCOMUtils.defineLazyGetter(this, "prefBranch", function() { - let prefService = Cc[prefSvc].getService(Ci.nsIPrefService); - return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2); -}); - -Cu.import("resource:///modules/devtools/GcliTiltCommands.jsm", {}); -Cu.import("resource:///modules/devtools/GcliCookieCommands.jsm", {}); - -/** - * A place to store the names of the commands that we have added as a result of - * calling refreshAutoCommands(). Used by refreshAutoCommands to remove the - * added commands. - */ -let commands = []; - -/** - * Exported API - */ -let GcliCommands = { - /** - * Called to look in a directory pointed at by the devtools.commands.dir pref - * for *.mozcmd files which are then loaded. - * @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which - * we eval the script from the .mozcmd file. This should be a chrome window. - */ - refreshAutoCommands: function GC_refreshAutoCommands(aSandboxPrincipal) { - // First get rid of the last set of commands - commands.forEach(function(name) { - gcli.removeCommand(name); - }); - - let dirName = prefBranch.getComplexValue("devtools.commands.dir", - Ci.nsISupportsString).data; - if (dirName == "") { - return; - } - - let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); - dir.initWithPath(dirName); - if (!dir.exists() || !dir.isDirectory()) { - throw new Error('\'' + dirName + '\' is not a directory.'); - } - - let en = dir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator); - - while (true) { - let file = en.nextFile; - if (!file) { - break; - } - if (file.leafName.match(/.*\.mozcmd$/) && file.isFile() && file.isReadable()) { - loadCommandFile(file, aSandboxPrincipal); - } - } - }, -}; - -/** - * Load the commands from a single file - * @param nsIFile aFile The file containing the commands that we should read - * @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which - * we eval the script from the .mozcmd file. This should be a chrome window. - */ -function loadCommandFile(aFile, aSandboxPrincipal) { - NetUtil.asyncFetch(aFile, function refresh_fetch(aStream, aStatus) { - if (!Components.isSuccessCode(aStatus)) { - console.error("NetUtil.asyncFetch(" + aFile.path + ",..) failed. Status=" + aStatus); - return; - } - - let source = NetUtil.readInputStreamToString(aStream, aStream.available()); - aStream.close(); - - let sandbox = new Cu.Sandbox(aSandboxPrincipal, { - sandboxPrototype: aSandboxPrincipal, - wantXrays: false, - sandboxName: aFile.path - }); - let data = Cu.evalInSandbox(source, sandbox, "1.8", aFile.leafName, 1); - - if (!Array.isArray(data)) { - console.error("Command file '" + aFile.leafName + "' does not have top level array."); - return; - } - - data.forEach(function(commandSpec) { - gcli.addCommand(commandSpec); - commands.push(commandSpec.name); - }); - }.bind(this)); -} - -/** - * 'cmd' command - */ -gcli.addCommand({ - name: "cmd", - description: gcli.lookup("cmdDesc"), - hidden: true -}); - -/** - * 'cmd refresh' command - */ -gcli.addCommand({ - name: "cmd refresh", - description: gcli.lookup("cmdRefreshDesc"), - hidden: true, - exec: function Command_cmdRefresh(args, context) { - GcliCommands.refreshAutoCommands(context.environment.chromeDocument.defaultView); - } -}); - -/** - * 'echo' command - */ -gcli.addCommand({ - name: "echo", - description: gcli.lookup("echoDesc"), - params: [ - { - name: "message", - type: "string", - description: gcli.lookup("echoMessageDesc") - } - ], - returnType: "string", - hidden: true, - exec: function Command_echo(args, context) { - return args.message; - } -}); - - -/** - * 'screenshot' command - */ -gcli.addCommand({ - name: "screenshot", - description: gcli.lookup("screenshotDesc"), - manual: gcli.lookup("screenshotManual"), - returnType: "string", - params: [ - { - name: "filename", - type: "string", - description: gcli.lookup("screenshotFilenameDesc"), - manual: gcli.lookup("screenshotFilenameManual") - }, - { - name: "delay", - type: { name: "number", min: 0 }, - defaultValue: 0, - description: gcli.lookup("screenshotDelayDesc"), - manual: gcli.lookup("screenshotDelayManual") - }, - { - name: "fullpage", - type: "boolean", - defaultValue: false, - description: gcli.lookup("screenshotFullPageDesc"), - manual: gcli.lookup("screenshotFullPageManual") - }, - { - name: "node", - type: "node", - defaultValue: null, - description: gcli.lookup("inspectNodeDesc"), - manual: gcli.lookup("inspectNodeManual") - } - ], - exec: function Command_screenshot(args, context) { - var document = context.environment.contentDocument; - if (args.delay > 0) { - var promise = context.createPromise(); - document.defaultView.setTimeout(function Command_screenshotDelay() { - let reply = this.grabScreen(document, args.filename); - promise.resolve(reply); - }.bind(this), args.delay * 1000); - return promise; - } - else { - return this.grabScreen(document, args.filename, args.fullpage, args.node); - } - }, - grabScreen: - function Command_screenshotGrabScreen(document, filename, fullpage, node) { - let window = document.defaultView; - let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); - let left = 0; - let top = 0; - let width; - let height; - - if (!fullpage) { - if (!node) { - left = window.scrollX; - top = window.scrollY; - width = window.innerWidth; - height = window.innerHeight; - } else { - let rect = LayoutHelpers.getRect(node, window); - top = rect.top; - left = rect.left; - width = rect.width; - height = rect.height; - } - } else { - width = window.innerWidth + window.scrollMaxX; - height = window.innerHeight + window.scrollMaxY; - } - canvas.width = width; - canvas.height = height; - - let ctx = canvas.getContext("2d"); - ctx.drawWindow(window, left, top, width, height, "#fff"); - - let data = canvas.toDataURL("image/png", ""); - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); - - // Check there is a .png extension to filename - if (!filename.match(/.png$/i)) { - filename += ".png"; - } - - // If the filename is relative, tack it onto the download directory - if (!filename.match(/[\\\/]/)) { - let downloadMgr = Cc["@mozilla.org/download-manager;1"] - .getService(Ci.nsIDownloadManager); - let tempfile = downloadMgr.userDownloadsDirectory; - tempfile.append(filename); - filename = tempfile.path; - } - - try { - file.initWithPath(filename); - } catch (ex) { - return "Error saving to " + filename; - } - - let ioService = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - - let Persist = Ci.nsIWebBrowserPersist; - let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"] - .createInstance(Persist); - persist.persistFlags = Persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES | - Persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION; - - let source = ioService.newURI(data, "UTF8", null); - persist.saveURI(source, null, null, null, null, file); - - return "Saved to " + filename; - } -}); - - -let callLogDebuggers = []; - -/** - * 'calllog' command - */ -gcli.addCommand({ - name: "calllog", - description: gcli.lookup("calllogDesc") -}) - -/** - * 'calllog start' command - */ -gcli.addCommand({ - name: "calllog start", - description: gcli.lookup("calllogStartDesc"), - - exec: function(args, context) { - let contentWindow = context.environment.contentDocument.defaultView; - - let dbg = new Debugger(contentWindow); - dbg.onEnterFrame = function(frame) { - // BUG 773652 - Make the output from the GCLI calllog command nicer - contentWindow.console.log("Method call: " + this.callDescription(frame)); - }.bind(this); - - callLogDebuggers.push(dbg); - - let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab; - HUDService.activateHUDForContext(tab); - - return gcli.lookup("calllogStartReply"); - }, - - callDescription: function(frame) { - let name = "<anonymous>"; - if (frame.callee.name) { - name = frame.callee.name; - } - else { - let desc = frame.callee.getOwnPropertyDescriptor("displayName"); - if (desc && desc.value && typeof desc.value == "string") { - name = desc.value; - } - } - - let args = frame.arguments.map(this.valueToString).join(", "); - return name + "(" + args + ")"; - }, - - valueToString: function(value) { - if (typeof value !== "object" || value === null) { - return uneval(value); - } - return "[object " + value.class + "]"; - } -}); +let EXPORTED_SYMBOLS = [ ]; -/** - * 'calllog stop' command - */ -gcli.addCommand({ - name: "calllog stop", - description: gcli.lookup("calllogStopDesc"), - - exec: function(args, context) { - let numDebuggers = callLogDebuggers.length; - if (numDebuggers == 0) { - return gcli.lookup("calllogStopNoLogging"); - } - - for (let dbg of callLogDebuggers) { - dbg.onEnterFrame = undefined; - } - callLogDebuggers = []; - - return gcli.lookupFormat("calllogStopReply", [ numDebuggers ]); - } -}); - - -/** - * 'console' command - */ -gcli.addCommand({ - name: "console", - description: gcli.lookup("consoleDesc"), - manual: gcli.lookup("consoleManual") -}); - -/** - * 'console clear' command - */ -gcli.addCommand({ - name: "console clear", - description: gcli.lookup("consoleclearDesc"), - exec: function Command_consoleClear(args, context) { - let window = context.environment.contentDocument.defaultView; - let hud = HUDService.getHudByWindow(window); - // hud will be null if the web console has not been opened for this window - if (hud) { - hud.jsterm.clearOutput(); - } - } -}); - -/** - * 'console close' command - */ -gcli.addCommand({ - name: "console close", - description: gcli.lookup("consolecloseDesc"), - exec: function Command_consoleClose(args, context) { - let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab - HUDService.deactivateHUDForContext(tab); - } -}); - -/** - * 'console open' command - */ -gcli.addCommand({ - name: "console open", - description: gcli.lookup("consoleopenDesc"), - exec: function Command_consoleOpen(args, context) { - let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab - HUDService.activateHUDForContext(tab); - } -}); - -/** - * Restart command - * - * @param boolean nocache - * Disables loading content from cache upon restart. - * - * Examples : - * >> restart - * - restarts browser immediately - * >> restart --nocache - * - restarts immediately and starts Firefox without using cache - */ -gcli.addCommand({ - name: "restart", - description: gcli.lookup("restartFirefoxDesc"), - params: [ - { - name: "nocache", - type: "boolean", - defaultValue: false, - description: gcli.lookup("restartFirefoxNocacheDesc") - } - ], - returnType: "string", - exec: function Restart(args, context) { - let canceled = Cc["@mozilla.org/supports-PRBool;1"] - .createInstance(Ci.nsISupportsPRBool); - Services.obs.notifyObservers(canceled, "quit-application-requested", "restart"); - if (canceled.data) { - return gcli.lookup("restartFirefoxRequestCancelled"); - } - - // disable loading content from cache. - if (args.nocache) { - Services.appinfo.invalidateCachesOnRestart(); - } - - // restart - Cc['@mozilla.org/toolkit/app-startup;1'] - .getService(Ci.nsIAppStartup) - .quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart); - return gcli.lookup("restartFirefoxRestarting"); - } -}); - -/** - * 'inspect' command - */ -gcli.addCommand({ - name: "inspect", - description: gcli.lookup("inspectDesc"), - manual: gcli.lookup("inspectManual"), - params: [ - { - name: "node", - type: "node", - description: gcli.lookup("inspectNodeDesc"), - manual: gcli.lookup("inspectNodeManual") - } - ], - exec: function Command_inspect(args, context) { - let document = context.environment.chromeDocument; - document.defaultView.InspectorUI.openInspectorUI(args.node); - } -}); - -/** - * 'edit' command - */ -gcli.addCommand({ - name: "edit", - description: gcli.lookup("editDesc"), - manual: gcli.lookup("editManual2"), - params: [ - { - name: 'resource', - type: { - name: 'resource', - include: 'text/css' - }, - description: gcli.lookup("editResourceDesc") - }, - { - name: "line", - defaultValue: 1, - type: { - name: "number", - min: 1, - step: 10 - }, - description: gcli.lookup("editLineToJumpToDesc") - } - ], - exec: function(args, context) { - let win = HUDService.currentContext(); - win.StyleEditor.openChrome(args.resource.element, args.line); - } -}); - -/** - * 'break' command - */ -gcli.addCommand({ - name: "break", - description: gcli.lookup("breakDesc"), - manual: gcli.lookup("breakManual") -}); - - -/** - * 'break list' command - */ -gcli.addCommand({ - name: "break list", - description: gcli.lookup("breaklistDesc"), - returnType: "html", - exec: function(args, context) { - let win = HUDService.currentContext(); - let dbg = win.DebuggerUI.getDebugger(); - if (!dbg) { - return gcli.lookup("breakaddDebuggerStopped"); - } - let breakpoints = dbg.breakpoints; - - if (Object.keys(breakpoints).length === 0) { - return gcli.lookup("breaklistNone"); - } - - let reply = gcli.lookup("breaklistIntro"); - reply += "<ol>"; - for each (let breakpoint in breakpoints) { - let text = gcli.lookupFormat("breaklistLineEntry", - [breakpoint.location.url, - breakpoint.location.line]); - reply += "<li>" + text + "</li>"; - }; - reply += "</ol>"; - return reply; - } -}); - - -/** - * 'break add' command - */ -gcli.addCommand({ - name: "break add", - description: gcli.lookup("breakaddDesc"), - manual: gcli.lookup("breakaddManual") -}); - -/** - * 'break add line' command - */ -gcli.addCommand({ - name: "break add line", - description: gcli.lookup("breakaddlineDesc"), - params: [ - { - name: "file", - type: { - name: "selection", - data: function() { - let win = HUDService.currentContext(); - let dbg = win.DebuggerUI.getDebugger(); - let files = []; - if (dbg) { - let scriptsView = dbg.contentWindow.DebuggerView.Scripts; - for each (let script in scriptsView.scriptLocations) { - files.push(script); - } - } - return files; - } - }, - description: gcli.lookup("breakaddlineFileDesc") - }, - { - name: "line", - type: { name: "number", min: 1, step: 10 }, - description: gcli.lookup("breakaddlineLineDesc") - } - ], - returnType: "html", - exec: function(args, context) { - args.type = "line"; - let win = HUDService.currentContext(); - let dbg = win.DebuggerUI.getDebugger(); - if (!dbg) { - return gcli.lookup("breakaddDebuggerStopped"); - } - var promise = context.createPromise(); - let position = { url: args.file, line: args.line }; - dbg.addBreakpoint(position, function(aBreakpoint, aError) { - if (aError) { - promise.resolve(gcli.lookupFormat("breakaddFailed", [aError])); - return; - } - promise.resolve(gcli.lookup("breakaddAdded")); - }); - return promise; - } -}); - - -/** - * 'break del' command - */ -gcli.addCommand({ - name: "break del", - description: gcli.lookup("breakdelDesc"), - params: [ - { - name: "breakid", - type: { - name: "number", - min: 0, - max: function() { - let win = HUDService.currentContext(); - let dbg = win.DebuggerUI.getDebugger(); - if (!dbg) { - return gcli.lookup("breakaddDebuggerStopped"); - } - return Object.keys(dbg.breakpoints).length - 1; - }, - }, - description: gcli.lookup("breakdelBreakidDesc") - } - ], - returnType: "html", - exec: function(args, context) { - let win = HUDService.currentContext(); - let dbg = win.DebuggerUI.getDebugger(); - if (!dbg) { - return gcli.lookup("breakaddDebuggerStopped"); - } - - let breakpoints = dbg.breakpoints; - let id = Object.keys(dbg.breakpoints)[args.breakid]; - if (!id || !(id in breakpoints)) { - return gcli.lookup("breakNotFound"); - } - - let promise = context.createPromise(); - try { - dbg.removeBreakpoint(breakpoints[id], function() { - promise.resolve(gcli.lookup("breakdelRemoved")); - }); - } catch (ex) { - // If the debugger has been closed already, don't scare the user. - promise.resolve(gcli.lookup("breakdelRemoved")); - } - return promise; - } -}); - -/** - * 'export' command - */ -gcli.addCommand({ - name: "export", - description: gcli.lookup("exportDesc"), -}); - -/** - * The 'export html' command. This command allows the user to export the page to - * HTML after they do DOM changes. - */ -gcli.addCommand({ - name: "export html", - description: gcli.lookup("exportHtmlDesc"), - exec: function(args, context) { - let document = context.environment.contentDocument; - let window = document.defaultView; - let page = document.documentElement.outerHTML; - window.open('data:text/plain;charset=utf8,' + encodeURIComponent(page)); - } -}); - -/** - * 'pagemod' command - */ -gcli.addCommand({ - name: "pagemod", - description: gcli.lookup("pagemodDesc"), -}); +const Cu = Components.utils; -/** - * The 'pagemod replace' command. This command allows the user to search and - * replace within text nodes and attributes. - */ -gcli.addCommand({ - name: "pagemod replace", - description: gcli.lookup("pagemodReplaceDesc"), - params: [ - { - name: "search", - type: "string", - description: gcli.lookup("pagemodReplaceSearchDesc"), - }, - { - name: "replace", - type: "string", - description: gcli.lookup("pagemodReplaceReplaceDesc"), - }, - { - name: "ignoreCase", - type: "boolean", - description: gcli.lookup("pagemodReplaceIgnoreCaseDesc"), - }, - { - name: "selector", - type: "string", - description: gcli.lookup("pagemodReplaceSelectorDesc"), - defaultValue: "*:not(script):not(style):not(embed):not(object):not(frame):not(iframe):not(frameset)", - }, - { - name: "root", - type: "node", - description: gcli.lookup("pagemodReplaceRootDesc"), - defaultValue: null, - }, - { - name: "attrOnly", - type: "boolean", - description: gcli.lookup("pagemodReplaceAttrOnlyDesc"), - }, - { - name: "contentOnly", - type: "boolean", - description: gcli.lookup("pagemodReplaceContentOnlyDesc"), - }, - { - name: "attributes", - type: "string", - description: gcli.lookup("pagemodReplaceAttributesDesc"), - defaultValue: null, - }, - ], - exec: function(args, context) { - let document = context.environment.contentDocument; - let searchTextNodes = !args.attrOnly; - let searchAttributes = !args.contentOnly; - let regexOptions = args.ignoreCase ? 'ig' : 'g'; - let search = new RegExp(escapeRegex(args.search), regexOptions); - let attributeRegex = null; - if (args.attributes) { - attributeRegex = new RegExp(args.attributes, regexOptions); - } - - let root = args.root || document; - let elements = root.querySelectorAll(args.selector); - elements = Array.prototype.slice.call(elements); - - let replacedTextNodes = 0; - let replacedAttributes = 0; - - function replaceAttribute() { - replacedAttributes++; - return args.replace; - } - function replaceTextNode() { - replacedTextNodes++; - return args.replace; - } - - for (let i = 0; i < elements.length; i++) { - let element = elements[i]; - if (searchTextNodes) { - for (let y = 0; y < element.childNodes.length; y++) { - let node = element.childNodes[y]; - if (node.nodeType == node.TEXT_NODE) { - node.textContent = node.textContent.replace(search, replaceTextNode); - } - } - } - - if (searchAttributes) { - if (!element.attributes) { - continue; - } - for (let y = 0; y < element.attributes.length; y++) { - let attr = element.attributes[y]; - if (!attributeRegex || attributeRegex.test(attr.name)) { - attr.value = attr.value.replace(search, replaceAttribute); - } - } - } - } - - return gcli.lookupFormat("pagemodReplaceResult", - [elements.length, replacedTextNodes, - replacedAttributes]); - } -}); - -/** - * 'pagemod remove' command - */ -gcli.addCommand({ - name: "pagemod remove", - description: gcli.lookup("pagemodRemoveDesc"), -}); - - -/** - * The 'pagemod remove element' command. - */ -gcli.addCommand({ - name: "pagemod remove element", - description: gcli.lookup("pagemodRemoveElementDesc"), - params: [ - { - name: "search", - type: "string", - description: gcli.lookup("pagemodRemoveElementSearchDesc"), - }, - { - name: "root", - type: "node", - description: gcli.lookup("pagemodRemoveElementRootDesc"), - defaultValue: null, - }, - { - name: 'stripOnly', - type: 'boolean', - description: gcli.lookup("pagemodRemoveElementStripOnlyDesc"), - }, - { - name: 'ifEmptyOnly', - type: 'boolean', - description: gcli.lookup("pagemodRemoveElementIfEmptyOnlyDesc"), - }, - ], - exec: function(args, context) { - let document = context.environment.contentDocument; - let root = args.root || document; - let elements = Array.prototype.slice.call(root.querySelectorAll(args.search)); - - let removed = 0; - for (let i = 0; i < elements.length; i++) { - let element = elements[i]; - let parentNode = element.parentNode; - if (!parentNode || !element.removeChild) { - continue; - } - if (args.stripOnly) { - while (element.hasChildNodes()) { - parentNode.insertBefore(element.childNodes[0], element); - } - } - if (!args.ifEmptyOnly || !element.hasChildNodes()) { - element.parentNode.removeChild(element); - removed++; - } - } - - return gcli.lookupFormat("pagemodRemoveElementResultMatchedAndRemovedElements", - [elements.length, removed]); - } -}); - -/** - * The 'pagemod remove attribute' command. - */ -gcli.addCommand({ - name: "pagemod remove attribute", - description: gcli.lookup("pagemodRemoveAttributeDesc"), - params: [ - { - name: "searchAttributes", - type: "string", - description: gcli.lookup("pagemodRemoveAttributeSearchAttributesDesc"), - }, - { - name: "searchElements", - type: "string", - description: gcli.lookup("pagemodRemoveAttributeSearchElementsDesc"), - }, - { - name: "root", - type: "node", - description: gcli.lookup("pagemodRemoveAttributeRootDesc"), - defaultValue: null, - }, - { - name: "ignoreCase", - type: "boolean", - description: gcli.lookup("pagemodRemoveAttributeIgnoreCaseDesc"), - }, - ], - exec: function(args, context) { - let document = context.environment.contentDocument; - - let root = args.root || document; - let regexOptions = args.ignoreCase ? 'ig' : 'g'; - let attributeRegex = new RegExp(args.searchAttributes, regexOptions); - let elements = root.querySelectorAll(args.searchElements); - elements = Array.prototype.slice.call(elements); - - let removed = 0; - for (let i = 0; i < elements.length; i++) { - let element = elements[i]; - if (!element.attributes) { - continue; - } - - var attrs = Array.prototype.slice.call(element.attributes); - for (let y = 0; y < attrs.length; y++) { - let attr = attrs[y]; - if (attributeRegex.test(attr.name)) { - element.removeAttribute(attr.name); - removed++; - } - } - } - - return gcli.lookupFormat("pagemodRemoveAttributeResult", - [elements.length, removed]); - } -}); - - -/** - * Make a given string safe to use in a regular expression. - * - * @param string aString - * The string you want to use in a regex. - * @return string - * The equivalent of |aString| but safe to use in a regex. - */ -function escapeRegex(aString) { - return aString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); -} - -/** - * 'addon' command. - */ -gcli.addCommand({ - name: "addon", - description: gcli.lookup("addonDesc") -}); - -/** - * 'addon list' command. - */ -gcli.addCommand({ - name: "addon list", - description: gcli.lookup("addonListDesc"), - params: [{ - name: 'type', - type: { - name: 'selection', - data: ["dictionary", "extension", "locale", "plugin", "theme", "all"] - }, - defaultValue: 'all', - description: gcli.lookup("addonListTypeDesc"), - }], - exec: function(aArgs, context) { - function representEnabledAddon(aAddon) { - return "<li><![CDATA[" + aAddon.name + "\u2002" + aAddon.version + - getAddonStatus(aAddon) + "]]></li>"; - } - - function representDisabledAddon(aAddon) { - return "<li class=\"gcli-addon-disabled\">" + - "<![CDATA[" + aAddon.name + "\u2002" + aAddon.version + aAddon.version + - "]]></li>"; - } - - function getAddonStatus(aAddon) { - let operations = []; - - if (aAddon.pendingOperations & AddonManager.PENDING_ENABLE) { - operations.push("PENDING_ENABLE"); - } - - if (aAddon.pendingOperations & AddonManager.PENDING_DISABLE) { - operations.push("PENDING_DISABLE"); - } - - if (aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL) { - operations.push("PENDING_UNINSTALL"); - } - - if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL) { - operations.push("PENDING_INSTALL"); - } - - if (aAddon.pendingOperations & AddonManager.PENDING_UPGRADE) { - operations.push("PENDING_UPGRADE"); - } - - if (operations.length) { - return " (" + operations.join(", ") + ")"; - } - return ""; - } - - /** - * Compares two addons by their name. Used in sorting. - */ - function compareAddonNames(aNameA, aNameB) { - return String.localeCompare(aNameA.name, aNameB.name); - } - - /** - * Resolves the promise which is the scope (this) of this function, filling - * it with an HTML representation of the passed add-ons. - */ - function list(aType, aAddons) { - if (!aAddons.length) { - this.resolve(gcli.lookup("addonNoneOfType")); - } - - // Separate the enabled add-ons from the disabled ones. - let enabledAddons = []; - let disabledAddons = []; - - aAddons.forEach(function(aAddon) { - if (aAddon.isActive) { - enabledAddons.push(aAddon); - } else { - disabledAddons.push(aAddon); - } - }); - - let header; - switch(aType) { - case "dictionary": - header = gcli.lookup("addonListDictionaryHeading"); - break; - case "extension": - header = gcli.lookup("addonListExtensionHeading"); - break; - case "locale": - header = gcli.lookup("addonListLocaleHeading"); - break; - case "plugin": - header = gcli.lookup("addonListPluginHeading"); - break; - case "theme": - header = gcli.lookup("addonListThemeHeading"); - case "all": - header = gcli.lookup("addonListAllHeading"); - break; - default: - header = gcli.lookup("addonListUnknownHeading"); - } - - // Map and sort the add-ons, and create an HTML list. - this.resolve(header + - "<ol>" + - enabledAddons.sort(compareAddonNames).map(representEnabledAddon).join("") + - disabledAddons.sort(compareAddonNames).map(representDisabledAddon).join("") + - "</ol>"); - } - - // Create the promise that will be resolved when the add-on listing has - // been finished. - let promise = context.createPromise(); - let types = aArgs.type == "all" ? null : [aArgs.type]; - AddonManager.getAddonsByTypes(types, list.bind(promise, aArgs.type)); - return promise; - } -}); - - -/** - * 'dbg' command - */ -gcli.addCommand({ - name: "dbg", - description: gcli.lookup("dbgDesc"), - manual: gcli.lookup("dbgManual") -}); - - -/** - * 'dbg interrupt' command - */ -gcli.addCommand({ - name: "dbg interrupt", - description: gcli.lookup("dbgInterrupt"), - params: [], - exec: function(args, context) { - let win = context.environment.chromeDocument.defaultView; - let dbg = win.DebuggerUI.getDebugger(); - - if (dbg) { - let controller = dbg.contentWindow.DebuggerController; - let thread = controller.activeThread; - if (!thread.paused) { - thread.interrupt(); - } - } - } -}); - -/** - * 'dbg continue' command - */ -gcli.addCommand({ - name: "dbg continue", - description: gcli.lookup("dbgContinue"), - params: [], - exec: function(args, context) { - let win = context.environment.chromeDocument.defaultView; - let dbg = win.DebuggerUI.getDebugger(); - - if (dbg) { - let controller = dbg.contentWindow.DebuggerController; - let thread = controller.activeThread; - if (thread.paused) { - thread.resume(); - } - } - } -}); - - -/** - * 'dbg step' command - */ -gcli.addCommand({ - name: "dbg step", - description: gcli.lookup("dbgStepDesc"), - manual: gcli.lookup("dbgStepManual") -}); - - -/** - * 'dbg step over' command - */ -gcli.addCommand({ - name: "dbg step over", - description: gcli.lookup("dbgStepOverDesc"), - params: [], - exec: function(args, context) { - let win = context.environment.chromeDocument.defaultView; - let dbg = win.DebuggerUI.getDebugger(); - - if (dbg) { - let controller = dbg.contentWindow.DebuggerController; - let thread = controller.activeThread; - if (thread.paused) { - thread.stepOver(); - } - } - } -}); - -/** - * 'dbg step in' command - */ -gcli.addCommand({ - name: 'dbg step in', - description: gcli.lookup("dbgStepInDesc"), - params: [], - exec: function(args, context) { - let win = context.environment.chromeDocument.defaultView; - let dbg = win.DebuggerUI.getDebugger(); - - if (dbg) { - let controller = dbg.contentWindow.DebuggerController; - let thread = controller.activeThread; - if (thread.paused) { - thread.stepIn(); - } - } - } -}); - -/** - * 'dbg step over' command - */ -gcli.addCommand({ - name: 'dbg step out', - description: gcli.lookup("dbgStepOutDesc"), - params: [], - exec: function(args, context) { - let win = context.environment.chromeDocument.defaultView; - let dbg = win.DebuggerUI.getDebugger(); - - if (dbg) { - let controller = dbg.contentWindow.DebuggerController; - let thread = controller.activeThread; - if (thread.paused) { - thread.stepOut(); - } - } - } -}); - -// We need a list of addon names for the enable and disable commands. Because -// getting the name list is async we do not add the commands until we have the -// list. -AddonManager.getAllAddons(function addonAsync(aAddons) { - // We listen for installs to keep our addon list up to date. There is no need - // to listen for uninstalls because uninstalled addons are simply disabled - // until restart (to enable undo functionality). - AddonManager.addAddonListener({ - onInstalled: function(aAddon) { - addonNameCache.push({ - name: representAddon(aAddon).replace(/\s/g, "_"), - value: aAddon.name - }); - }, - onUninstalled: function(aAddon) { - let name = representAddon(aAddon).replace(/\s/g, "_"); - - for (let i = 0; i < addonNameCache.length; i++) { - if(addonNameCache[i].name == name) { - addonNameCache.splice(i, 1); - break; - } - } - }, - }); - - /** - * Returns a string that represents the passed add-on. - */ - function representAddon(aAddon) { - let name = aAddon.name + " " + aAddon.version; - return name.trim(); - } - - let addonNameCache = []; - - // The name parameter, used in "addon enable" and "addon disable." - let nameParameter = { - name: "name", - type: { - name: "selection", - lookup: addonNameCache - }, - description: gcli.lookup("addonNameDesc") - }; - - for (let addon of aAddons) { - addonNameCache.push({ - name: representAddon(addon).replace(/\s/g, "_"), - value: addon.name - }); - } - - /** - * 'addon enable' command. - */ - gcli.addCommand({ - name: "addon enable", - description: gcli.lookup("addonEnableDesc"), - params: [nameParameter], - exec: function(aArgs, context) { - /** - * Enables the addon in the passed list which has a name that matches - * according to the passed name comparer, and resolves the promise which - * is the scope (this) of this function to display the result of this - * enable attempt. - */ - function enable(aName, addons) { - // Find the add-on. - let addon = null; - addons.some(function(candidate) { - if (candidate.name == aName) { - addon = candidate; - return true; - } else { - return false; - } - }); - - let name = representAddon(addon); - - if (!addon.userDisabled) { - this.resolve("<![CDATA[" + - gcli.lookupFormat("addonAlreadyEnabled", [name]) + "]]>"); - } else { - addon.userDisabled = false; - // nl-nl: {$1} is ingeschakeld. - this.resolve("<![CDATA[" + - gcli.lookupFormat("addonEnabled", [name]) + "]]>"); - } - } - - let promise = context.createPromise(); - // List the installed add-ons, enable one when done listing. - AddonManager.getAllAddons(enable.bind(promise, aArgs.name)); - return promise; - } - }); - - /** - * 'addon disable' command. - */ - gcli.addCommand({ - name: "addon disable", - description: gcli.lookup("addonDisableDesc"), - params: [nameParameter], - exec: function(aArgs, context) { - /** - * Like enable, but ... you know ... the exact opposite. - */ - function disable(aName, addons) { - // Find the add-on. - let addon = null; - addons.some(function(candidate) { - if (candidate.name == aName) { - addon = candidate; - return true; - } else { - return false; - } - }); - - let name = representAddon(addon); - - if (addon.userDisabled) { - this.resolve("<![CDATA[" + - gcli.lookupFormat("addonAlreadyDisabled", [name]) + "]]>"); - } else { - addon.userDisabled = true; - // nl-nl: {$1} is uitgeschakeld. - this.resolve("<![CDATA[" + - gcli.lookupFormat("addonDisabled", [name]) + "]]>"); - } - } - - let promise = context.createPromise(); - // List the installed add-ons, disable one when done listing. - AddonManager.getAllAddons(disable.bind(promise, aArgs.name)); - return promise; - } - }); - Services.obs.notifyObservers(null, "gcli_addon_commands_ready", null); -}); - -/* Responsive Mode commands */ -(function gcli_cmd_resize_container() { - function gcli_cmd_resize(args, context) { - let browserDoc = context.environment.chromeDocument; - let browserWindow = browserDoc.defaultView; - let mgr = browserWindow.ResponsiveUI.ResponsiveUIManager; - mgr.handleGcliCommand(browserWindow, - browserWindow.gBrowser.selectedTab, - this.name, - args); - } - - gcli.addCommand({ - name: 'resize', - description: gcli.lookup('resizeModeDesc') - }); - - gcli.addCommand({ - name: 'resize on', - description: gcli.lookup('resizeModeOnDesc'), - manual: gcli.lookup('resizeModeManual'), - exec: gcli_cmd_resize - }); - - gcli.addCommand({ - name: 'resize off', - description: gcli.lookup('resizeModeOffDesc'), - manual: gcli.lookup('resizeModeManual'), - exec: gcli_cmd_resize - }); - - gcli.addCommand({ - name: 'resize toggle', - description: gcli.lookup('resizeModeToggleDesc'), - manual: gcli.lookup('resizeModeManual'), - exec: gcli_cmd_resize - }); - - gcli.addCommand({ - name: 'resize to', - description: gcli.lookup('resizeModeToDesc'), - params: [ - { - name: 'width', - type: 'number', - description: gcli.lookup("resizePageArgWidthDesc"), - }, - { - name: 'height', - type: 'number', - description: gcli.lookup("resizePageArgHeightDesc"), - }, - ], - exec: gcli_cmd_resize - }); -})(); - -/** - * jsb command. - */ -gcli.addCommand({ - name: 'jsb', - description: gcli.lookup('jsbDesc'), - returnValue:'string', - hidden: true, - params: [ - { - name: 'url', - type: 'string', - description: gcli.lookup('jsbUrlDesc'), - manual: 'The URL of the JS to prettify' - }, - { - name: 'indentSize', - type: 'number', - description: gcli.lookup('jsbIndentSizeDesc'), - manual: gcli.lookup('jsbIndentSizeManual'), - defaultValue: 2 - }, - { - name: 'indentChar', - type: { - name: 'selection', - lookup: [{name: "space", value: " "}, {name: "tab", value: "\t"}] - }, - description: gcli.lookup('jsbIndentCharDesc'), - manual: gcli.lookup('jsbIndentCharManual'), - defaultValue: ' ', - }, - { - name: 'preserveNewlines', - type: 'boolean', - description: gcli.lookup('jsbPreserveNewlinesDesc'), - manual: gcli.lookup('jsbPreserveNewlinesManual'), - defaultValue: true - }, - { - name: 'preserveMaxNewlines', - type: 'number', - description: gcli.lookup('jsbPreserveMaxNewlinesDesc'), - manual: gcli.lookup('jsbPreserveMaxNewlinesManual'), - defaultValue: -1 - }, - { - name: 'jslintHappy', - type: 'boolean', - description: gcli.lookup('jsbJslintHappyDesc'), - manual: gcli.lookup('jsbJslintHappyManual'), - defaultValue: false - }, - { - name: 'braceStyle', - type: { - name: 'selection', - data: ['collapse', 'expand', 'end-expand', 'expand-strict'] - }, - description: gcli.lookup('jsbBraceStyleDesc'), - manual: gcli.lookup('jsbBraceStyleManual'), - defaultValue: "collapse" - }, - { - name: 'spaceBeforeConditional', - type: 'boolean', - description: gcli.lookup('jsbSpaceBeforeConditionalDesc'), - manual: gcli.lookup('jsbSpaceBeforeConditionalManual'), - defaultValue: true - }, - { - name: 'unescapeStrings', - type: 'boolean', - description: gcli.lookup('jsbUnescapeStringsDesc'), - manual: gcli.lookup('jsbUnescapeStringsManual'), - defaultValue: false - } - ], - exec: function(args, context) { - let opts = { - indent_size: args.indentSize, - indent_char: args.indentChar, - preserve_newlines: args.preserveNewlines, - max_preserve_newlines: args.preserveMaxNewlines == -1 ? - undefined : args.preserveMaxNewlines, - jslint_happy: args.jslintHappy, - brace_style: args.braceStyle, - space_before_conditional: args.spaceBeforeConditional, - unescape_strings: args.unescapeStrings - } - - let xhr = new XMLHttpRequest(); - - try { - xhr.open("GET", args.url, true); - } catch(e) { - return gcli.lookup('jsbInvalidURL'); - } - - let promise = context.createPromise(); - - xhr.onreadystatechange = function(aEvt) { - if (xhr.readyState == 4) { - if (xhr.status == 200 || xhr.status == 0) { - let browserDoc = context.environment.chromeDocument; - let browserWindow = browserDoc.defaultView; - let browser = browserWindow.gBrowser; - - browser.selectedTab = browser.addTab("data:text/plain;base64," + - browserWindow.btoa(js_beautify(xhr.responseText, opts))); - promise.resolve(); - } - else { - promise.resolve("Unable to load page to beautify: " + args.url + " " + - xhr.status + " " + xhr.statusText); - } - }; - } - xhr.send(null); - return promise; - } -}); +Cu.import("resource:///modules/devtools/CmdAddon.jsm"); +Cu.import("resource:///modules/devtools/CmdBreak.jsm"); +Cu.import("resource:///modules/devtools/CmdCalllog.jsm"); +Cu.import("resource:///modules/devtools/CmdConsole.jsm"); +Cu.import("resource:///modules/devtools/CmdCookie.jsm"); +Cu.import("resource:///modules/devtools/CmdDbg.jsm"); +Cu.import("resource:///modules/devtools/CmdEcho.jsm"); +Cu.import("resource:///modules/devtools/CmdEdit.jsm"); +Cu.import("resource:///modules/devtools/CmdExport.jsm"); +Cu.import("resource:///modules/devtools/CmdInspect.jsm"); +Cu.import("resource:///modules/devtools/CmdJsb.jsm"); +Cu.import("resource:///modules/devtools/CmdPagemod.jsm"); +Cu.import("resource:///modules/devtools/CmdResize.jsm"); +Cu.import("resource:///modules/devtools/CmdRestart.jsm"); +Cu.import("resource:///modules/devtools/CmdScreenshot.jsm"); +Cu.import("resource:///modules/devtools/CmdTilt.jsm");
rename from browser/devtools/commandline/gcli.css rename to browser/devtools/commandline/commandline.css
rename from browser/devtools/commandline/gclioutput.xhtml rename to browser/devtools/commandline/commandlineoutput.xhtml --- a/browser/devtools/commandline/gclioutput.xhtml +++ b/browser/devtools/commandline/commandlineoutput.xhtml @@ -4,15 +4,15 @@ <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="stylesheet" href="chrome://global/skin/global.css" type="text/css"/> - <link rel="stylesheet" href="chrome://browser/content/devtools/gcli.css" type="text/css"/> - <link rel="stylesheet" href="chrome://browser/skin/devtools/gcli.css" type="text/css"/> + <link rel="stylesheet" href="chrome://browser/content/devtools/commandline.css" type="text/css"/> + <link rel="stylesheet" href="chrome://browser/skin/devtools/commandline.css" type="text/css"/> </head> <body class="gcli-body"> <div id="gcli-output-root"></div> </body> </html>
rename from browser/devtools/commandline/gclitooltip.xhtml rename to browser/devtools/commandline/commandlinetooltip.xhtml --- a/browser/devtools/commandline/gclitooltip.xhtml +++ b/browser/devtools/commandline/commandlinetooltip.xhtml @@ -4,16 +4,16 @@ <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="stylesheet" href="chrome://global/skin/global.css" type="text/css"/> - <link rel="stylesheet" href="chrome://browser/content/devtools/gcli.css" type="text/css"/> - <link rel="stylesheet" href="chrome://browser/skin/devtools/gcli.css" type="text/css"/> + <link rel="stylesheet" href="chrome://browser/content/devtools/commandline.css" type="text/css"/> + <link rel="stylesheet" href="chrome://browser/skin/devtools/commandline.css" type="text/css"/> </head> <body class="gcli-body"> <div id="gcli-tooltip-root"></div> <div id="gcli-tooltip-connector"></div> </body> </html>
--- a/browser/devtools/commandline/test/Makefile.in +++ b/browser/devtools/commandline/test/Makefile.in @@ -7,40 +7,33 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk MOCHITEST_BROWSER_FILES = \ - browser_gcli_addon.js \ - browser_gcli_break.js \ - browser_gcli_calllog.js \ - browser_gcli_commands.js \ - browser_gcli_cookie.js \ - browser_gcli_dbg.js \ - browser_gcli_edit.js \ - browser_gcli_inspect.js \ - browser_gcli_integrate.js \ - browser_gcli_jsb.js \ - browser_gcli_pagemod_export.js \ - browser_gcli_pref.js \ - browser_gcli_responsivemode.js \ - browser_gcli_restart.js \ - browser_gcli_settings.js \ + browser_dbg_cmd_break.js \ + browser_dbg_cmd.js \ + browser_cmd_addon.js \ + browser_cmd_calllog.js \ + browser_cmd_commands.js \ + browser_cmd_cookie.js \ + browser_cmd_integrate.js \ + browser_cmd_jsb.js \ + browser_cmd_pagemod_export.js \ + browser_cmd_pref.js \ + browser_cmd_restart.js \ + browser_cmd_settings.js \ browser_gcli_web.js \ head.js \ + helper.js \ $(NULL) MOCHITEST_BROWSER_FILES += \ - browser_gcli_break.html \ - browser_gcli_inspect.html \ - resources_dbg.html \ - resources_inpage.js \ - resources_inpage1.css \ - resources_inpage2.css \ - resources_jsb_script.js \ - resources.html \ + browser_dbg_cmd_break.html \ + browser_dbg_cmd.html \ + browser_cmd_pagemod_export.html \ + browser_cmd_jsb_script.jsi \ $(NULL) include $(topsrcdir)/config/rules.mk -
rename from browser/devtools/commandline/test/browser_gcli_addon.js rename to browser/devtools/commandline/test/browser_cmd_addon.js --- a/browser/devtools/commandline/test/browser_gcli_addon.js +++ b/browser/devtools/commandline/test/browser_cmd_addon.js @@ -1,43 +1,50 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the addon commands works as they should + function test() { - DeveloperToolbarTest.test("about:blank", function GAT_test() { - function GAT_ready() { - Services.obs.removeObserver(GAT_ready, "gcli_addon_commands_ready", false); + DeveloperToolbarTest.test("about:blank", [ GAT_test ]); +} + +function GAT_test() { + Services.obs.addObserver(GAT_ready, "gcli_addon_commands_ready", false); +} + +var GAT_ready = DeveloperToolbarTest.checkCalled(function() { + Services.obs.removeObserver(GAT_ready, "gcli_addon_commands_ready", false); - DeveloperToolbarTest.checkInputStatus({ - typed: "addon list dictionary", - status: "VALID" - }); - DeveloperToolbarTest.checkInputStatus({ - typed: "addon list extension", - status: "VALID" - }); - DeveloperToolbarTest.checkInputStatus({ - typed: "addon list locale", - status: "VALID" - }); - DeveloperToolbarTest.checkInputStatus({ - typed: "addon list plugin", - status: "VALID" - }); - DeveloperToolbarTest.checkInputStatus({ - typed: "addon list theme", - status: "VALID" - }); - DeveloperToolbarTest.checkInputStatus({ - typed: "addon list all", - status: "VALID" - }); - DeveloperToolbarTest.checkInputStatus({ - typed: "addon disable Test_Plug-in_1.0.0.0", - status: "VALID" - }); - DeveloperToolbarTest.checkInputStatus({ - typed: "addon enable Test_Plug-in_1.0.0.0", - status: "VALID" - }); - DeveloperToolbarTest.exec({ completed: false }); - finish(); - } - Services.obs.addObserver(GAT_ready, "gcli_addon_commands_ready", false); + DeveloperToolbarTest.checkInputStatus({ + typed: "addon list dictionary", + status: "VALID" + }); + DeveloperToolbarTest.checkInputStatus({ + typed: "addon list extension", + status: "VALID" + }); + DeveloperToolbarTest.checkInputStatus({ + typed: "addon list locale", + status: "VALID" + }); + DeveloperToolbarTest.checkInputStatus({ + typed: "addon list plugin", + status: "VALID" }); -} + DeveloperToolbarTest.checkInputStatus({ + typed: "addon list theme", + status: "VALID" + }); + DeveloperToolbarTest.checkInputStatus({ + typed: "addon list all", + status: "VALID" + }); + DeveloperToolbarTest.checkInputStatus({ + typed: "addon disable Test_Plug-in_1.0.0.0", + status: "VALID" + }); + DeveloperToolbarTest.checkInputStatus({ + typed: "addon enable Test_Plug-in_1.0.0.0", + status: "VALID" + }); + DeveloperToolbarTest.exec({ completed: false }); +});
rename from browser/devtools/commandline/test/browser_gcli_calllog.js rename to browser/devtools/commandline/test/browser_cmd_calllog.js --- a/browser/devtools/commandline/test/browser_gcli_calllog.js +++ b/browser/devtools/commandline/test/browser_cmd_calllog.js @@ -4,21 +4,17 @@ // Tests that the calllog commands works as they should let imported = {}; Components.utils.import("resource:///modules/HUDService.jsm", imported); const TEST_URI = "data:text/html;charset=utf-8,gcli-calllog"; function test() { - DeveloperToolbarTest.test(TEST_URI, function(browser, tab) { - testCallLogStatus(); - testCallLogExec(); - finish(); - }); + DeveloperToolbarTest.test(TEST_URI, [ testCallLogStatus, testCallLogExec ]); } function testCallLogStatus() { DeveloperToolbarTest.checkInputStatus({ typed: "calllog", status: "ERROR" });
rename from browser/devtools/commandline/test/browser_gcli_commands.js rename to browser/devtools/commandline/test/browser_cmd_commands.js --- a/browser/devtools/commandline/test/browser_gcli_commands.js +++ b/browser/devtools/commandline/test/browser_cmd_commands.js @@ -4,36 +4,30 @@ // Test various GCLI commands let imported = {}; Components.utils.import("resource:///modules/HUDService.jsm", imported); const TEST_URI = "data:text/html;charset=utf-8,gcli-commands"; function test() { - DeveloperToolbarTest.test(TEST_URI, function(browser, tab) { - testEcho(); - testConsole(tab); - - imported = undefined; - finish(); - }); + DeveloperToolbarTest.test(TEST_URI, [ testEcho, testConsole ]); } function testEcho() { /* DeveloperToolbarTest.exec({ typed: "echo message", args: { message: "message" }, outputMatch: /^message$/, }); */ } -function testConsole(tab) { +function testConsole(browser, tab) { let hud = null; function onWebConsoleOpen(aSubject) { Services.obs.removeObserver(onWebConsoleOpen, "web-console-created"); aSubject.QueryInterface(Ci.nsISupportsString); hud = imported.HUDService.getHudReferenceById(aSubject.data); ok(hud.hudId in imported.HUDService.hudReferences, "console open"); @@ -65,11 +59,10 @@ function testConsole(tab) { typed: "console close", args: {}, blankOutput: true, }); ok(!(hud.hudId in imported.HUDService.hudReferences), "console closed"); imported = undefined; - finish(); } }
rename from browser/devtools/commandline/test/browser_gcli_cookie.js rename to browser/devtools/commandline/test/browser_cmd_cookie.js --- a/browser/devtools/commandline/test/browser_gcli_cookie.js +++ b/browser/devtools/commandline/test/browser_cmd_cookie.js @@ -1,20 +1,17 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests that the cookie commands works as they should const TEST_URI = "data:text/html;charset=utf-8,gcli-cookie"; function test() { - DeveloperToolbarTest.test(TEST_URI, function(browser, tab) { - testCookieCommands(); - finish(); - }); + DeveloperToolbarTest.test(TEST_URI, [ testCookieCommands ]); } function testCookieCommands() { DeveloperToolbarTest.checkInputStatus({ typed: "cook", directTabText: "ie", status: "ERROR" });
rename from browser/devtools/commandline/test/browser_gcli_integrate.js rename to browser/devtools/commandline/test/browser_cmd_integrate.js
rename from browser/devtools/commandline/test/browser_gcli_jsb.js rename to browser/devtools/commandline/test/browser_cmd_jsb.js --- a/browser/devtools/commandline/test/browser_gcli_jsb.js +++ b/browser/devtools/commandline/test/browser_cmd_jsb.js @@ -1,48 +1,51 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the jsb command works as it should + +const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" + + "test/browser_cmd_jsb_script.jsi"; + function test() { - const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" + - "test/resources_jsb_script.js"; + DeveloperToolbarTest.test("about:blank", [ /*GJT_test*/ ]); +} - DeveloperToolbarTest.test("about:blank", function GJT_test() { - /* Commented out by bug 774057, re-enable with un-hidden jsb command - DeveloperToolbarTest.exec({ - typed: "jsb AAA", - outputMatch: /valid/ - }); +function GJT_test() { + DeveloperToolbarTest.exec({ + typed: "jsb AAA", + outputMatch: /valid/ + }); - gBrowser.addTabsProgressListener({ - onProgressChange: function GJT_onProgressChange(aBrowser) { - gBrowser.removeTabsProgressListener(this); + gBrowser.addTabsProgressListener({ + onProgressChange: DeveloperToolbarTest.checkCalled(function GJT_onProgressChange(aBrowser) { + gBrowser.removeTabsProgressListener(this); - let win = aBrowser._contentWindow; - let uri = win.document.location.href; - let result = win.atob(uri.replace(/.*,/, "")); - - result = result.replace(/[\r\n]]/g, "\n"); + let win = aBrowser._contentWindow; + let uri = win.document.location.href; + let result = win.atob(uri.replace(/.*,/, "")); - checkResult(result); - finish(); - } - }); + result = result.replace(/[\r\n]]/g, "\n"); + + checkResult(result); + }) + }); - info("Checking beautification"); - DeveloperToolbarTest.checkInputStatus({ - typed: "jsb " + TEST_URI + " 4 space true -1 false collapse true false", - status: "VALID" - }); - DeveloperToolbarTest.exec({ completed: false }); + info("Checking beautification"); + DeveloperToolbarTest.checkInputStatus({ + typed: "jsb " + TEST_URI + " 4 space true -1 false collapse true false", + status: "VALID" + }); + DeveloperToolbarTest.exec({ completed: false }); - function checkResult(aResult) { - let correct = "function somefunc() {\n" + - " for (let n = 0; n < 500; n++) {\n" + - " if (n % 2 == 1) {\n" + - " console.log(n);\n" + - " console.log(n + 1);\n" + - " }\n" + - " }\n" + - "}"; - is(aResult, correct, "JS has been correctly prettified"); - } - */ - finish(); - }); + function checkResult(aResult) { + let correct = "function somefunc() {\n" + + " for (let n = 0; n < 500; n++) {\n" + + " if (n % 2 == 1) {\n" + + " console.log(n);\n" + + " console.log(n + 1);\n" + + " }\n" + + " }\n" + + "}"; + is(aResult, correct, "JS has been correctly prettified"); + } }
rename from browser/devtools/commandline/test/resources_jsb_script.js rename to browser/devtools/commandline/test/browser_cmd_jsb_script.jsi
rename from browser/devtools/commandline/test/browser_gcli_inspect.html rename to browser/devtools/commandline/test/browser_cmd_pagemod_export.html
rename from browser/devtools/commandline/test/browser_gcli_pagemod_export.js rename to browser/devtools/commandline/test/browser_cmd_pagemod_export.js --- a/browser/devtools/commandline/test/browser_gcli_pagemod_export.js +++ b/browser/devtools/commandline/test/browser_cmd_pagemod_export.js @@ -1,27 +1,30 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests that the inspect command works as it should -const TEST_URI = "http://example.com/browser/browser/devtools/commandline/test/browser_gcli_inspect.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/commandline/"+ + "test/browser_cmd_pagemod_export.html"; function test() { let initialHtml = ""; - DeveloperToolbarTest.test(TEST_URI, function(browser, tab) { - initialHtml = content.document.documentElement.innerHTML; + DeveloperToolbarTest.test(TEST_URI, [ + init, + testExportHtml, + testPageModReplace, + testPageModRemoveElement, + testPageModRemoveAttribute + ]); - testExportHtml(); - testPageModReplace(); - testPageModRemoveElement(); - testPageModRemoveAttribute(); - finish(); - }); + function init() { + initialHtml = content.document.documentElement.innerHTML; + } function testExportHtml() { DeveloperToolbarTest.checkInputStatus({ typed: "export html", status: "VALID" }); let oldOpen = content.open;
rename from browser/devtools/commandline/test/browser_gcli_pref.js rename to browser/devtools/commandline/test/browser_cmd_pref.js --- a/browser/devtools/commandline/test/browser_gcli_pref.js +++ b/browser/devtools/commandline/test/browser_cmd_pref.js @@ -17,29 +17,26 @@ imports.XPCOMUtils.defineLazyGetter(impo imports.XPCOMUtils.defineLazyGetter(imports, "supportsString", function() { return Components.classes["@mozilla.org/supports-string;1"] .createInstance(Components.interfaces.nsISupportsString); }); const TEST_URI = "data:text/html;charset=utf-8,gcli-pref"; function test() { - DeveloperToolbarTest.test(TEST_URI, function(browser, tab) { - setup(); - - testPrefSetEnable(); - testPrefStatus(); - testPrefBoolExec(); - testPrefNumberExec(); - testPrefStringExec(); - testPrefSetDisable(); - - shutdown(); - finish(); - }); + DeveloperToolbarTest.test(TEST_URI, [ + setup, + testPrefSetEnable, + testPrefStatus, + testPrefBoolExec, + testPrefNumberExec, + testPrefStringExec, + testPrefSetDisable, + shutdown + ]); } let tiltEnabledOrig = undefined; let tabSizeOrig = undefined; let remoteHostOrig = undefined; function setup() { Components.utils.import("resource://gre/modules/devtools/Require.jsm", imports);
rename from browser/devtools/commandline/test/browser_gcli_restart.js rename to browser/devtools/commandline/test/browser_cmd_restart.js --- a/browser/devtools/commandline/test/browser_gcli_restart.js +++ b/browser/devtools/commandline/test/browser_cmd_restart.js @@ -1,20 +1,17 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Test that restart command works properly (input wise) const TEST_URI = "data:text/html;charset=utf-8,gcli-command-restart"; function test() { - DeveloperToolbarTest.test(TEST_URI, function(browser, tab) { - testRestart(); - finish(); - }); + DeveloperToolbarTest.test(TEST_URI, [ testRestart ]); } function testRestart() { DeveloperToolbarTest.checkInputStatus({ typed: "restart", markup: "VVVVVVV", status: "VALID", emptyParameters: [ " [nocache]" ],
rename from browser/devtools/commandline/test/browser_gcli_settings.js rename to browser/devtools/commandline/test/browser_cmd_settings.js --- a/browser/devtools/commandline/test/browser_gcli_settings.js +++ b/browser/devtools/commandline/test/browser_cmd_settings.js @@ -17,24 +17,17 @@ imports.XPCOMUtils.defineLazyGetter(impo imports.XPCOMUtils.defineLazyGetter(imports, "supportsString", function() { return Components.classes["@mozilla.org/supports-string;1"] .createInstance(Components.interfaces.nsISupportsString); }); const TEST_URI = "data:text/html;charset=utf-8,gcli-settings"; function test() { - DeveloperToolbarTest.test(TEST_URI, function(browser, tab) { - setup(); - - testSettings(); - - shutdown(); - finish(); - }); + DeveloperToolbarTest.test(TEST_URI, [ setup, testSettings, shutdown ]); } let tiltEnabled = undefined; let tabSize = undefined; let remoteHost = undefined; let tiltEnabledOrig = undefined; let tabSizeOrig = undefined;
rename from browser/devtools/commandline/test/resources_dbg.html rename to browser/devtools/commandline/test/browser_dbg_cmd.html
rename from browser/devtools/commandline/test/browser_gcli_dbg.js rename to browser/devtools/commandline/test/browser_dbg_cmd.js --- a/browser/devtools/commandline/test/browser_gcli_dbg.js +++ b/browser/devtools/commandline/test/browser_dbg_cmd.js @@ -1,67 +1,72 @@ function test() { - const TEST_URI = TEST_BASE_HTTP + "resources_dbg.html"; + const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" + + "test/browser_dbg_cmd.html"; - DeveloperToolbarTest.test(TEST_URI, function GAT_test() { - let pane = DebuggerUI.toggleDebugger(); - ok(pane, "toggleDebugger() should return a pane."); - let frame = pane._frame; + DeveloperToolbarTest.test(TEST_URI, function() { + testDbgCmd(); + }); +} - frame.addEventListener("Debugger:Connecting", function dbgConnected(aEvent) { - frame.removeEventListener("Debugger:Connecting", dbgConnected, true); +function testDbgCmd() { + let pane = DebuggerUI.toggleDebugger(); + ok(pane, "toggleDebugger() should return a pane."); + let frame = pane._frame; + + frame.addEventListener("Debugger:Connecting", function dbgConnected(aEvent) { + frame.removeEventListener("Debugger:Connecting", dbgConnected, true); - // Wait for the initial resume... - aEvent.target.ownerDocument.defaultView.gClient - .addOneTimeListener("resumed", function() { + // Wait for the initial resume... + aEvent.target.ownerDocument.defaultView.gClient + .addOneTimeListener("resumed", function() { - info("Starting tests."); + info("Starting tests."); - let contentDoc = content.window.document; - let output = contentDoc.querySelector("input[type=text]"); - let btnDoit = contentDoc.querySelector("input[type=button]"); + let contentDoc = content.window.document; + let output = contentDoc.querySelector("input[type=text]"); + let btnDoit = contentDoc.querySelector("input[type=button]"); - cmd("dbg interrupt", function() { - ok(true, "debugger is paused"); - pane.contentWindow.gClient.addOneTimeListener("resumed", function() { - ok(true, "debugger continued"); - pane.contentWindow.gClient.addOneTimeListener("paused", function() { + cmd("dbg interrupt", function() { + ok(true, "debugger is paused"); + pane.contentWindow.gClient.addOneTimeListener("resumed", function() { + ok(true, "debugger continued"); + pane.contentWindow.gClient.addOneTimeListener("paused", function() { + cmd("dbg step in", function() { cmd("dbg step in", function() { cmd("dbg step in", function() { - cmd("dbg step in", function() { - is(output.value, "step in", "debugger stepped in"); - cmd("dbg step over", function() { - is(output.value, "step over", "debugger stepped over"); - cmd("dbg step out", function() { - is(output.value, "step out", "debugger stepped out"); + is(output.value, "step in", "debugger stepped in"); + cmd("dbg step over", function() { + is(output.value, "step over", "debugger stepped over"); + cmd("dbg step out", function() { + is(output.value, "step out", "debugger stepped out"); + cmd("dbg continue", function() { cmd("dbg continue", function() { - cmd("dbg continue", function() { - is(output.value, "dbg continue", "debugger continued"); - pane.contentWindow.gClient.close(function() { - finish(); - }); + is(output.value, "dbg continue", "debugger continued"); + pane.contentWindow.gClient.close(function() { + finish(); }); }); }); }); }); }); }); }); - EventUtils.sendMouseEvent({type:"click"}, btnDoit); }); - DeveloperToolbarTest.exec({ - typed: "dbg continue", - blankOutput: true - }); + EventUtils.sendMouseEvent({type:"click"}, btnDoit); + }); + DeveloperToolbarTest.exec({ + typed: "dbg continue", + blankOutput: true }); }); + }); - function cmd(aTyped, aCallback) { - pane.contentWindow.gClient.addOneTimeListener("paused", aCallback); - DeveloperToolbarTest.exec({ - typed: aTyped, - blankOutput: true - }); - } - }); + function cmd(aTyped, aCallback) { + pane.contentWindow.gClient.addOneTimeListener("paused", aCallback); + DeveloperToolbarTest.exec({ + typed: aTyped, + blankOutput: true + }); + } }); }
rename from browser/devtools/commandline/test/browser_gcli_break.html rename to browser/devtools/commandline/test/browser_dbg_cmd_break.html
rename from browser/devtools/commandline/test/browser_gcli_break.js rename to browser/devtools/commandline/test/browser_dbg_cmd_break.js --- a/browser/devtools/commandline/test/browser_gcli_break.js +++ b/browser/devtools/commandline/test/browser_dbg_cmd_break.js @@ -1,22 +1,30 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests that the break command works as it should -const TEST_URI = "http://example.com/browser/browser/devtools/commandline/test/browser_gcli_break.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" + + "test/browser_dbg_cmd_break.html"; function test() { - DeveloperToolbarTest.test(TEST_URI, function(browser, tab) { - testBreakCommands(); - }); + DeveloperToolbarTest.test(TEST_URI, [ testBreakCommands ]); } function testBreakCommands() { + + info('###################################################'); + info('###################################################'); + info('###################################################'); + info('###################################################'); + info('###################################################'); + info('###################################################'); + info(content.document.documentElement.innerHTML + '\n'); + DeveloperToolbarTest.checkInputStatus({ typed: "brea", directTabText: "k", status: "ERROR" }); DeveloperToolbarTest.checkInputStatus({ typed: "break", @@ -30,23 +38,26 @@ function testBreakCommands() { DeveloperToolbarTest.checkInputStatus({ typed: "break add line", emptyParameters: [ " <file>", " <line>" ], status: "ERROR" }); let pane = DebuggerUI.toggleDebugger(); - pane._frame.addEventListener("Debugger:Connecting", function dbgConnected() { + + var dbgConnected = DeveloperToolbarTest.checkCalled(function() { pane._frame.removeEventListener("Debugger:Connecting", dbgConnected, true); // Wait for the initial resume. let client = pane.contentWindow.gClient; - client.addOneTimeListener("resumed", function() { - client.activeThread.addOneTimeListener("framesadded", function() { + + var resumed = DeveloperToolbarTest.checkCalled(function() { + + var framesAdded = DeveloperToolbarTest.checkCalled(function() { DeveloperToolbarTest.checkInputStatus({ typed: "break add line " + TEST_URI + " " + content.wrappedJSObject.line0, status: "VALID" }); DeveloperToolbarTest.exec({ args: { type: 'line', file: TEST_URI, @@ -56,27 +67,33 @@ function testBreakCommands() { }); DeveloperToolbarTest.checkInputStatus({ typed: "break list", status: "VALID" }); DeveloperToolbarTest.exec(); - client.activeThread.resume(function() { + var cleanup = DeveloperToolbarTest.checkCalled(function() { DeveloperToolbarTest.checkInputStatus({ typed: "break del 0", status: "VALID" }); DeveloperToolbarTest.exec({ args: { breakid: 0 }, completed: false }); + }); - finish(); - }); + client.activeThread.resume(cleanup); }); + client.activeThread.addOneTimeListener("framesadded", framesAdded); + // Trigger newScript notifications using eval. content.wrappedJSObject.firstCall(); }); - }, true); + + client.addOneTimeListener("resumed", resumed); + }); + + pane._frame.addEventListener("Debugger:Connecting", dbgConnected, true); }
--- a/browser/devtools/commandline/test/head.js +++ b/browser/devtools/commandline/test/head.js @@ -6,16 +6,20 @@ const TEST_BASE_HTTP = "http://example.c const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/commandline/test/"; let console = (function() { let tempScope = {}; Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope); return tempScope.console; })(); +// Import the GCLI test helper +let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/")); +Services.scriptloader.loadSubScript(testDir + "/helper.js", this); + /** * Open a new tab at a URL and call a callback on load */ function addTab(aURL, aCallback) { waitForExplicitFinish(); gBrowser.selectedTab = gBrowser.addTab(); @@ -34,523 +38,8 @@ function addTab(aURL, aCallback) registerCleanupFunction(function tearDown() { while (gBrowser.tabs.length > 1) { gBrowser.removeCurrentTab(); } console = undefined; }); - -/** - * Various functions for testing DeveloperToolbar. - * Parts of this code exist in: - * - browser/devtools/commandline/test/head.js - * - browser/devtools/shared/test/head.js - */ -let DeveloperToolbarTest = { }; - -/** - * Paranoid DeveloperToolbar.show(); - */ -DeveloperToolbarTest.show = function DTT_show(aCallback) { - if (DeveloperToolbar.visible) { - ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar"); - } - else { - DeveloperToolbar.show(true, aCallback); - } -}; - -/** - * Paranoid DeveloperToolbar.hide(); - */ -DeveloperToolbarTest.hide = function DTT_hide() { - if (!DeveloperToolbar.visible) { - ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar"); - } - else { - DeveloperToolbar.display.inputter.setInput(""); - DeveloperToolbar.hide(); - } -}; - -/** - * check() is the new status. Similar API except that it doesn't attempt to - * alter the display/requisition at all, and it makes extra checks. - * Test inputs - * typed: The text to type at the input - * Available checks: - * input: The text displayed in the input field - * cursor: The position of the start of the cursor - * status: One of "VALID", "ERROR", "INCOMPLETE" - * emptyParameters: Array of parameters still to type. e.g. [ "<message>" ] - * directTabText: Simple completion text - * arrowTabText: When the completion is not an extension (without arrow) - * markup: What state should the error markup be in. e.g. "VVVIIIEEE" - * args: Maps of checks to make against the arguments: - * value: i.e. assignment.value (which ignores defaultValue) - * type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned - * Care should be taken with this since it's something of an - * implementation detail - * arg: The toString value of the argument - * status: i.e. assignment.getStatus - * message: i.e. assignment.getMessage - * name: For commands - checks assignment.value.name - */ -DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) { - if (!checks.emptyParameters) { - checks.emptyParameters = []; - } - if (!checks.directTabText) { - checks.directTabText = ''; - } - if (!checks.arrowTabText) { - checks.arrowTabText = ''; - } - - var display = DeveloperToolbar.display; - - if (checks.typed) { - display.inputter.setInput(checks.typed); - } - else { - ok(false, "Missing typed for " + JSON.stringify(checks)); - return; - } - - if (checks.cursor) { - display.inputter.setCursor(checks.cursor) - } - - var cursor = checks.cursor ? checks.cursor.start : checks.typed.length; - - var requisition = display.requisition; - var completer = display.completer; - var actual = completer._getCompleterTemplateData(); - - /* - if (checks.input) { - is(display.inputter.element.value, - checks.input, - 'input'); - } - - if (checks.cursor) { - is(display.inputter.element.selectionStart, - checks.cursor, - 'cursor'); - } - */ - - if (checks.status) { - is(requisition.getStatus().toString(), - checks.status, - 'status'); - } - - if (checks.markup) { - var statusMarkup = requisition.getInputStatusMarkup(cursor); - var actualMarkup = statusMarkup.map(function(s) { - return Array(s.string.length + 1).join(s.status.toString()[0]); - }).join(''); - - is(checks.markup, - actualMarkup, - 'markup'); - } - - if (checks.emptyParameters) { - var actualParams = actual.emptyParameters; - is(actualParams.length, - checks.emptyParameters.length, - 'emptyParameters.length'); - - if (actualParams.length === checks.emptyParameters.length) { - for (var i = 0; i < actualParams.length; i++) { - is(actualParams[i].replace(/\u00a0/g, ' '), - checks.emptyParameters[i], - 'emptyParameters[' + i + ']'); - } - } - } - - if (checks.directTabText) { - is(actual.directTabText, - checks.directTabText, - 'directTabText'); - } - - if (checks.arrowTabText) { - is(actual.arrowTabText, - ' \u00a0\u21E5 ' + checks.arrowTabText, - 'arrowTabText'); - } - - if (checks.args) { - Object.keys(checks.args).forEach(function(paramName) { - var check = checks.args[paramName]; - - var assignment; - if (paramName === 'command') { - assignment = requisition.commandAssignment; - } - else { - assignment = requisition.getAssignment(paramName); - } - - if (assignment == null) { - ok(false, 'Unknown parameter: ' + paramName); - return; - } - - if (check.value) { - is(assignment.value, - check.value, - 'checkStatus value for ' + paramName); - } - - if (check.name) { - is(assignment.value.name, - check.name, - 'checkStatus name for ' + paramName); - } - - if (check.type) { - is(assignment.arg.type, - check.type, - 'checkStatus type for ' + paramName); - } - - if (check.arg) { - is(assignment.arg.toString(), - check.arg, - 'checkStatus arg for ' + paramName); - } - - if (check.status) { - is(assignment.getStatus().toString(), - check.status, - 'checkStatus status for ' + paramName); - } - - if (check.message) { - is(assignment.getMessage(), - check.message, - 'checkStatus message for ' + paramName); - } - }); - } -}; - -/** - * Execute a command: - * - * DeveloperToolbarTest.exec({ - * // Test inputs - * typed: "echo hi", // Optional, uses existing if undefined - * - * // Thing to check - * args: { message: "hi" }, // Check that the args were understood properly - * outputMatch: /^hi$/, // RegExp to test against textContent of output - * // (can also be array of RegExps) - * blankOutput: true, // Special checks when there is no output - * }); - */ -DeveloperToolbarTest.exec = function DTT_exec(tests) { - tests = tests || {}; - - if (tests.typed) { - DeveloperToolbar.display.inputter.setInput(tests.typed); - } - - let typed = DeveloperToolbar.display.inputter.getInputState().typed; - let output = DeveloperToolbar.display.requisition.exec(); - - is(typed, output.typed, 'output.command for: ' + typed); - - if (tests.completed !== false) { - ok(output.completed, 'output.completed false for: ' + typed); - } - else { - // It is actually an error if we say something is async and it turns - // out not to be? For now we're saying 'no' - // ok(!output.completed, 'output.completed true for: ' + typed); - } - - if (tests.args != null) { - is(Object.keys(tests.args).length, Object.keys(output.args).length, - 'arg count for ' + typed); - - Object.keys(output.args).forEach(function(arg) { - let expectedArg = tests.args[arg]; - let actualArg = output.args[arg]; - - if (typeof expectedArg === 'function') { - ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg); - } - else { - if (Array.isArray(expectedArg)) { - if (!Array.isArray(actualArg)) { - ok(false, 'actual is not an array. ' + typed + '/' + arg); - return; - } - - is(expectedArg.length, actualArg.length, - 'array length: ' + typed + '/' + arg); - for (let i = 0; i < expectedArg.length; i++) { - is(expectedArg[i], actualArg[i], - 'member: "' + typed + '/' + arg + '/' + i); - } - } - else { - is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg); - } - } - }); - } - - let displayed = DeveloperToolbar.outputPanel._div.textContent; - - if (tests.outputMatch) { - function doTest(match, against) { - if (!match.test(against)) { - ok(false, "html output for " + typed + " against " + match.source + - " (textContent sent to info)"); - info("Actual textContent"); - info(against); - } - } - if (Array.isArray(tests.outputMatch)) { - tests.outputMatch.forEach(function(match) { - doTest(match, displayed); - }); - } - else { - doTest(tests.outputMatch, displayed); - } - } - - if (tests.blankOutput != null) { - if (!/^$/.test(displayed)) { - ok(false, "html output for " + typed + " (textContent sent to info)"); - info("Actual textContent"); - info(displayed); - } - } -}; - -/** - * Quick wrapper around the things you need to do to run DeveloperToolbar - * command tests: - * - Set the pref 'devtools.toolbar.enabled' to true - * - Add a tab pointing at |uri| - * - Open the DeveloperToolbar - * - Register a cleanup function to undo the above - * - Run the tests - * - * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...' - * @param testFunc A function containing the tests to run. This should - * arrange for 'finish()' to be called on completion. - */ -DeveloperToolbarTest.test = function DTT_test(uri, testFunc) { - let menuItem = document.getElementById("menu_devToolbar"); - let command = document.getElementById("Tools:DevToolbar"); - let appMenuItem = document.getElementById("appmenu_devToolbar"); - - registerCleanupFunction(function() { - DeveloperToolbarTest.hide(); - - // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled"); - if (menuItem) { - menuItem.hidden = true; - } - if (command) { - command.setAttribute("disabled", "true"); - } - if (appMenuItem) { - appMenuItem.hidden = true; - } - - // leakHunt({ DeveloperToolbar: DeveloperToolbar }); - }); - - // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true); - if (menuItem) { - menuItem.hidden = false; - } - if (command) { - command.removeAttribute("disabled"); - } - if (appMenuItem) { - appMenuItem.hidden = false; - } - - addTab(uri, function(browser, tab) { - DeveloperToolbarTest.show(function() { - - try { - testFunc(browser, tab); - } - catch (ex) { - ok(false, "" + ex); - console.error(ex); - finish(); - throw ex; - } - }); - }); -}; - - -/** - * Memory leak hunter. Walks a tree of objects looking for DOM nodes. - * Usage: - * leakHunt({ - * thing: thing, - * otherthing: otherthing - * }); - */ - -var noRecurse = [ - /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/, - /^Window$/, /^Document$/, - /^XULDocument$/, /^XULElement$/, - /^DOMWindow$/, /^HTMLDocument$/, /^HTML.*Element$/ -]; - -var hide = [ /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/ ]; - -function leakHunt(root, path, seen) { - path = path || []; - seen = seen || []; - - try { - var output = leakHuntInner(root, path, seen); - output.forEach(function(line) { - dump(line + '\n'); - }); - } - catch (ex) { - dump(ex + '\n'); - } -} - -function leakHuntInner(root, path, seen) { - var prefix = new Array(path.length).join(' '); - - var reply = []; - function log(msg) { - reply.push(msg); - } - - var direct - try { - direct = Object.keys(root); - } - catch (ex) { - log(prefix + ' Error enumerating: ' + ex); - return reply; - } - - for (var prop in root) { - var newPath = path.slice(); - newPath.push(prop); - prefix = new Array(newPath.length).join(' '); - - var data; - try { - data = root[prop]; - } - catch (ex) { - log(prefix + prop + ' Error reading: ' + ex); - continue; - } - - var recurse = true; - var message = getType(data); - - if (matchesAnyPattern(message, hide)) { - continue; - } - - if (message === 'function' && direct.indexOf(prop) == -1) { - continue; - } - - if (message === 'string') { - var extra = data.length > 10 ? data.substring(0, 9) + '_' : data; - message += ' "' + extra.replace(/\n/g, "|") + '"'; - recurse = false; - } - else if (matchesAnyPattern(message, noRecurse)) { - message += ' (no recurse)' - recurse = false; - } - else if (seen.indexOf(data) !== -1) { - message += ' (already seen)'; - recurse = false; - } - - if (recurse) { - seen.push(data); - var lines = leakHuntInner(data, newPath, seen); - if (lines.length == 0) { - if (message !== 'function') { - log(prefix + prop + ' = ' + message + ' { }'); - } - } - else { - log(prefix + prop + ' = ' + message + ' {'); - lines.forEach(function(line) { - reply.push(line); - }); - log(prefix + '}'); - } - } - else { - log(prefix + prop + ' = ' + message); - } - } - - return reply; -} - -function matchesAnyPattern(str, patterns) { - var match = false; - patterns.forEach(function(pattern) { - if (str.match(pattern)) { - match = true; - } - }); - return match; -} - -function getType(data) { - if (data === null) { - return 'null'; - } - if (data === undefined) { - return 'undefined'; - } - - var type = typeof data; - if (type === 'object' || type === 'Object') { - type = getCtorName(data); - } - - return type; -} - -function getCtorName(aObj) { - try { - if (aObj.constructor && aObj.constructor.name) { - return aObj.constructor.name; - } - } - catch (ex) { - return 'UnknownObject'; - } - - // If that fails, use Objects toString which sometimes gives something - // better than 'Object', and at least defaults to Object if nothing better - return Object.prototype.toString.call(aObj).slice(8, -1); -}
new file mode 100644 --- /dev/null +++ b/browser/devtools/commandline/test/helper.js @@ -0,0 +1,459 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + * + * DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES + * OF THIS FILE. + * + * UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON + * THE NAUGHTY STEP. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * FOR A LONG TIME. + * + */ + + +/** + * Various functions for testing DeveloperToolbar. + * Parts of this code exist in: + * - browser/devtools/commandline/test/head.js + * - browser/devtools/shared/test/head.js + */ +let DeveloperToolbarTest = { }; + +/** + * Paranoid DeveloperToolbar.show(); + */ +DeveloperToolbarTest.show = function DTT_show(aCallback) { + if (DeveloperToolbar.visible) { + ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar"); + } + else { + DeveloperToolbar.show(true, aCallback); + } +}; + +/** + * Paranoid DeveloperToolbar.hide(); + */ +DeveloperToolbarTest.hide = function DTT_hide() { + if (!DeveloperToolbar.visible) { + ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar"); + } + else { + DeveloperToolbar.display.inputter.setInput(""); + DeveloperToolbar.hide(); + } +}; + +/** + * check() is the new status. Similar API except that it doesn't attempt to + * alter the display/requisition at all, and it makes extra checks. + * Test inputs + * typed: The text to type at the input + * Available checks: + * input: The text displayed in the input field + * cursor: The position of the start of the cursor + * status: One of "VALID", "ERROR", "INCOMPLETE" + * emptyParameters: Array of parameters still to type. e.g. [ "<message>" ] + * directTabText: Simple completion text + * arrowTabText: When the completion is not an extension (without arrow) + * markup: What state should the error markup be in. e.g. "VVVIIIEEE" + * args: Maps of checks to make against the arguments: + * value: i.e. assignment.value (which ignores defaultValue) + * type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned + * Care should be taken with this since it's something of an + * implementation detail + * arg: The toString value of the argument + * status: i.e. assignment.getStatus + * message: i.e. assignment.getMessage + * name: For commands - checks assignment.value.name + */ +DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) { + if (!checks.emptyParameters) { + checks.emptyParameters = []; + } + if (!checks.directTabText) { + checks.directTabText = ''; + } + if (!checks.arrowTabText) { + checks.arrowTabText = ''; + } + + var display = DeveloperToolbar.display; + + if (checks.typed) { + info('Starting tests for ' + checks.typed); + display.inputter.setInput(checks.typed); + } + else { + ok(false, "Missing typed for " + JSON.stringify(checks)); + return; + } + + if (checks.cursor) { + display.inputter.setCursor(checks.cursor) + } + + var cursor = checks.cursor ? checks.cursor.start : checks.typed.length; + + var requisition = display.requisition; + var completer = display.completer; + var actual = completer._getCompleterTemplateData(); + + /* + if (checks.input) { + is(display.inputter.element.value, + checks.input, + 'input'); + } + + if (checks.cursor) { + is(display.inputter.element.selectionStart, + checks.cursor, + 'cursor'); + } + */ + + if (checks.status) { + is(requisition.getStatus().toString(), + checks.status, + 'status'); + } + + if (checks.markup) { + var statusMarkup = requisition.getInputStatusMarkup(cursor); + var actualMarkup = statusMarkup.map(function(s) { + return Array(s.string.length + 1).join(s.status.toString()[0]); + }).join(''); + + is(checks.markup, + actualMarkup, + 'markup'); + } + + if (checks.emptyParameters) { + var actualParams = actual.emptyParameters; + is(actualParams.length, + checks.emptyParameters.length, + 'emptyParameters.length'); + + if (actualParams.length === checks.emptyParameters.length) { + for (var i = 0; i < actualParams.length; i++) { + is(actualParams[i].replace(/\u00a0/g, ' '), + checks.emptyParameters[i], + 'emptyParameters[' + i + ']'); + } + } + } + + if (checks.directTabText) { + is(actual.directTabText, + checks.directTabText, + 'directTabText'); + } + + if (checks.arrowTabText) { + is(actual.arrowTabText, + ' \u00a0\u21E5 ' + checks.arrowTabText, + 'arrowTabText'); + } + + if (checks.args) { + Object.keys(checks.args).forEach(function(paramName) { + var check = checks.args[paramName]; + + var assignment; + if (paramName === 'command') { + assignment = requisition.commandAssignment; + } + else { + assignment = requisition.getAssignment(paramName); + } + + if (assignment == null) { + ok(false, 'Unknown parameter: ' + paramName); + return; + } + + if (check.value) { + is(assignment.value, + check.value, + 'checkStatus value for ' + paramName); + } + + if (check.name) { + is(assignment.value.name, + check.name, + 'checkStatus name for ' + paramName); + } + + if (check.type) { + is(assignment.arg.type, + check.type, + 'checkStatus type for ' + paramName); + } + + if (check.arg) { + is(assignment.arg.toString(), + check.arg, + 'checkStatus arg for ' + paramName); + } + + if (check.status) { + is(assignment.getStatus().toString(), + check.status, + 'checkStatus status for ' + paramName); + } + + if (check.message) { + is(assignment.getMessage(), + check.message, + 'checkStatus message for ' + paramName); + } + }); + } +}; + +/** + * Execute a command: + * + * DeveloperToolbarTest.exec({ + * // Test inputs + * typed: "echo hi", // Optional, uses existing if undefined + * + * // Thing to check + * args: { message: "hi" }, // Check that the args were understood properly + * outputMatch: /^hi$/, // RegExp to test against textContent of output + * // (can also be array of RegExps) + * blankOutput: true, // Special checks when there is no output + * }); + */ +DeveloperToolbarTest.exec = function DTT_exec(tests) { + tests = tests || {}; + + if (tests.typed) { + DeveloperToolbar.display.inputter.setInput(tests.typed); + } + + let typed = DeveloperToolbar.display.inputter.getInputState().typed; + let output = DeveloperToolbar.display.requisition.exec(); + + is(typed, output.typed, 'output.command for: ' + typed); + + if (tests.completed !== false) { + ok(output.completed, 'output.completed false for: ' + typed); + } + else { + // It is actually an error if we say something is async and it turns + // out not to be? For now we're saying 'no' + // ok(!output.completed, 'output.completed true for: ' + typed); + } + + if (tests.args != null) { + is(Object.keys(tests.args).length, Object.keys(output.args).length, + 'arg count for ' + typed); + + Object.keys(output.args).forEach(function(arg) { + let expectedArg = tests.args[arg]; + let actualArg = output.args[arg]; + + if (typeof expectedArg === 'function') { + ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg); + } + else { + if (Array.isArray(expectedArg)) { + if (!Array.isArray(actualArg)) { + ok(false, 'actual is not an array. ' + typed + '/' + arg); + return; + } + + is(expectedArg.length, actualArg.length, + 'array length: ' + typed + '/' + arg); + for (let i = 0; i < expectedArg.length; i++) { + is(expectedArg[i], actualArg[i], + 'member: "' + typed + '/' + arg + '/' + i); + } + } + else { + is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg); + } + } + }); + } + + let displayed = DeveloperToolbar.outputPanel._div.textContent; + + if (tests.outputMatch) { + var doTest = function(match, against) { + if (!match.test(against)) { + ok(false, "html output for " + typed + " against " + match.source + + " (textContent sent to info)"); + info("Actual textContent"); + info(against); + } + } + if (Array.isArray(tests.outputMatch)) { + tests.outputMatch.forEach(function(match) { + doTest(match, displayed); + }); + } + else { + doTest(tests.outputMatch, displayed); + } + } + + if (tests.blankOutput != null) { + if (!/^$/.test(displayed)) { + ok(false, "html output for " + typed + " (textContent sent to info)"); + info("Actual textContent"); + info(displayed); + } + } +}; + +/** + * Quick wrapper around the things you need to do to run DeveloperToolbar + * command tests: + * - Set the pref 'devtools.toolbar.enabled' to true + * - Add a tab pointing at |uri| + * - Open the DeveloperToolbar + * - Register a cleanup function to undo the above + * - Run the tests + * + * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...' + * @param target Either a function or array of functions containing the tests + * to run. If an array of test function is passed then we will clear up after + * the tests have completed. If a single test function is passed then this + * function should arrange for 'finish()' to be called on completion. + */ +DeveloperToolbarTest.test = function DTT_test(uri, target) { + let menuItem = document.getElementById("menu_devToolbar"); + let command = document.getElementById("Tools:DevToolbar"); + let appMenuItem = document.getElementById("appmenu_devToolbar"); + + registerCleanupFunction(function() { + DeveloperToolbarTest.hide(); + + // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled"); + if (menuItem) { + menuItem.hidden = true; + } + if (command) { + command.setAttribute("disabled", "true"); + } + if (appMenuItem) { + appMenuItem.hidden = true; + } + + // leakHunt({ DeveloperToolbar: DeveloperToolbar }); + }); + + // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true); + if (menuItem) { + menuItem.hidden = false; + } + if (command) { + command.removeAttribute("disabled"); + } + if (appMenuItem) { + appMenuItem.hidden = false; + } + + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + content.location = uri; + + let tab = gBrowser.selectedTab; + let browser = gBrowser.getBrowserForTab(tab); + + var onTabLoad = function() { + browser.removeEventListener("load", onTabLoad, true); + + DeveloperToolbarTest.show(function() { + if (Array.isArray(target)) { + try { + target.forEach(function(func) { + func(browser, tab); + }) + } + finally { + DeveloperToolbarTest._checkFinish(); + } + } + else { + try { + target(browser, tab); + } + catch (ex) { + ok(false, "" + ex); + DeveloperToolbarTest._finish(); + throw ex; + } + } + }); + } + + browser.addEventListener("load", onTabLoad, true); +}; + +DeveloperToolbarTest._outstanding = []; + +DeveloperToolbarTest._checkFinish = function() { + if (DeveloperToolbarTest._outstanding.length == 0) { + DeveloperToolbarTest._finish(); + } +} + +DeveloperToolbarTest._finish = function() { + DeveloperToolbarTest.closeAllTabs(); + finish(); +} + +DeveloperToolbarTest.checkCalled = function(aFunc, aScope) { + var todo = function() { + var reply = aFunc.apply(aScope, arguments); + DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) { + return aJob != todo; + }); + DeveloperToolbarTest._checkFinish(); + return reply; + } + DeveloperToolbarTest._outstanding.push(todo); + return todo; +}; + +DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) { + return function() { + ok(false, aMsg); + return aFunc.apply(aScope, arguments); + } +}; + +/** + * + */ +DeveloperToolbarTest.closeAllTabs = function() { + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } +};
new file mode 100644 --- /dev/null +++ b/browser/devtools/highlighter/CmdInspect.jsm @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); + +/** + * 'inspect' command + */ +gcli.addCommand({ + name: "inspect", + description: gcli.lookup("inspectDesc"), + manual: gcli.lookup("inspectManual"), + params: [ + { + name: "node", + type: "node", + description: gcli.lookup("inspectNodeDesc"), + manual: gcli.lookup("inspectNodeManual") + } + ], + exec: function Command_inspect(args, context) { + let document = context.environment.chromeDocument; + document.defaultView.InspectorUI.openInspectorUI(args.node); + } +});
--- a/browser/devtools/highlighter/Makefile.in +++ b/browser/devtools/highlighter/Makefile.in @@ -19,8 +19,11 @@ EXTRA_JS_MODULES = \ EXTRA_PP_JS_MODULES = \ inspector.jsm \ $(NULL) TEST_DIRS += test include $(topsrcdir)/config/rules.mk + +libs:: + $(NSINSTALL) $(srcdir)/CmdInspect.jsm $(FINAL_TARGET)/modules/devtools
--- a/browser/devtools/highlighter/test/Makefile.in +++ b/browser/devtools/highlighter/test/Makefile.in @@ -32,13 +32,16 @@ include $(topsrcdir)/config/rules.mk browser_inspector_ruleviewstore.js \ browser_inspector_invalidate.js \ browser_inspector_sidebarstate.js \ browser_inspector_menu.js \ browser_inspector_pseudoclass_lock.js \ browser_inspector_pseudoClass_menu.js \ browser_inspector_destroyselection.html \ browser_inspector_destroyselection.js \ + browser_inspector_cmd_inspect.js \ + browser_inspector_cmd_inspect.html \ head.js \ + helper.js \ $(NULL) libs:: $(_BROWSER_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
copy from browser/devtools/commandline/test/browser_gcli_inspect.html copy to browser/devtools/highlighter/test/browser_inspector_cmd_inspect.html
rename from browser/devtools/commandline/test/browser_gcli_inspect.js rename to browser/devtools/highlighter/test/browser_inspector_cmd_inspect.js --- a/browser/devtools/commandline/test/browser_gcli_inspect.js +++ b/browser/devtools/highlighter/test/browser_inspector_cmd_inspect.js @@ -1,21 +1,18 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests that the inspect command works as it should -const TEST_URI = "http://example.com/browser/browser/devtools/commandline/test/browser_gcli_inspect.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/highlighter/" + + "test/browser_inspector_cmd_inspect.html"; function test() { - DeveloperToolbarTest.test(TEST_URI, function(browser, tab) { - testInspect(); - - finish(); - }); + DeveloperToolbarTest.test(TEST_URI, [ testInspect ]); } function testInspect() { DeveloperToolbarTest.checkInputStatus({ typed: "inspec", directTabText: "t", status: "ERROR" });
--- a/browser/devtools/highlighter/test/head.js +++ b/browser/devtools/highlighter/test/head.js @@ -2,16 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const Cu = Components.utils; let tempScope = {}; Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope); let LayoutHelpers = tempScope.LayoutHelpers; +// Import the GCLI test helper +let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/")); +Services.scriptloader.loadSubScript(testDir + "/helper.js", this); + // Clear preferences that may be set during the course of tests. function clearUserPrefs() { Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen"); Services.prefs.clearUserPref("devtools.inspector.sidebarOpen"); Services.prefs.clearUserPref("devtools.inspector.activeSidebar"); }
new file mode 100644 --- /dev/null +++ b/browser/devtools/highlighter/test/helper.js @@ -0,0 +1,459 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + * + * DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES + * OF THIS FILE. + * + * UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON + * THE NAUGHTY STEP. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * FOR A LONG TIME. + * + */ + + +/** + * Various functions for testing DeveloperToolbar. + * Parts of this code exist in: + * - browser/devtools/commandline/test/head.js + * - browser/devtools/shared/test/head.js + */ +let DeveloperToolbarTest = { }; + +/** + * Paranoid DeveloperToolbar.show(); + */ +DeveloperToolbarTest.show = function DTT_show(aCallback) { + if (DeveloperToolbar.visible) { + ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar"); + } + else { + DeveloperToolbar.show(true, aCallback); + } +}; + +/** + * Paranoid DeveloperToolbar.hide(); + */ +DeveloperToolbarTest.hide = function DTT_hide() { + if (!DeveloperToolbar.visible) { + ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar"); + } + else { + DeveloperToolbar.display.inputter.setInput(""); + DeveloperToolbar.hide(); + } +}; + +/** + * check() is the new status. Similar API except that it doesn't attempt to + * alter the display/requisition at all, and it makes extra checks. + * Test inputs + * typed: The text to type at the input + * Available checks: + * input: The text displayed in the input field + * cursor: The position of the start of the cursor + * status: One of "VALID", "ERROR", "INCOMPLETE" + * emptyParameters: Array of parameters still to type. e.g. [ "<message>" ] + * directTabText: Simple completion text + * arrowTabText: When the completion is not an extension (without arrow) + * markup: What state should the error markup be in. e.g. "VVVIIIEEE" + * args: Maps of checks to make against the arguments: + * value: i.e. assignment.value (which ignores defaultValue) + * type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned + * Care should be taken with this since it's something of an + * implementation detail + * arg: The toString value of the argument + * status: i.e. assignment.getStatus + * message: i.e. assignment.getMessage + * name: For commands - checks assignment.value.name + */ +DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) { + if (!checks.emptyParameters) { + checks.emptyParameters = []; + } + if (!checks.directTabText) { + checks.directTabText = ''; + } + if (!checks.arrowTabText) { + checks.arrowTabText = ''; + } + + var display = DeveloperToolbar.display; + + if (checks.typed) { + info('Starting tests for ' + checks.typed); + display.inputter.setInput(checks.typed); + } + else { + ok(false, "Missing typed for " + JSON.stringify(checks)); + return; + } + + if (checks.cursor) { + display.inputter.setCursor(checks.cursor) + } + + var cursor = checks.cursor ? checks.cursor.start : checks.typed.length; + + var requisition = display.requisition; + var completer = display.completer; + var actual = completer._getCompleterTemplateData(); + + /* + if (checks.input) { + is(display.inputter.element.value, + checks.input, + 'input'); + } + + if (checks.cursor) { + is(display.inputter.element.selectionStart, + checks.cursor, + 'cursor'); + } + */ + + if (checks.status) { + is(requisition.getStatus().toString(), + checks.status, + 'status'); + } + + if (checks.markup) { + var statusMarkup = requisition.getInputStatusMarkup(cursor); + var actualMarkup = statusMarkup.map(function(s) { + return Array(s.string.length + 1).join(s.status.toString()[0]); + }).join(''); + + is(checks.markup, + actualMarkup, + 'markup'); + } + + if (checks.emptyParameters) { + var actualParams = actual.emptyParameters; + is(actualParams.length, + checks.emptyParameters.length, + 'emptyParameters.length'); + + if (actualParams.length === checks.emptyParameters.length) { + for (var i = 0; i < actualParams.length; i++) { + is(actualParams[i].replace(/\u00a0/g, ' '), + checks.emptyParameters[i], + 'emptyParameters[' + i + ']'); + } + } + } + + if (checks.directTabText) { + is(actual.directTabText, + checks.directTabText, + 'directTabText'); + } + + if (checks.arrowTabText) { + is(actual.arrowTabText, + ' \u00a0\u21E5 ' + checks.arrowTabText, + 'arrowTabText'); + } + + if (checks.args) { + Object.keys(checks.args).forEach(function(paramName) { + var check = checks.args[paramName]; + + var assignment; + if (paramName === 'command') { + assignment = requisition.commandAssignment; + } + else { + assignment = requisition.getAssignment(paramName); + } + + if (assignment == null) { + ok(false, 'Unknown parameter: ' + paramName); + return; + } + + if (check.value) { + is(assignment.value, + check.value, + 'checkStatus value for ' + paramName); + } + + if (check.name) { + is(assignment.value.name, + check.name, + 'checkStatus name for ' + paramName); + } + + if (check.type) { + is(assignment.arg.type, + check.type, + 'checkStatus type for ' + paramName); + } + + if (check.arg) { + is(assignment.arg.toString(), + check.arg, + 'checkStatus arg for ' + paramName); + } + + if (check.status) { + is(assignment.getStatus().toString(), + check.status, + 'checkStatus status for ' + paramName); + } + + if (check.message) { + is(assignment.getMessage(), + check.message, + 'checkStatus message for ' + paramName); + } + }); + } +}; + +/** + * Execute a command: + * + * DeveloperToolbarTest.exec({ + * // Test inputs + * typed: "echo hi", // Optional, uses existing if undefined + * + * // Thing to check + * args: { message: "hi" }, // Check that the args were understood properly + * outputMatch: /^hi$/, // RegExp to test against textContent of output + * // (can also be array of RegExps) + * blankOutput: true, // Special checks when there is no output + * }); + */ +DeveloperToolbarTest.exec = function DTT_exec(tests) { + tests = tests || {}; + + if (tests.typed) { + DeveloperToolbar.display.inputter.setInput(tests.typed); + } + + let typed = DeveloperToolbar.display.inputter.getInputState().typed; + let output = DeveloperToolbar.display.requisition.exec(); + + is(typed, output.typed, 'output.command for: ' + typed); + + if (tests.completed !== false) { + ok(output.completed, 'output.completed false for: ' + typed); + } + else { + // It is actually an error if we say something is async and it turns + // out not to be? For now we're saying 'no' + // ok(!output.completed, 'output.completed true for: ' + typed); + } + + if (tests.args != null) { + is(Object.keys(tests.args).length, Object.keys(output.args).length, + 'arg count for ' + typed); + + Object.keys(output.args).forEach(function(arg) { + let expectedArg = tests.args[arg]; + let actualArg = output.args[arg]; + + if (typeof expectedArg === 'function') { + ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg); + } + else { + if (Array.isArray(expectedArg)) { + if (!Array.isArray(actualArg)) { + ok(false, 'actual is not an array. ' + typed + '/' + arg); + return; + } + + is(expectedArg.length, actualArg.length, + 'array length: ' + typed + '/' + arg); + for (let i = 0; i < expectedArg.length; i++) { + is(expectedArg[i], actualArg[i], + 'member: "' + typed + '/' + arg + '/' + i); + } + } + else { + is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg); + } + } + }); + } + + let displayed = DeveloperToolbar.outputPanel._div.textContent; + + if (tests.outputMatch) { + var doTest = function(match, against) { + if (!match.test(against)) { + ok(false, "html output for " + typed + " against " + match.source + + " (textContent sent to info)"); + info("Actual textContent"); + info(against); + } + } + if (Array.isArray(tests.outputMatch)) { + tests.outputMatch.forEach(function(match) { + doTest(match, displayed); + }); + } + else { + doTest(tests.outputMatch, displayed); + } + } + + if (tests.blankOutput != null) { + if (!/^$/.test(displayed)) { + ok(false, "html output for " + typed + " (textContent sent to info)"); + info("Actual textContent"); + info(displayed); + } + } +}; + +/** + * Quick wrapper around the things you need to do to run DeveloperToolbar + * command tests: + * - Set the pref 'devtools.toolbar.enabled' to true + * - Add a tab pointing at |uri| + * - Open the DeveloperToolbar + * - Register a cleanup function to undo the above + * - Run the tests + * + * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...' + * @param target Either a function or array of functions containing the tests + * to run. If an array of test function is passed then we will clear up after + * the tests have completed. If a single test function is passed then this + * function should arrange for 'finish()' to be called on completion. + */ +DeveloperToolbarTest.test = function DTT_test(uri, target) { + let menuItem = document.getElementById("menu_devToolbar"); + let command = document.getElementById("Tools:DevToolbar"); + let appMenuItem = document.getElementById("appmenu_devToolbar"); + + registerCleanupFunction(function() { + DeveloperToolbarTest.hide(); + + // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled"); + if (menuItem) { + menuItem.hidden = true; + } + if (command) { + command.setAttribute("disabled", "true"); + } + if (appMenuItem) { + appMenuItem.hidden = true; + } + + // leakHunt({ DeveloperToolbar: DeveloperToolbar }); + }); + + // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true); + if (menuItem) { + menuItem.hidden = false; + } + if (command) { + command.removeAttribute("disabled"); + } + if (appMenuItem) { + appMenuItem.hidden = false; + } + + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + content.location = uri; + + let tab = gBrowser.selectedTab; + let browser = gBrowser.getBrowserForTab(tab); + + var onTabLoad = function() { + browser.removeEventListener("load", onTabLoad, true); + + DeveloperToolbarTest.show(function() { + if (Array.isArray(target)) { + try { + target.forEach(function(func) { + func(browser, tab); + }) + } + finally { + DeveloperToolbarTest._checkFinish(); + } + } + else { + try { + target(browser, tab); + } + catch (ex) { + ok(false, "" + ex); + DeveloperToolbarTest._finish(); + throw ex; + } + } + }); + } + + browser.addEventListener("load", onTabLoad, true); +}; + +DeveloperToolbarTest._outstanding = []; + +DeveloperToolbarTest._checkFinish = function() { + if (DeveloperToolbarTest._outstanding.length == 0) { + DeveloperToolbarTest._finish(); + } +} + +DeveloperToolbarTest._finish = function() { + DeveloperToolbarTest.closeAllTabs(); + finish(); +} + +DeveloperToolbarTest.checkCalled = function(aFunc, aScope) { + var todo = function() { + var reply = aFunc.apply(aScope, arguments); + DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) { + return aJob != todo; + }); + DeveloperToolbarTest._checkFinish(); + return reply; + } + DeveloperToolbarTest._outstanding.push(todo); + return todo; +}; + +DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) { + return function() { + ok(false, aMsg); + return aFunc.apply(aScope, arguments); + } +}; + +/** + * + */ +DeveloperToolbarTest.closeAllTabs = function() { + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } +};
--- a/browser/devtools/jar.mn +++ b/browser/devtools/jar.mn @@ -21,11 +21,11 @@ browser.jar: content/browser/devtools/layoutview/view.xhtml (layoutview/view.xhtml) content/browser/devtools/layoutview/view.css (layoutview/view.css) content/browser/orion.js (sourceeditor/orion/orion.js) * content/browser/source-editor-overlay.xul (sourceeditor/source-editor-overlay.xul) * content/browser/debugger.xul (debugger/debugger.xul) content/browser/debugger.css (debugger/debugger.css) content/browser/debugger-controller.js (debugger/debugger-controller.js) content/browser/debugger-view.js (debugger/debugger-view.js) - content/browser/devtools/gcli.css (commandline/gcli.css) - content/browser/devtools/gclioutput.xhtml (commandline/gclioutput.xhtml) - content/browser/devtools/gclitooltip.xhtml (commandline/gclitooltip.xhtml) + content/browser/devtools/commandline.css (commandline/commandline.css) + content/browser/devtools/commandlineoutput.xhtml (commandline/commandlineoutput.xhtml) + content/browser/devtools/commandlinetooltip.xhtml (commandline/commandlinetooltip.xhtml)
new file mode 100644 --- /dev/null +++ b/browser/devtools/responsivedesign/CmdResize.jsm @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource:///modules/devtools/gcli.jsm"); + +/* Responsive Mode commands */ +gcli.addCommand({ + name: 'resize', + description: gcli.lookup('resizeModeDesc') +}); + +gcli.addCommand({ + name: 'resize on', + description: gcli.lookup('resizeModeOnDesc'), + manual: gcli.lookup('resizeModeManual'), + exec: gcli_cmd_resize +}); + +gcli.addCommand({ + name: 'resize off', + description: gcli.lookup('resizeModeOffDesc'), + manual: gcli.lookup('resizeModeManual'), + exec: gcli_cmd_resize +}); + +gcli.addCommand({ + name: 'resize toggle', + description: gcli.lookup('resizeModeToggleDesc'), + manual: gcli.lookup('resizeModeManual'), + exec: gcli_cmd_resize +}); + +gcli.addCommand({ + name: 'resize to', + description: gcli.lookup('resizeModeToDesc'), + params: [ + { + name: 'width', + type: 'number', + description: gcli.lookup("resizePageArgWidthDesc"), + }, + { + name: 'height', + type: 'number', + description: gcli.lookup("resizePageArgHeightDesc"), + }, + ], + exec: gcli_cmd_resize +}); + +function gcli_cmd_resize(args, context) { + let browserDoc = context.environment.chromeDocument; + let browserWindow = browserDoc.defaultView; + let mgr = browserWindow.ResponsiveUI.ResponsiveUIManager; + mgr.handleGcliCommand(browserWindow, + browserWindow.gBrowser.selectedTab, + this.name, + args); +}
--- a/browser/devtools/responsivedesign/test/Makefile.in +++ b/browser/devtools/responsivedesign/test/Makefile.in @@ -42,14 +42,17 @@ VPATH = @srcdir@ relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _BROWSER_FILES = \ browser_responsiveui.js \ browser_responsiveruleview.js \ + browser_responsive_cmd.js \ browser_responsivecomputedview.js \ + head.js \ + helper.js \ $(NULL) libs:: $(_BROWSER_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
rename from browser/devtools/commandline/test/browser_gcli_responsivemode.js rename to browser/devtools/responsivedesign/test/browser_responsive_cmd.js --- a/browser/devtools/commandline/test/browser_gcli_responsivemode.js +++ b/browser/devtools/responsivedesign/test/browser_responsive_cmd.js @@ -1,58 +1,60 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ function test() { - DeveloperToolbarTest.test("about:blank", function GAT_test() { - DeveloperToolbarTest.checkInputStatus({ - typed: "resize toggle", - status: "VALID" - }); - DeveloperToolbarTest.exec(); - ok(isOpen(), "responsive mode is open"); + DeveloperToolbarTest.test("about:blank", [ GAT_test ]); +} + +function isOpen() { + return !!gBrowser.selectedTab.__responsiveUI; +} + +function isClosed() { + return !isOpen(); +} - DeveloperToolbarTest.checkInputStatus({ - typed: "resize toggle", - status: "VALID" - }); - DeveloperToolbarTest.exec(); - ok(isClosed(), "responsive mode is closed"); +function GAT_test() { + DeveloperToolbarTest.checkInputStatus({ + typed: "resize toggle", + status: "VALID" + }); + DeveloperToolbarTest.exec(); + ok(isOpen(), "responsive mode is open"); - DeveloperToolbarTest.checkInputStatus({ - typed: "resize on", - status: "VALID" - }); - DeveloperToolbarTest.exec(); - ok(isOpen(), "responsive mode is open"); + DeveloperToolbarTest.checkInputStatus({ + typed: "resize toggle", + status: "VALID" + }); + DeveloperToolbarTest.exec(); + ok(isClosed(), "responsive mode is closed"); - DeveloperToolbarTest.checkInputStatus({ - typed: "resize off", - status: "VALID" - }); - DeveloperToolbarTest.exec(); - ok(isClosed(), "responsive mode is closed"); + DeveloperToolbarTest.checkInputStatus({ + typed: "resize on", + status: "VALID" + }); + DeveloperToolbarTest.exec(); + ok(isOpen(), "responsive mode is open"); - DeveloperToolbarTest.checkInputStatus({ - typed: "resize to 400 400", - status: "VALID" - }); - DeveloperToolbarTest.exec(); - ok(isOpen(), "responsive mode is open"); + DeveloperToolbarTest.checkInputStatus({ + typed: "resize off", + status: "VALID" + }); + DeveloperToolbarTest.exec(); + ok(isClosed(), "responsive mode is closed"); - DeveloperToolbarTest.checkInputStatus({ - typed: "resize off", - status: "VALID" - }); - DeveloperToolbarTest.exec(); - ok(isClosed(), "responsive mode is closed"); - - executeSoon(finish); + DeveloperToolbarTest.checkInputStatus({ + typed: "resize to 400 400", + status: "VALID" }); - - function isOpen() { - return !!gBrowser.selectedTab.__responsiveUI; - } + DeveloperToolbarTest.exec(); + ok(isOpen(), "responsive mode is open"); - function isClosed() { - return !isOpen(); - } + DeveloperToolbarTest.checkInputStatus({ + typed: "resize off", + status: "VALID" + }); + DeveloperToolbarTest.exec(); + ok(isClosed(), "responsive mode is closed"); + + // executeSoon(finish); }
new file mode 100644 --- /dev/null +++ b/browser/devtools/responsivedesign/test/head.js @@ -0,0 +1,8 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Import the GCLI test helper +let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/")); +Services.scriptloader.loadSubScript(testDir + "/helper.js", this);
new file mode 100644 --- /dev/null +++ b/browser/devtools/responsivedesign/test/helper.js @@ -0,0 +1,459 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + * + * DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES + * OF THIS FILE. + * + * UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON + * THE NAUGHTY STEP. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * FOR A LONG TIME. + * + */ + + +/** + * Various functions for testing DeveloperToolbar. + * Parts of this code exist in: + * - browser/devtools/commandline/test/head.js + * - browser/devtools/shared/test/head.js + */ +let DeveloperToolbarTest = { }; + +/** + * Paranoid DeveloperToolbar.show(); + */ +DeveloperToolbarTest.show = function DTT_show(aCallback) { + if (DeveloperToolbar.visible) { + ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar"); + } + else { + DeveloperToolbar.show(true, aCallback); + } +}; + +/** + * Paranoid DeveloperToolbar.hide(); + */ +DeveloperToolbarTest.hide = function DTT_hide() { + if (!DeveloperToolbar.visible) { + ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar"); + } + else { + DeveloperToolbar.display.inputter.setInput(""); + DeveloperToolbar.hide(); + } +}; + +/** + * check() is the new status. Similar API except that it doesn't attempt to + * alter the display/requisition at all, and it makes extra checks. + * Test inputs + * typed: The text to type at the input + * Available checks: + * input: The text displayed in the input field + * cursor: The position of the start of the cursor + * status: One of "VALID", "ERROR", "INCOMPLETE" + * emptyParameters: Array of parameters still to type. e.g. [ "<message>" ] + * directTabText: Simple completion text + * arrowTabText: When the completion is not an extension (without arrow) + * markup: What state should the error markup be in. e.g. "VVVIIIEEE" + * args: Maps of checks to make against the arguments: + * value: i.e. assignment.value (which ignores defaultValue) + * type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned + * Care should be taken with this since it's something of an + * implementation detail + * arg: The toString value of the argument + * status: i.e. assignment.getStatus + * message: i.e. assignment.getMessage + * name: For commands - checks assignment.value.name + */ +DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) { + if (!checks.emptyParameters) { + checks.emptyParameters = []; + } + if (!checks.directTabText) { + checks.directTabText = ''; + } + if (!checks.arrowTabText) { + checks.arrowTabText = ''; + } + + var display = DeveloperToolbar.display; + + if (checks.typed) { + info('Starting tests for ' + checks.typed); + display.inputter.setInput(checks.typed); + } + else { + ok(false, "Missing typed for " + JSON.stringify(checks)); + return; + } + + if (checks.cursor) { + display.inputter.setCursor(checks.cursor) + } + + var cursor = checks.cursor ? checks.cursor.start : checks.typed.length; + + var requisition = display.requisition; + var completer = display.completer; + var actual = completer._getCompleterTemplateData(); + + /* + if (checks.input) { + is(display.inputter.element.value, + checks.input, + 'input'); + } + + if (checks.cursor) { + is(display.inputter.element.selectionStart, + checks.cursor, + 'cursor'); + } + */ + + if (checks.status) { + is(requisition.getStatus().toString(), + checks.status, + 'status'); + } + + if (checks.markup) { + var statusMarkup = requisition.getInputStatusMarkup(cursor); + var actualMarkup = statusMarkup.map(function(s) { + return Array(s.string.length + 1).join(s.status.toString()[0]); + }).join(''); + + is(checks.markup, + actualMarkup, + 'markup'); + } + + if (checks.emptyParameters) { + var actualParams = actual.emptyParameters; + is(actualParams.length, + checks.emptyParameters.length, + 'emptyParameters.length'); + + if (actualParams.length === checks.emptyParameters.length) { + for (var i = 0; i < actualParams.length; i++) { + is(actualParams[i].replace(/\u00a0/g, ' '), + checks.emptyParameters[i], + 'emptyParameters[' + i + ']'); + } + } + } + + if (checks.directTabText) { + is(actual.directTabText, + checks.directTabText, + 'directTabText'); + } + + if (checks.arrowTabText) { + is(actual.arrowTabText, + ' \u00a0\u21E5 ' + checks.arrowTabText, + 'arrowTabText'); + } + + if (checks.args) { + Object.keys(checks.args).forEach(function(paramName) { + var check = checks.args[paramName]; + + var assignment; + if (paramName === 'command') { + assignment = requisition.commandAssignment; + } + else { + assignment = requisition.getAssignment(paramName); + } + + if (assignment == null) { + ok(false, 'Unknown parameter: ' + paramName); + return; + } + + if (check.value) { + is(assignment.value, + check.value, + 'checkStatus value for ' + paramName); + } + + if (check.name) { + is(assignment.value.name, + check.name, + 'checkStatus name for ' + paramName); + } + + if (check.type) { + is(assignment.arg.type, + check.type, + 'checkStatus type for ' + paramName); + } + + if (check.arg) { + is(assignment.arg.toString(), + check.arg, + 'checkStatus arg for ' + paramName); + } + + if (check.status) { + is(assignment.getStatus().toString(), + check.status, + 'checkStatus status for ' + paramName); + } + + if (check.message) { + is(assignment.getMessage(), + check.message, + 'checkStatus message for ' + paramName); + } + }); + } +}; + +/** + * Execute a command: + * + * DeveloperToolbarTest.exec({ + * // Test inputs + * typed: "echo hi", // Optional, uses existing if undefined + * + * // Thing to check + * args: { message: "hi" }, // Check that the args were understood properly + * outputMatch: /^hi$/, // RegExp to test against textContent of output + * // (can also be array of RegExps) + * blankOutput: true, // Special checks when there is no output + * }); + */ +DeveloperToolbarTest.exec = function DTT_exec(tests) { + tests = tests || {}; + + if (tests.typed) { + DeveloperToolbar.display.inputter.setInput(tests.typed); + } + + let typed = DeveloperToolbar.display.inputter.getInputState().typed; + let output = DeveloperToolbar.display.requisition.exec(); + + is(typed, output.typed, 'output.command for: ' + typed); + + if (tests.completed !== false) { + ok(output.completed, 'output.completed false for: ' + typed); + } + else { + // It is actually an error if we say something is async and it turns + // out not to be? For now we're saying 'no' + // ok(!output.completed, 'output.completed true for: ' + typed); + } + + if (tests.args != null) { + is(Object.keys(tests.args).length, Object.keys(output.args).length, + 'arg count for ' + typed); + + Object.keys(output.args).forEach(function(arg) { + let expectedArg = tests.args[arg]; + let actualArg = output.args[arg]; + + if (typeof expectedArg === 'function') { + ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg); + } + else { + if (Array.isArray(expectedArg)) { + if (!Array.isArray(actualArg)) { + ok(false, 'actual is not an array. ' + typed + '/' + arg); + return; + } + + is(expectedArg.length, actualArg.length, + 'array length: ' + typed + '/' + arg); + for (let i = 0; i < expectedArg.length; i++) { + is(expectedArg[i], actualArg[i], + 'member: "' + typed + '/' + arg + '/' + i); + } + } + else { + is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg); + } + } + }); + } + + let displayed = DeveloperToolbar.outputPanel._div.textContent; + + if (tests.outputMatch) { + var doTest = function(match, against) { + if (!match.test(against)) { + ok(false, "html output for " + typed + " against " + match.source + + " (textContent sent to info)"); + info("Actual textContent"); + info(against); + } + } + if (Array.isArray(tests.outputMatch)) { + tests.outputMatch.forEach(function(match) { + doTest(match, displayed); + }); + } + else { + doTest(tests.outputMatch, displayed); + } + } + + if (tests.blankOutput != null) { + if (!/^$/.test(displayed)) { + ok(false, "html output for " + typed + " (textContent sent to info)"); + info("Actual textContent"); + info(displayed); + } + } +}; + +/** + * Quick wrapper around the things you need to do to run DeveloperToolbar + * command tests: + * - Set the pref 'devtools.toolbar.enabled' to true + * - Add a tab pointing at |uri| + * - Open the DeveloperToolbar + * - Register a cleanup function to undo the above + * - Run the tests + * + * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...' + * @param target Either a function or array of functions containing the tests + * to run. If an array of test function is passed then we will clear up after + * the tests have completed. If a single test function is passed then this + * function should arrange for 'finish()' to be called on completion. + */ +DeveloperToolbarTest.test = function DTT_test(uri, target) { + let menuItem = document.getElementById("menu_devToolbar"); + let command = document.getElementById("Tools:DevToolbar"); + let appMenuItem = document.getElementById("appmenu_devToolbar"); + + registerCleanupFunction(function() { + DeveloperToolbarTest.hide(); + + // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled"); + if (menuItem) { + menuItem.hidden = true; + } + if (command) { + command.setAttribute("disabled", "true"); + } + if (appMenuItem) { + appMenuItem.hidden = true; + } + + // leakHunt({ DeveloperToolbar: DeveloperToolbar }); + }); + + // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true); + if (menuItem) { + menuItem.hidden = false; + } + if (command) { + command.removeAttribute("disabled"); + } + if (appMenuItem) { + appMenuItem.hidden = false; + } + + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + content.location = uri; + + let tab = gBrowser.selectedTab; + let browser = gBrowser.getBrowserForTab(tab); + + var onTabLoad = function() { + browser.removeEventListener("load", onTabLoad, true); + + DeveloperToolbarTest.show(function() { + if (Array.isArray(target)) { + try { + target.forEach(function(func) { + func(browser, tab); + }) + } + finally { + DeveloperToolbarTest._checkFinish(); + } + } + else { + try { + target(browser, tab); + } + catch (ex) { + ok(false, "" + ex); + DeveloperToolbarTest._finish(); + throw ex; + } + } + }); + } + + browser.addEventListener("load", onTabLoad, true); +}; + +DeveloperToolbarTest._outstanding = []; + +DeveloperToolbarTest._checkFinish = function() { + if (DeveloperToolbarTest._outstanding.length == 0) { + DeveloperToolbarTest._finish(); + } +} + +DeveloperToolbarTest._finish = function() { + DeveloperToolbarTest.closeAllTabs(); + finish(); +} + +DeveloperToolbarTest.checkCalled = function(aFunc, aScope) { + var todo = function() { + var reply = aFunc.apply(aScope, arguments); + DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) { + return aJob != todo; + }); + DeveloperToolbarTest._checkFinish(); + return reply; + } + DeveloperToolbarTest._outstanding.push(todo); + return todo; +}; + +DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) { + return function() { + ok(false, aMsg); + return aFunc.apply(aScope, arguments); + } +}; + +/** + * + */ +DeveloperToolbarTest.closeAllTabs = function() { + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } +};
--- a/browser/devtools/shared/DeveloperToolbar.jsm +++ b/browser/devtools/shared/DeveloperToolbar.jsm @@ -8,25 +8,26 @@ const EXPORTED_SYMBOLS = [ "DeveloperToo const NS_XHTML = "http://www.w3.org/1999/xhtml"; const WEBCONSOLE_CONTENT_SCRIPT_URL = "chrome://browser/content/devtools/HUDService-content.js"; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource:///modules/devtools/Commands.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "console", "resource://gre/modules/devtools/Console.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "gcli", "resource:///modules/devtools/gcli.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "GcliCommands", - "resource:///modules/devtools/GcliCommands.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "CmdCommands", + "resource:///modules/devtools/CmdCmd.jsm"); /** * A component to manage the global developer toolbar, which contains a GCLI * and buttons for various developer tools. * @param aChromeWindow The browser window to which this toolbar is attached * @param aToolbarElement See browser.xul:<toolbar id="developer-toolbar"> */ function DeveloperToolbar(aChromeWindow, aToolbarElement) @@ -40,17 +41,17 @@ function DeveloperToolbar(aChromeWindow, this._lastState = NOTIFICATIONS.HIDE; this._pendingShowCallback = undefined; this._pendingHide = false; this._errorsCount = {}; this._webConsoleButton = this._doc .getElementById("developer-toolbar-webconsole"); try { - GcliCommands.refreshAutoCommands(aChromeWindow); + CmdCommands.refreshAutoCommands(aChromeWindow); } catch (ex) { console.error(ex); } } /** * Inspector notifications dispatched through the nsIObserverService @@ -551,32 +552,32 @@ function OutputPanel(aChromeDoc, aInput, /* <tooltip id="gcli-output" noautofocus="true" noautohide="true" class="gcli-panel"> <html:iframe xmlns:html="http://www.w3.org/1999/xhtml" id="gcli-output-frame" - src="chrome://browser/content/devtools/gclioutput.xhtml" + src="chrome://browser/content/devtools/commandlineoutput.xhtml" flex="1"/> </tooltip> */ // TODO: Switch back from tooltip to panel when metacity focus issue is fixed: // https://bugzilla.mozilla.org/show_bug.cgi?id=780102 this._panel = aChromeDoc.createElement("tooltip"); this._panel.id = "gcli-output"; this._panel.classList.add("gcli-panel"); this._toolbar.parentElement.insertBefore(this._panel, this._toolbar); this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe"); this._frame.id = "gcli-output-frame"; - this._frame.setAttribute("src", "chrome://browser/content/devtools/gclioutput.xhtml"); + this._frame.setAttribute("src", "chrome://browser/content/devtools/commandlineoutput.xhtml"); this._frame.setAttribute("flex", "1"); this._panel.appendChild(this._frame); this.displayedOutput = undefined; this._onload = this._onload.bind(this); this._frame.addEventListener("load", this._onload, true); @@ -762,32 +763,32 @@ function TooltipPanel(aChromeDoc, aInput /* <tooltip id="gcli-tooltip" type="arrow" noautofocus="true" noautohide="true" class="gcli-panel"> <html:iframe xmlns:html="http://www.w3.org/1999/xhtml" id="gcli-tooltip-frame" - src="chrome://browser/content/devtools/gclitooltip.xhtml" + src="chrome://browser/content/devtools/commandlinetooltip.xhtml" flex="1"/> </tooltip> */ // TODO: Switch back from tooltip to panel when metacity focus issue is fixed: // https://bugzilla.mozilla.org/show_bug.cgi?id=780102 this._panel = aChromeDoc.createElement("tooltip"); this._panel.id = "gcli-tooltip"; this._panel.classList.add("gcli-panel"); this._toolbar.parentElement.insertBefore(this._panel, this._toolbar); this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe"); this._frame.id = "gcli-tooltip-frame"; - this._frame.setAttribute("src", "chrome://browser/content/devtools/gclitooltip.xhtml"); + this._frame.setAttribute("src", "chrome://browser/content/devtools/commandlinetooltip.xhtml"); this._frame.setAttribute("flex", "1"); this._panel.appendChild(this._frame); this._frame.addEventListener("load", this._onload, true); this.loaded = false; this.canHide = false;
--- a/browser/devtools/shared/test/Makefile.in +++ b/browser/devtools/shared/test/Makefile.in @@ -16,16 +16,18 @@ MOCHITEST_BROWSER_FILES = \ browser_promise_basic.js \ browser_require_basic.js \ browser_templater_basic.js \ browser_toolbar_basic.js \ browser_toolbar_tooltip.js \ browser_toolbar_webconsole_errors_count.js \ browser_layoutHelpers.js \ head.js \ + helper.js \ + leakhunt.js \ $(NULL) MOCHITEST_BROWSER_FILES += \ browser_templater_basic.html \ browser_toolbar_basic.html \ browser_toolbar_webconsole_errors_count.html \ browser_layoutHelpers.html \ browser_layoutHelpers_iframe.html \
--- a/browser/devtools/shared/test/head.js +++ b/browser/devtools/shared/test/head.js @@ -3,16 +3,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ let console = (function() { let tempScope = {}; Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope); return tempScope.console; })(); +// Import the GCLI test helper +let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/")); +Services.scriptloader.loadSubScript(testDir + "/helper.js", this); + /** * Open a new tab at a URL and call a callback on load */ function addTab(aURL, aCallback) { waitForExplicitFinish(); gBrowser.selectedTab = gBrowser.addTab(); @@ -32,97 +36,16 @@ function addTab(aURL, aCallback) registerCleanupFunction(function tearDown() { while (gBrowser.tabs.length > 1) { gBrowser.removeCurrentTab(); } console = undefined; }); -/** - * Various functions for testing DeveloperToolbar. - * Parts of this code exist in: - * - browser/devtools/commandline/test/head.js - * - browser/devtools/shared/test/head.js - */ -let DeveloperToolbarTest = { - /** - * Paranoid DeveloperToolbar.show(); - */ - show: function DTT_show(aCallback) { - if (DeveloperToolbar.visible) { - ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar"); - } - else { - DeveloperToolbar.show(true, aCallback); - } - }, - - /** - * Paranoid DeveloperToolbar.hide(); - */ - hide: function DTT_hide() { - if (!DeveloperToolbar.visible) { - ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar"); - } - else { - DeveloperToolbar.display.inputter.setInput(""); - DeveloperToolbar.hide(); - } - }, - - /** - * Quick wrapper around the things you need to do to run DeveloperToolbar - * command tests: - * - Set the pref 'devtools.toolbar.enabled' to true - * - Add a tab pointing at |uri| - * - Open the DeveloperToolbar - * - Register a cleanup function to undo the above - * - Run the tests - * - * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...' - * @param testFunc A function containing the tests to run. This should - * arrange for 'finish()' to be called on completion. - */ - test: function DTT_test(uri, testFunc) { - let menuItem = document.getElementById("menu_devToolbar"); - let command = document.getElementById("Tools:DevToolbar"); - let appMenuItem = document.getElementById("appmenu_devToolbar"); - - registerCleanupFunction(function() { - DeveloperToolbarTest.hide(); - - // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled"); - if (menuItem) menuItem.hidden = true; - if (command) command.setAttribute("disabled", "true"); - if (appMenuItem) appMenuItem.hidden = true; - }); - - // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true); - if (menuItem) menuItem.hidden = false; - if (command) command.removeAttribute("disabled"); - if (appMenuItem) appMenuItem.hidden = false; - - addTab(uri, function(browser, tab) { - DeveloperToolbarTest.show(function() { - - try { - testFunc(browser, tab); - } - catch (ex) { - ok(false, "" + ex); - console.error(ex); - finish(); - throw ex; - } - }); - }); - }, -}; - function catchFail(func) { return function() { try { return func.apply(null, arguments); } catch (ex) { ok(false, ex); console.error(ex);
new file mode 100644 --- /dev/null +++ b/browser/devtools/shared/test/helper.js @@ -0,0 +1,459 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + * + * DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES + * OF THIS FILE. + * + * UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON + * THE NAUGHTY STEP. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * FOR A LONG TIME. + * + */ + + +/** + * Various functions for testing DeveloperToolbar. + * Parts of this code exist in: + * - browser/devtools/commandline/test/head.js + * - browser/devtools/shared/test/head.js + */ +let DeveloperToolbarTest = { }; + +/** + * Paranoid DeveloperToolbar.show(); + */ +DeveloperToolbarTest.show = function DTT_show(aCallback) { + if (DeveloperToolbar.visible) { + ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar"); + } + else { + DeveloperToolbar.show(true, aCallback); + } +}; + +/** + * Paranoid DeveloperToolbar.hide(); + */ +DeveloperToolbarTest.hide = function DTT_hide() { + if (!DeveloperToolbar.visible) { + ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar"); + } + else { + DeveloperToolbar.display.inputter.setInput(""); + DeveloperToolbar.hide(); + } +}; + +/** + * check() is the new status. Similar API except that it doesn't attempt to + * alter the display/requisition at all, and it makes extra checks. + * Test inputs + * typed: The text to type at the input + * Available checks: + * input: The text displayed in the input field + * cursor: The position of the start of the cursor + * status: One of "VALID", "ERROR", "INCOMPLETE" + * emptyParameters: Array of parameters still to type. e.g. [ "<message>" ] + * directTabText: Simple completion text + * arrowTabText: When the completion is not an extension (without arrow) + * markup: What state should the error markup be in. e.g. "VVVIIIEEE" + * args: Maps of checks to make against the arguments: + * value: i.e. assignment.value (which ignores defaultValue) + * type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned + * Care should be taken with this since it's something of an + * implementation detail + * arg: The toString value of the argument + * status: i.e. assignment.getStatus + * message: i.e. assignment.getMessage + * name: For commands - checks assignment.value.name + */ +DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) { + if (!checks.emptyParameters) { + checks.emptyParameters = []; + } + if (!checks.directTabText) { + checks.directTabText = ''; + } + if (!checks.arrowTabText) { + checks.arrowTabText = ''; + } + + var display = DeveloperToolbar.display; + + if (checks.typed) { + info('Starting tests for ' + checks.typed); + display.inputter.setInput(checks.typed); + } + else { + ok(false, "Missing typed for " + JSON.stringify(checks)); + return; + } + + if (checks.cursor) { + display.inputter.setCursor(checks.cursor) + } + + var cursor = checks.cursor ? checks.cursor.start : checks.typed.length; + + var requisition = display.requisition; + var completer = display.completer; + var actual = completer._getCompleterTemplateData(); + + /* + if (checks.input) { + is(display.inputter.element.value, + checks.input, + 'input'); + } + + if (checks.cursor) { + is(display.inputter.element.selectionStart, + checks.cursor, + 'cursor'); + } + */ + + if (checks.status) { + is(requisition.getStatus().toString(), + checks.status, + 'status'); + } + + if (checks.markup) { + var statusMarkup = requisition.getInputStatusMarkup(cursor); + var actualMarkup = statusMarkup.map(function(s) { + return Array(s.string.length + 1).join(s.status.toString()[0]); + }).join(''); + + is(checks.markup, + actualMarkup, + 'markup'); + } + + if (checks.emptyParameters) { + var actualParams = actual.emptyParameters; + is(actualParams.length, + checks.emptyParameters.length, + 'emptyParameters.length'); + + if (actualParams.length === checks.emptyParameters.length) { + for (var i = 0; i < actualParams.length; i++) { + is(actualParams[i].replace(/\u00a0/g, ' '), + checks.emptyParameters[i], + 'emptyParameters[' + i + ']'); + } + } + } + + if (checks.directTabText) { + is(actual.directTabText, + checks.directTabText, + 'directTabText'); + } + + if (checks.arrowTabText) { + is(actual.arrowTabText, + ' \u00a0\u21E5 ' + checks.arrowTabText, + 'arrowTabText'); + } + + if (checks.args) { + Object.keys(checks.args).forEach(function(paramName) { + var check = checks.args[paramName]; + + var assignment; + if (paramName === 'command') { + assignment = requisition.commandAssignment; + } + else { + assignment = requisition.getAssignment(paramName); + } + + if (assignment == null) { + ok(false, 'Unknown parameter: ' + paramName); + return; + } + + if (check.value) { + is(assignment.value, + check.value, + 'checkStatus value for ' + paramName); + } + + if (check.name) { + is(assignment.value.name, + check.name, + 'checkStatus name for ' + paramName); + } + + if (check.type) { + is(assignment.arg.type, + check.type, + 'checkStatus type for ' + paramName); + } + + if (check.arg) { + is(assignment.arg.toString(), + check.arg, + 'checkStatus arg for ' + paramName); + } + + if (check.status) { + is(assignment.getStatus().toString(), + check.status, + 'checkStatus status for ' + paramName); + } + + if (check.message) { + is(assignment.getMessage(), + check.message, + 'checkStatus message for ' + paramName); + } + }); + } +}; + +/** + * Execute a command: + * + * DeveloperToolbarTest.exec({ + * // Test inputs + * typed: "echo hi", // Optional, uses existing if undefined + * + * // Thing to check + * args: { message: "hi" }, // Check that the args were understood properly + * outputMatch: /^hi$/, // RegExp to test against textContent of output + * // (can also be array of RegExps) + * blankOutput: true, // Special checks when there is no output + * }); + */ +DeveloperToolbarTest.exec = function DTT_exec(tests) { + tests = tests || {}; + + if (tests.typed) { + DeveloperToolbar.display.inputter.setInput(tests.typed); + } + + let typed = DeveloperToolbar.display.inputter.getInputState().typed; + let output = DeveloperToolbar.display.requisition.exec(); + + is(typed, output.typed, 'output.command for: ' + typed); + + if (tests.completed !== false) { + ok(output.completed, 'output.completed false for: ' + typed); + } + else { + // It is actually an error if we say something is async and it turns + // out not to be? For now we're saying 'no' + // ok(!output.completed, 'output.completed true for: ' + typed); + } + + if (tests.args != null) { + is(Object.keys(tests.args).length, Object.keys(output.args).length, + 'arg count for ' + typed); + + Object.keys(output.args).forEach(function(arg) { + let expectedArg = tests.args[arg]; + let actualArg = output.args[arg]; + + if (typeof expectedArg === 'function') { + ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg); + } + else { + if (Array.isArray(expectedArg)) { + if (!Array.isArray(actualArg)) { + ok(false, 'actual is not an array. ' + typed + '/' + arg); + return; + } + + is(expectedArg.length, actualArg.length, + 'array length: ' + typed + '/' + arg); + for (let i = 0; i < expectedArg.length; i++) { + is(expectedArg[i], actualArg[i], + 'member: "' + typed + '/' + arg + '/' + i); + } + } + else { + is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg); + } + } + }); + } + + let displayed = DeveloperToolbar.outputPanel._div.textContent; + + if (tests.outputMatch) { + var doTest = function(match, against) { + if (!match.test(against)) { + ok(false, "html output for " + typed + " against " + match.source + + " (textContent sent to info)"); + info("Actual textContent"); + info(against); + } + } + if (Array.isArray(tests.outputMatch)) { + tests.outputMatch.forEach(function(match) { + doTest(match, displayed); + }); + } + else { + doTest(tests.outputMatch, displayed); + } + } + + if (tests.blankOutput != null) { + if (!/^$/.test(displayed)) { + ok(false, "html output for " + typed + " (textContent sent to info)"); + info("Actual textContent"); + info(displayed); + } + } +}; + +/** + * Quick wrapper around the things you need to do to run DeveloperToolbar + * command tests: + * - Set the pref 'devtools.toolbar.enabled' to true + * - Add a tab pointing at |uri| + * - Open the DeveloperToolbar + * - Register a cleanup function to undo the above + * - Run the tests + * + * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...' + * @param target Either a function or array of functions containing the tests + * to run. If an array of test function is passed then we will clear up after + * the tests have completed. If a single test function is passed then this + * function should arrange for 'finish()' to be called on completion. + */ +DeveloperToolbarTest.test = function DTT_test(uri, target) { + let menuItem = document.getElementById("menu_devToolbar"); + let command = document.getElementById("Tools:DevToolbar"); + let appMenuItem = document.getElementById("appmenu_devToolbar"); + + registerCleanupFunction(function() { + DeveloperToolbarTest.hide(); + + // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled"); + if (menuItem) { + menuItem.hidden = true; + } + if (command) { + command.setAttribute("disabled", "true"); + } + if (appMenuItem) { + appMenuItem.hidden = true; + } + + // leakHunt({ DeveloperToolbar: DeveloperToolbar }); + }); + + // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true); + if (menuItem) { + menuItem.hidden = false; + } + if (command) { + command.removeAttribute("disabled"); + } + if (appMenuItem) { + appMenuItem.hidden = false; + } + + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + content.location = uri; + + let tab = gBrowser.selectedTab; + let browser = gBrowser.getBrowserForTab(tab); + + var onTabLoad = function() { + browser.removeEventListener("load", onTabLoad, true); + + DeveloperToolbarTest.show(function() { + if (Array.isArray(target)) { + try { + target.forEach(function(func) { + func(browser, tab); + }) + } + finally { + DeveloperToolbarTest._checkFinish(); + } + } + else { + try { + target(browser, tab); + } + catch (ex) { + ok(false, "" + ex); + DeveloperToolbarTest._finish(); + throw ex; + } + } + }); + } + + browser.addEventListener("load", onTabLoad, true); +}; + +DeveloperToolbarTest._outstanding = []; + +DeveloperToolbarTest._checkFinish = function() { + if (DeveloperToolbarTest._outstanding.length == 0) { + DeveloperToolbarTest._finish(); + } +} + +DeveloperToolbarTest._finish = function() { + DeveloperToolbarTest.closeAllTabs(); + finish(); +} + +DeveloperToolbarTest.checkCalled = function(aFunc, aScope) { + var todo = function() { + var reply = aFunc.apply(aScope, arguments); + DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) { + return aJob != todo; + }); + DeveloperToolbarTest._checkFinish(); + return reply; + } + DeveloperToolbarTest._outstanding.push(todo); + return todo; +}; + +DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) { + return function() { + ok(false, aMsg); + return aFunc.apply(aScope, arguments); + } +}; + +/** + * + */ +DeveloperToolbarTest.closeAllTabs = function() { + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } +};
new file mode 100644 --- /dev/null +++ b/browser/devtools/shared/test/leakhunt.js @@ -0,0 +1,157 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Memory leak hunter. Walks a tree of objects looking for DOM nodes. + * Usage: + * leakHunt({ + * thing: thing, + * otherthing: otherthing + * }); + */ + +var noRecurse = [ + /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/, + /^Window$/, /^Document$/, + /^XULDocument$/, /^XULElement$/, + /^DOMWindow$/, /^HTMLDocument$/, /^HTML.*Element$/ +]; + +var hide = [ /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/ ]; + +function leakHunt(root, path, seen) { + path = path || []; + seen = seen || []; + + try { + var output = leakHuntInner(root, path, seen); + output.forEach(function(line) { + dump(line + '\n'); + }); + } + catch (ex) { + dump(ex + '\n'); + } +} + +function leakHuntInner(root, path, seen) { + var prefix = new Array(path.length).join(' '); + + var reply = []; + function log(msg) { + reply.push(msg); + } + + var direct + try { + direct = Object.keys(root); + } + catch (ex) { + log(prefix + ' Error enumerating: ' + ex); + return reply; + } + + for (var prop in root) { + var newPath = path.slice(); + newPath.push(prop); + prefix = new Array(newPath.length).join(' '); + + var data; + try { + data = root[prop]; + } + catch (ex) { + log(prefix + prop + ' Error reading: ' + ex); + continue; + } + + var recurse = true; + var message = getType(data); + + if (matchesAnyPattern(message, hide)) { + continue; + } + + if (message === 'function' && direct.indexOf(prop) == -1) { + continue; + } + + if (message === 'string') { + var extra = data.length > 10 ? data.substring(0, 9) + '_' : data; + message += ' "' + extra.replace(/\n/g, "|") + '"'; + recurse = false; + } + else if (matchesAnyPattern(message, noRecurse)) { + message += ' (no recurse)' + recurse = false; + } + else if (seen.indexOf(data) !== -1) { + message += ' (already seen)'; + recurse = false; + } + + if (recurse) { + seen.push(data); + var lines = leakHuntInner(data, newPath, seen); + if (lines.length == 0) { + if (message !== 'function') { + log(prefix + prop + ' = ' + message + ' { }'); + } + } + else { + log(prefix + prop + ' = ' + message + ' {'); + lines.forEach(function(line) { + reply.push(line); + }); + log(prefix + '}'); + } + } + else { + log(prefix + prop + ' = ' + message); + } + } + + return reply; +} + +function matchesAnyPattern(str, patterns) { + var match = false; + patterns.forEach(function(pattern) { + if (str.match(pattern)) { + match = true; + } + }); + return match; +} + +function getType(data) { + if (data === null) { + return 'null'; + } + if (data === undefined) { + return 'undefined'; + } + + var type = typeof data; + if (type === 'object' || type === 'Object') { + type = getCtorName(data); + } + + return type; +} + +function getCtorName(aObj) { + try { + if (aObj.constructor && aObj.constructor.name) { + return aObj.constructor.name; + } + } + catch (ex) { + return 'UnknownObject'; + } + + // If that fails, use Objects toString which sometimes gives something + // better than 'Object', and at least defaults to Object if nothing better + return Object.prototype.toString.call(aObj).slice(8, -1); +}
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleeditor/CmdEdit.jsm @@ -0,0 +1,46 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let EXPORTED_SYMBOLS = [ ]; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource:///modules/devtools/gcli.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "HUDService", + "resource:///modules/HUDService.jsm"); + +/** + * 'edit' command + */ +gcli.addCommand({ + name: "edit", + description: gcli.lookup("editDesc"), + manual: gcli.lookup("editManual2"), + params: [ + { + name: 'resource', + type: { + name: 'resource', + include: 'text/css' + }, + description: gcli.lookup("editResourceDesc") + }, + { + name: "line", + defaultValue: 1, + type: { + name: "number", + min: 1, + step: 10 + }, + description: gcli.lookup("editLineToJumpToDesc") + } + ], + exec: function(args, context) { + let win = HUDService.currentContext(); + win.StyleEditor.openChrome(args.resource.element, args.line); + } +});
--- a/browser/devtools/styleeditor/test/Makefile.in +++ b/browser/devtools/styleeditor/test/Makefile.in @@ -9,31 +9,37 @@ VPATH = @srcdir@ relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _BROWSER_TEST_FILES = \ browser_styleeditor_enabled.js \ browser_styleeditor_filesave.js \ + browser_styleeditor_cmd_edit.js \ + browser_styleeditor_cmd_edit.html \ browser_styleeditor_import.js \ browser_styleeditor_init.js \ browser_styleeditor_loading.js \ browser_styleeditor_new.js \ browser_styleeditor_passedinsheet.js \ browser_styleeditor_pretty.js \ browser_styleeditor_readonly.js \ browser_styleeditor_reopen.js \ browser_styleeditor_sv_keynav.js \ browser_styleeditor_sv_resize.js \ four.html \ head.js \ + helper.js \ media.html \ media-small.css \ minified.html \ + resources_inpage.jsi \ + resources_inpage1.css \ + resources_inpage2.css \ simple.css \ simple.css.gz \ simple.css.gz^headers^ \ simple.gz.html \ simple.html \ $(NULL) libs:: $(_BROWSER_TEST_FILES)
rename from browser/devtools/commandline/test/resources.html rename to browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.html --- a/browser/devtools/commandline/test/resources.html +++ b/browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.html @@ -11,17 +11,17 @@ var div = document.createElement('div'); div.id = 'divid'; div.classList.add('divclass'); div.appendChild(document.createTextNode('div')); div.setAttribute('data-a1', 'div'); pid.parentNode.appendChild(div); }); </script> - <script src="resources_inpage.js"></script> + <script src="resources_inpage.jsi"></script> <link rel="stylesheet" type="text/css" href="resources_inpage1.css"/> <link rel="stylesheet" type="text/css" href="resources_inpage2.css"/> <style type="text/css"> p { color: #800; } div { color: #008; } h4 { color: #080; } h3 { color: #880; } </style>
rename from browser/devtools/commandline/test/browser_gcli_edit.js rename to browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.js --- a/browser/devtools/commandline/test/browser_gcli_edit.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.js @@ -1,22 +1,20 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests that the edit command works -const TEST_URI = TEST_BASE_HTTP + "resources.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/styleeditor/" + + "test/browser_styleeditor_cmd_edit.html"; function test() { - DeveloperToolbarTest.test(TEST_URI, function(browser, tab) { - testEditStatus(browser, tab); - // Bug 759853 - // testEditExec(browser, tab); // calls finish() - finish(); - }); + DeveloperToolbarTest.test(TEST_URI, [ testEditStatus ]); + // Bug 759853 + // testEditExec } function testEditStatus(browser, tab) { DeveloperToolbarTest.checkInputStatus({ typed: "edit", markup: "VVVV", status: "ERROR", emptyParameters: [ " <resource>", " [line]" ], @@ -37,36 +35,36 @@ function testEditStatus(browser, tab) { directTabText: "ss#style2", emptyParameters: [ " [line]" ], }); DeveloperToolbarTest.checkInputStatus({ typed: "edit http", markup: "VVVVVIIII", status: "ERROR", - directTabText: "://example.com/browser/browser/devtools/commandline/test/resources_inpage1.css", + directTabText: "://example.com/browser/browser/devtools/styleeditor/test/resources_inpage1.css", arrowTabText: "", emptyParameters: [ " [line]" ], }); DeveloperToolbarTest.checkInputStatus({ typed: "edit page1", markup: "VVVVVIIIII", status: "ERROR", directTabText: "", - arrowTabText: "http://example.com/browser/browser/devtools/commandline/test/resources_inpage1.css", + arrowTabText: "http://example.com/browser/browser/devtools/styleeditor/test/resources_inpage1.css", emptyParameters: [ " [line]" ], }); DeveloperToolbarTest.checkInputStatus({ typed: "edit page2", markup: "VVVVVIIIII", status: "ERROR", directTabText: "", - arrowTabText: "http://example.com/browser/browser/devtools/commandline/test/resources_inpage2.css", + arrowTabText: "http://example.com/browser/browser/devtools/styleeditor/test/resources_inpage2.css", emptyParameters: [ " [line]" ], }); DeveloperToolbarTest.checkInputStatus({ typed: "edit stylez", markup: "VVVVVEEEEEE", status: "ERROR", directTabText: "",
--- a/browser/devtools/styleeditor/test/head.js +++ b/browser/devtools/styleeditor/test/head.js @@ -1,18 +1,21 @@ -/* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ const TEST_BASE = "chrome://mochitests/content/browser/browser/devtools/styleeditor/test/"; const TEST_BASE_HTTP = "http://example.com/browser/browser/devtools/styleeditor/test/"; const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/styleeditor/test/"; let gChromeWindow; //StyleEditorChrome window +// Import the GCLI test helper +let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/")); +Services.scriptloader.loadSubScript(testDir + "/helper.js", this); + function cleanup() { if (gChromeWindow) { gChromeWindow.close(); gChromeWindow = null; } while (gBrowser.tabs.length > 1) { gBrowser.removeCurrentTab();
new file mode 100644 --- /dev/null +++ b/browser/devtools/styleeditor/test/helper.js @@ -0,0 +1,459 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + * + * DO NOT ALTER THIS FILE WITHOUT KEEPING IT IN SYNC WITH THE OTHER COPIES + * OF THIS FILE. + * + * UNAUTHORIZED ALTERATION WILL RESULT IN THE ALTEREE BEING SENT TO SIT ON + * THE NAUGHTY STEP. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * FOR A LONG TIME. + * + */ + + +/** + * Various functions for testing DeveloperToolbar. + * Parts of this code exist in: + * - browser/devtools/commandline/test/head.js + * - browser/devtools/shared/test/head.js + */ +let DeveloperToolbarTest = { }; + +/** + * Paranoid DeveloperToolbar.show(); + */ +DeveloperToolbarTest.show = function DTT_show(aCallback) { + if (DeveloperToolbar.visible) { + ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar"); + } + else { + DeveloperToolbar.show(true, aCallback); + } +}; + +/** + * Paranoid DeveloperToolbar.hide(); + */ +DeveloperToolbarTest.hide = function DTT_hide() { + if (!DeveloperToolbar.visible) { + ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar"); + } + else { + DeveloperToolbar.display.inputter.setInput(""); + DeveloperToolbar.hide(); + } +}; + +/** + * check() is the new status. Similar API except that it doesn't attempt to + * alter the display/requisition at all, and it makes extra checks. + * Test inputs + * typed: The text to type at the input + * Available checks: + * input: The text displayed in the input field + * cursor: The position of the start of the cursor + * status: One of "VALID", "ERROR", "INCOMPLETE" + * emptyParameters: Array of parameters still to type. e.g. [ "<message>" ] + * directTabText: Simple completion text + * arrowTabText: When the completion is not an extension (without arrow) + * markup: What state should the error markup be in. e.g. "VVVIIIEEE" + * args: Maps of checks to make against the arguments: + * value: i.e. assignment.value (which ignores defaultValue) + * type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned + * Care should be taken with this since it's something of an + * implementation detail + * arg: The toString value of the argument + * status: i.e. assignment.getStatus + * message: i.e. assignment.getMessage + * name: For commands - checks assignment.value.name + */ +DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) { + if (!checks.emptyParameters) { + checks.emptyParameters = []; + } + if (!checks.directTabText) { + checks.directTabText = ''; + } + if (!checks.arrowTabText) { + checks.arrowTabText = ''; + } + + var display = DeveloperToolbar.display; + + if (checks.typed) { + info('Starting tests for ' + checks.typed); + display.inputter.setInput(checks.typed); + } + else { + ok(false, "Missing typed for " + JSON.stringify(checks)); + return; + } + + if (checks.cursor) { + display.inputter.setCursor(checks.cursor) + } + + var cursor = checks.cursor ? checks.cursor.start : checks.typed.length; + + var requisition = display.requisition; + var completer = display.completer; + var actual = completer._getCompleterTemplateData(); + + /* + if (checks.input) { + is(display.inputter.element.value, + checks.input, + 'input'); + } + + if (checks.cursor) { + is(display.inputter.element.selectionStart, + checks.cursor, + 'cursor'); + } + */ + + if (checks.status) { + is(requisition.getStatus().toString(), + checks.status, + 'status'); + } + + if (checks.markup) { + var statusMarkup = requisition.getInputStatusMarkup(cursor); + var actualMarkup = statusMarkup.map(function(s) { + return Array(s.string.length + 1).join(s.status.toString()[0]); + }).join(''); + + is(checks.markup, + actualMarkup, + 'markup'); + } + + if (checks.emptyParameters) { + var actualParams = actual.emptyParameters; + is(actualParams.length, + checks.emptyParameters.length, + 'emptyParameters.length'); + + if (actualParams.length === checks.emptyParameters.length) { + for (var i = 0; i < actualParams.length; i++) { + is(actualParams[i].replace(/\u00a0/g, ' '), + checks.emptyParameters[i], + 'emptyParameters[' + i + ']'); + } + } + } + + if (checks.directTabText) { + is(actual.directTabText, + checks.directTabText, + 'directTabText'); + } + + if (checks.arrowTabText) { + is(actual.arrowTabText, + ' \u00a0\u21E5 ' + checks.arrowTabText, + 'arrowTabText'); + } + + if (checks.args) { + Object.keys(checks.args).forEach(function(paramName) { + var check = checks.args[paramName]; + + var assignment; + if (paramName === 'command') { + assignment = requisition.commandAssignment; + } + else { + assignment = requisition.getAssignment(paramName); + } + + if (assignment == null) { + ok(false, 'Unknown parameter: ' + paramName); + return; + } + + if (check.value) { + is(assignment.value, + check.value, + 'checkStatus value for ' + paramName); + } + + if (check.name) { + is(assignment.value.name, + check.name, + 'checkStatus name for ' + paramName); + } + + if (check.type) { + is(assignment.arg.type, + check.type, + 'checkStatus type for ' + paramName); + } + + if (check.arg) { + is(assignment.arg.toString(), + check.arg, + 'checkStatus arg for ' + paramName); + } + + if (check.status) { + is(assignment.getStatus().toString(), + check.status, + 'checkStatus status for ' + paramName); + } + + if (check.message) { + is(assignment.getMessage(), + check.message, + 'checkStatus message for ' + paramName); + } + }); + } +}; + +/** + * Execute a command: + * + * DeveloperToolbarTest.exec({ + * // Test inputs + * typed: "echo hi", // Optional, uses existing if undefined + * + * // Thing to check + * args: { message: "hi" }, // Check that the args were understood properly + * outputMatch: /^hi$/, // RegExp to test against textContent of output + * // (can also be array of RegExps) + * blankOutput: true, // Special checks when there is no output + * }); + */ +DeveloperToolbarTest.exec = function DTT_exec(tests) { + tests = tests || {}; + + if (tests.typed) { + DeveloperToolbar.display.inputter.setInput(tests.typed); + } + + let typed = DeveloperToolbar.display.inputter.getInputState().typed; + let output = DeveloperToolbar.display.requisition.exec(); + + is(typed, output.typed, 'output.command for: ' + typed); + + if (tests.completed !== false) { + ok(output.completed, 'output.completed false for: ' + typed); + } + else { + // It is actually an error if we say something is async and it turns + // out not to be? For now we're saying 'no' + // ok(!output.completed, 'output.completed true for: ' + typed); + } + + if (tests.args != null) { + is(Object.keys(tests.args).length, Object.keys(output.args).length, + 'arg count for ' + typed); + + Object.keys(output.args).forEach(function(arg) { + let expectedArg = tests.args[arg]; + let actualArg = output.args[arg]; + + if (typeof expectedArg === 'function') { + ok(expectedArg(actualArg), 'failed test func. ' + typed + '/' + arg); + } + else { + if (Array.isArray(expectedArg)) { + if (!Array.isArray(actualArg)) { + ok(false, 'actual is not an array. ' + typed + '/' + arg); + return; + } + + is(expectedArg.length, actualArg.length, + 'array length: ' + typed + '/' + arg); + for (let i = 0; i < expectedArg.length; i++) { + is(expectedArg[i], actualArg[i], + 'member: "' + typed + '/' + arg + '/' + i); + } + } + else { + is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg); + } + } + }); + } + + let displayed = DeveloperToolbar.outputPanel._div.textContent; + + if (tests.outputMatch) { + var doTest = function(match, against) { + if (!match.test(against)) { + ok(false, "html output for " + typed + " against " + match.source + + " (textContent sent to info)"); + info("Actual textContent"); + info(against); + } + } + if (Array.isArray(tests.outputMatch)) { + tests.outputMatch.forEach(function(match) { + doTest(match, displayed); + }); + } + else { + doTest(tests.outputMatch, displayed); + } + } + + if (tests.blankOutput != null) { + if (!/^$/.test(displayed)) { + ok(false, "html output for " + typed + " (textContent sent to info)"); + info("Actual textContent"); + info(displayed); + } + } +}; + +/** + * Quick wrapper around the things you need to do to run DeveloperToolbar + * command tests: + * - Set the pref 'devtools.toolbar.enabled' to true + * - Add a tab pointing at |uri| + * - Open the DeveloperToolbar + * - Register a cleanup function to undo the above + * - Run the tests + * + * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...' + * @param target Either a function or array of functions containing the tests + * to run. If an array of test function is passed then we will clear up after + * the tests have completed. If a single test function is passed then this + * function should arrange for 'finish()' to be called on completion. + */ +DeveloperToolbarTest.test = function DTT_test(uri, target) { + let menuItem = document.getElementById("menu_devToolbar"); + let command = document.getElementById("Tools:DevToolbar"); + let appMenuItem = document.getElementById("appmenu_devToolbar"); + + registerCleanupFunction(function() { + DeveloperToolbarTest.hide(); + + // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled"); + if (menuItem) { + menuItem.hidden = true; + } + if (command) { + command.setAttribute("disabled", "true"); + } + if (appMenuItem) { + appMenuItem.hidden = true; + } + + // leakHunt({ DeveloperToolbar: DeveloperToolbar }); + }); + + // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true); + if (menuItem) { + menuItem.hidden = false; + } + if (command) { + command.removeAttribute("disabled"); + } + if (appMenuItem) { + appMenuItem.hidden = false; + } + + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + content.location = uri; + + let tab = gBrowser.selectedTab; + let browser = gBrowser.getBrowserForTab(tab); + + var onTabLoad = function() { + browser.removeEventListener("load", onTabLoad, true); + + DeveloperToolbarTest.show(function() { + if (Array.isArray(target)) { + try { + target.forEach(function(func) { + func(browser, tab); + }) + } + finally { + DeveloperToolbarTest._checkFinish(); + } + } + else { + try { + target(browser, tab); + } + catch (ex) { + ok(false, "" + ex); + DeveloperToolbarTest._finish(); + throw ex; + } + } + }); + } + + browser.addEventListener("load", onTabLoad, true); +}; + +DeveloperToolbarTest._outstanding = []; + +DeveloperToolbarTest._checkFinish = function() { + if (DeveloperToolbarTest._outstanding.length == 0) { + DeveloperToolbarTest._finish(); + } +} + +DeveloperToolbarTest._finish = function() { + DeveloperToolbarTest.closeAllTabs(); + finish(); +} + +DeveloperToolbarTest.checkCalled = function(aFunc, aScope) { + var todo = function() { + var reply = aFunc.apply(aScope, arguments); + DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) { + return aJob != todo; + }); + DeveloperToolbarTest._checkFinish(); + return reply; + } + DeveloperToolbarTest._outstanding.push(todo); + return todo; +}; + +DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) { + return function() { + ok(false, aMsg); + return aFunc.apply(aScope, arguments); + } +}; + +/** + * + */ +DeveloperToolbarTest.closeAllTabs = function() { + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } +};
rename from browser/devtools/commandline/test/resources_inpage.js rename to browser/devtools/styleeditor/test/resources_inpage.jsi --- a/browser/devtools/commandline/test/resources_inpage.js +++ b/browser/devtools/styleeditor/test/resources_inpage.jsi @@ -1,10 +1,10 @@ -// This script is used from within browser_gcli_edit.html +// This script is used from within browser_styleeditor_cmd_edit.html window.addEventListener('load', function() { var pid = document.getElementById('pid'); var h3 = document.createElement('h3'); h3.id = 'h3id'; h3.classList.add('h3class'); h3.appendChild(document.createTextNode('h3')); h3.setAttribute('data-a1', 'h3');
rename from browser/devtools/commandline/test/resources_inpage1.css rename to browser/devtools/styleeditor/test/resources_inpage1.css
rename from browser/devtools/commandline/test/resources_inpage2.css rename to browser/devtools/styleeditor/test/resources_inpage2.css
rename from browser/devtools/commandline/GcliTiltCommands.jsm rename to browser/devtools/tilt/CmdTilt.jsm
rename from browser/themes/gnomestripe/devtools/gcli.css rename to browser/themes/gnomestripe/devtools/commandline.css
--- a/browser/themes/gnomestripe/jar.mn +++ b/browser/themes/gnomestripe/jar.mn @@ -102,17 +102,17 @@ browser.jar: skin/classic/browser/devtools/arrows.png (devtools/arrows.png) skin/classic/browser/devtools/commandline.png (devtools/commandline.png) skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png) skin/classic/browser/devtools/goto-mdn.png (devtools/goto-mdn.png) skin/classic/browser/devtools/csshtmltree.css (devtools/csshtmltree.css) skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css) skin/classic/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css) skin/classic/browser/devtools/webconsole.png (devtools/webconsole.png) - skin/classic/browser/devtools/gcli.css (devtools/gcli.css) + skin/classic/browser/devtools/commandline.css (devtools/commandline.css) skin/classic/browser/devtools/htmlpanel.css (devtools/htmlpanel.css) skin/classic/browser/devtools/markup-view.css (devtools/markup-view.css) skin/classic/browser/devtools/orion.css (devtools/orion.css) skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css) skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png) skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png) skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png) skin/classic/browser/devtools/breadcrumbs-scrollbutton.png (devtools/breadcrumbs-scrollbutton.png)
rename from browser/themes/pinstripe/devtools/gcli.css rename to browser/themes/pinstripe/devtools/commandline.css
--- a/browser/themes/pinstripe/jar.mn +++ b/browser/themes/pinstripe/jar.mn @@ -138,17 +138,17 @@ browser.jar: skin/classic/browser/tabview/tabview.png (tabview/tabview.png) skin/classic/browser/tabview/tabview.css (tabview/tabview.css) * skin/classic/browser/devtools/common.css (devtools/common.css) skin/classic/browser/devtools/arrows.png (devtools/arrows.png) skin/classic/browser/devtools/commandline.png (devtools/commandline.png) skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png) skin/classic/browser/devtools/goto-mdn.png (devtools/goto-mdn.png) skin/classic/browser/devtools/csshtmltree.css (devtools/csshtmltree.css) - skin/classic/browser/devtools/gcli.css (devtools/gcli.css) + skin/classic/browser/devtools/commandline.css (devtools/commandline.css) skin/classic/browser/devtools/htmlpanel.css (devtools/htmlpanel.css) skin/classic/browser/devtools/markup-view.css (devtools/markup-view.css) skin/classic/browser/devtools/orion.css (devtools/orion.css) skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css) skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png) skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png) skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png) skin/classic/browser/devtools/toolbarbutton-close.png (devtools/toolbarbutton-close.png)
rename from browser/themes/winstripe/devtools/gcli.css rename to browser/themes/winstripe/devtools/commandline.css
--- a/browser/themes/winstripe/jar.mn +++ b/browser/themes/winstripe/jar.mn @@ -126,17 +126,17 @@ browser.jar: skin/classic/browser/tabview/tabview-inverted.png (tabview/tabview-inverted.png) skin/classic/browser/tabview/tabview.css (tabview/tabview.css) skin/classic/browser/devtools/common.css (devtools/common.css) skin/classic/browser/devtools/arrows.png (devtools/arrows.png) skin/classic/browser/devtools/commandline.png (devtools/commandline.png) skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png) skin/classic/browser/devtools/goto-mdn.png (devtools/goto-mdn.png) skin/classic/browser/devtools/csshtmltree.css (devtools/csshtmltree.css) - skin/classic/browser/devtools/gcli.css (devtools/gcli.css) + skin/classic/browser/devtools/commandline.css (devtools/commandline.css) skin/classic/browser/devtools/htmlpanel.css (devtools/htmlpanel.css) skin/classic/browser/devtools/markup-view.css (devtools/markup-view.css) skin/classic/browser/devtools/orion.css (devtools/orion.css) skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css) skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png) skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png) skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png) skin/classic/browser/devtools/toolbarbutton-close.png (devtools/toolbarbutton-close.png) @@ -329,17 +329,17 @@ browser.jar: skin/classic/aero/browser/tabview/tabview-inverted.png (tabview/tabview-inverted.png) skin/classic/aero/browser/tabview/tabview.css (tabview/tabview.css) skin/classic/aero/browser/devtools/common.css (devtools/common.css) skin/classic/aero/browser/devtools/arrows.png (devtools/arrows.png) skin/classic/aero/browser/devtools/commandline.png (devtools/commandline.png) skin/classic/aero/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png) skin/classic/aero/browser/devtools/goto-mdn.png (devtools/goto-mdn.png) skin/classic/aero/browser/devtools/csshtmltree.css (devtools/csshtmltree.css) - skin/classic/aero/browser/devtools/gcli.css (devtools/gcli.css) + skin/classic/aero/browser/devtools/commandline.css (devtools/commandline.css) skin/classic/aero/browser/devtools/htmlpanel.css (devtools/htmlpanel.css) skin/classic/aero/browser/devtools/markup-view.css (devtools/markup-view.css) skin/classic/aero/browser/devtools/orion.css (devtools/orion.css) skin/classic/aero/browser/devtools/orion-container.css (devtools/orion-container.css) skin/classic/aero/browser/devtools/orion-task.png (devtools/orion-task.png) skin/classic/aero/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png) skin/classic/aero/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png) skin/classic/aero/browser/devtools/toolbarbutton-close.png (devtools/toolbarbutton-close.png)