Bug 776875 - GCLI: Move existing GCLI commands into JSMs; r=jwalker

--HG--
rename : browser/devtools/commandline/GcliCookieCommands.jsm => browser/devtools/commandline/CmdCookie.jsm
rename : browser/devtools/commandline/GcliCommands.jsm => browser/devtools/commandline/Commands.jsm
rename : browser/devtools/commandline/gcli.css => browser/devtools/commandline/commandline.css
rename : browser/devtools/commandline/gclioutput.xhtml => browser/devtools/commandline/commandlineoutput.xhtml
rename : browser/devtools/commandline/gclitooltip.xhtml => browser/devtools/commandline/commandlinetooltip.xhtml
rename : browser/devtools/commandline/test/browser_gcli_addon.js => browser/devtools/commandline/test/browser_cmd_addon.js
rename : browser/devtools/commandline/test/browser_gcli_calllog.js => browser/devtools/commandline/test/browser_cmd_calllog.js
rename : browser/devtools/commandline/test/browser_gcli_commands.js => browser/devtools/commandline/test/browser_cmd_commands.js
rename : browser/devtools/commandline/test/browser_gcli_cookie.js => browser/devtools/commandline/test/browser_cmd_cookie.js
rename : browser/devtools/commandline/test/browser_gcli_integrate.js => browser/devtools/commandline/test/browser_cmd_integrate.js
rename : browser/devtools/commandline/test/browser_gcli_jsb.js => browser/devtools/commandline/test/browser_cmd_jsb.js
rename : browser/devtools/commandline/test/resources_jsb_script.js => browser/devtools/commandline/test/browser_cmd_jsb_script.jsi
rename : browser/devtools/commandline/test/browser_gcli_inspect.html => browser/devtools/commandline/test/browser_cmd_pagemod_export.html
rename : browser/devtools/commandline/test/browser_gcli_pagemod_export.js => browser/devtools/commandline/test/browser_cmd_pagemod_export.js
rename : browser/devtools/commandline/test/browser_gcli_pref.js => browser/devtools/commandline/test/browser_cmd_pref.js
rename : browser/devtools/commandline/test/browser_gcli_restart.js => browser/devtools/commandline/test/browser_cmd_restart.js
rename : browser/devtools/commandline/test/browser_gcli_settings.js => browser/devtools/commandline/test/browser_cmd_settings.js
rename : browser/devtools/commandline/test/resources_dbg.html => browser/devtools/commandline/test/browser_dbg_cmd.html
rename : browser/devtools/commandline/test/browser_gcli_dbg.js => browser/devtools/commandline/test/browser_dbg_cmd.js
rename : browser/devtools/commandline/test/browser_gcli_break.html => browser/devtools/commandline/test/browser_dbg_cmd_break.html
rename : browser/devtools/commandline/test/browser_gcli_break.js => browser/devtools/commandline/test/browser_dbg_cmd_break.js
rename : browser/devtools/commandline/test/browser_gcli_inspect.html => browser/devtools/highlighter/test/browser_inspector_cmd_inspect.html
rename : browser/devtools/commandline/test/browser_gcli_inspect.js => browser/devtools/highlighter/test/browser_inspector_cmd_inspect.js
rename : browser/devtools/commandline/test/browser_gcli_responsivemode.js => browser/devtools/responsivedesign/test/browser_responsive_cmd.js
rename : browser/devtools/commandline/test/resources.html => browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.html
rename : browser/devtools/commandline/test/browser_gcli_edit.js => browser/devtools/styleeditor/test/browser_styleeditor_cmd_edit.js
rename : browser/devtools/commandline/test/resources_inpage.js => browser/devtools/styleeditor/test/resources_inpage.jsi
rename : browser/devtools/commandline/test/resources_inpage1.css => browser/devtools/styleeditor/test/resources_inpage1.css
rename : browser/devtools/commandline/test/resources_inpage2.css => browser/devtools/styleeditor/test/resources_inpage2.css
rename : browser/devtools/commandline/GcliTiltCommands.jsm => browser/devtools/tilt/CmdTilt.jsm
rename : browser/themes/gnomestripe/devtools/gcli.css => browser/themes/gnomestripe/devtools/commandline.css
rename : browser/themes/pinstripe/devtools/gcli.css => browser/themes/pinstripe/devtools/commandline.css
rename : browser/themes/winstripe/devtools/gcli.css => browser/themes/winstripe/devtools/commandline.css
This commit is contained in:
Michael Ratcliffe 2012-08-14 15:51:48 +01:00
parent bc1bb4ec39
commit b6fe076c28
75 changed files with 4564 additions and 2486 deletions

View File

@ -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);
});

View File

@ -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;
}
});

View File

@ -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 ]);
}
});

View File

@ -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);
}
});

View File

@ -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);
}
});

View File

@ -2,11 +2,15 @@
* 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

View File

@ -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();
}
}
}
});

View File

@ -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;
}
});

View File

@ -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));
}
});

View File

@ -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;
}
});

View File

@ -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, "\\$&");
}

View File

@ -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");
}
});

View File

@ -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;
}
});

View File

@ -0,0 +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 = [ ];
const Cu = Components.utils;
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");

File diff suppressed because it is too large Load Diff

View File

@ -9,8 +9,8 @@
<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>

View File

@ -9,8 +9,8 @@
<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>

View File

@ -12,35 +12,28 @@ 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

View File

@ -0,0 +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", [ 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 });
});

View File

@ -9,11 +9,7 @@ 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() {

View File

@ -9,13 +9,7 @@ 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() {
@ -28,7 +22,7 @@ function testEcho() {
*/
}
function testConsole(tab) {
function testConsole(browser, tab) {
let hud = null;
function onWebConsoleOpen(aSubject) {
Services.obs.removeObserver(onWebConsoleOpen, "web-console-created");
@ -70,6 +64,5 @@ function testConsole(tab) {
ok(!(hud.hudId in imported.HUDService.hudReferences), "console closed");
imported = undefined;
finish();
}
}

View File

@ -6,10 +6,7 @@
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() {

View File

@ -0,0 +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() {
DeveloperToolbarTest.test("about:blank", [ /*GJT_test*/ ]);
}
function GJT_test() {
DeveloperToolbarTest.exec({
typed: "jsb AAA",
outputMatch: /valid/
});
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");
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 });
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");
}
}

View File

@ -3,20 +3,23 @@
// 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({

View File

@ -22,19 +22,16 @@ imports.XPCOMUtils.defineLazyGetter(imports, "supportsString", function() {
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;

View File

@ -6,10 +6,7 @@
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() {

View File

@ -22,14 +22,7 @@ imports.XPCOMUtils.defineLazyGetter(imports, "supportsString", function() {
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;

View File

@ -0,0 +1,72 @@
function test() {
const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
"test/browser_dbg_cmd.html";
DeveloperToolbarTest.test(TEST_URI, function() {
testDbgCmd();
});
}
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() {
info("Starting tests.");
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 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");
cmd("dbg continue", function() {
cmd("dbg continue", function() {
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
});
});
});
function cmd(aTyped, aCallback) {
pane.contentWindow.gClient.addOneTimeListener("paused", aCallback);
DeveloperToolbarTest.exec({
typed: aTyped,
blankOutput: true
});
}
});
}

View File

@ -3,15 +3,23 @@
// 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",
@ -35,13 +43,16 @@ function testBreakCommands() {
});
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"
@ -61,7 +72,7 @@ function testBreakCommands() {
});
DeveloperToolbarTest.exec();
client.activeThread.resume(function() {
var cleanup = DeveloperToolbarTest.checkCalled(function() {
DeveloperToolbarTest.checkInputStatus({
typed: "break del 0",
status: "VALID"
@ -70,13 +81,19 @@ function testBreakCommands() {
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);
}

View File

@ -1,43 +0,0 @@
function test() {
DeveloperToolbarTest.test("about:blank", function GAT_test() {
function GAT_ready() {
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);
});
}

View File

@ -1,67 +0,0 @@
function test() {
const TEST_URI = TEST_BASE_HTTP + "resources_dbg.html";
DeveloperToolbarTest.test(TEST_URI, function GAT_test() {
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() {
info("Starting tests.");
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 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");
cmd("dbg continue", function() {
cmd("dbg continue", function() {
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
});
});
});
function cmd(aTyped, aCallback) {
pane.contentWindow.gClient.addOneTimeListener("paused", aCallback);
DeveloperToolbarTest.exec({
typed: aTyped,
blankOutput: true
});
}
});
});
}

View File

@ -1,48 +0,0 @@
function test() {
const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
"test/resources_jsb_script.js";
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/
});
gBrowser.addTabsProgressListener({
onProgressChange: 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");
checkResult(result);
finish();
}
});
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();
});
}

View File

@ -1,58 +0,0 @@
/* 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.checkInputStatus({
typed: "resize toggle",
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 off",
status: "VALID"
});
DeveloperToolbarTest.exec();
ok(isClosed(), "responsive mode is closed");
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");
executeSoon(finish);
});
function isOpen() {
return !!gBrowser.selectedTab.__responsiveUI;
}
function isClosed() {
return !isOpen();
}
}

View File

@ -11,6 +11,10 @@ let console = (function() {
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
*/
@ -39,518 +43,3 @@ registerCleanupFunction(function tearDown() {
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);
}

View File

@ -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();
}
};

View File

@ -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);
}
});

View File

@ -24,3 +24,6 @@ EXTRA_PP_JS_MODULES = \
TEST_DIRS += test
include $(topsrcdir)/config/rules.mk
libs::
$(NSINSTALL) $(srcdir)/CmdInspect.jsm $(FINAL_TARGET)/modules/devtools

View File

@ -37,7 +37,10 @@ _BROWSER_FILES = \
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)

View File

@ -0,0 +1,25 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>GCLI inspect command test</title>
</head>
<body>
<!-- This is a list of 0 h1 elements -->
<!-- This is a list of 1 div elements -->
<div>Hello, I'm a div</div>
<!-- This is a list of 2 span elements -->
<span>Hello, I'm a span</span>
<span>And me</span>
<!-- This is a collection of various things that match only once -->
<p class="someclass">.someclass</p>
<p id="someid">#someid</p>
<button disabled>button[disabled]</button>
<p><strong>p&gt;strong</strong></p>
</body>
</html>

View File

@ -3,14 +3,11 @@
// 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() {

View File

@ -7,6 +7,10 @@ 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()
{

View File

@ -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();
}
};

View File

@ -26,6 +26,6 @@ browser.jar:
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)

View File

@ -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);
}

View File

@ -47,7 +47,10 @@ 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)

View File

@ -0,0 +1,60 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
DeveloperToolbarTest.test("about:blank", [ GAT_test ]);
}
function isOpen() {
return !!gBrowser.selectedTab.__responsiveUI;
}
function isClosed() {
return !isOpen();
}
function GAT_test() {
DeveloperToolbarTest.checkInputStatus({
typed: "resize toggle",
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 on",
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 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");
// executeSoon(finish);
}

View File

@ -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);

View File

@ -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();
}
};

View File

@ -13,6 +13,7 @@ const WEBCONSOLE_CONTENT_SCRIPT_URL =
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");
@ -20,8 +21,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "console",
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
@ -45,7 +46,7 @@ function DeveloperToolbar(aChromeWindow, aToolbarElement)
.getElementById("developer-toolbar-webconsole");
try {
GcliCommands.refreshAutoCommands(aChromeWindow);
CmdCommands.refreshAutoCommands(aChromeWindow);
}
catch (ex) {
console.error(ex);
@ -556,7 +557,7 @@ function OutputPanel(aChromeDoc, aInput, aLoadCallback)
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>
*/
@ -571,7 +572,7 @@ function OutputPanel(aChromeDoc, aInput, aLoadCallback)
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);
@ -767,7 +768,7 @@ function TooltipPanel(aChromeDoc, aInput, aLoadCallback)
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>
*/
@ -782,7 +783,7 @@ function TooltipPanel(aChromeDoc, aInput, aLoadCallback)
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);

View File

@ -21,6 +21,8 @@ MOCHITEST_BROWSER_FILES = \
browser_toolbar_webconsole_errors_count.js \
browser_layoutHelpers.js \
head.js \
helper.js \
leakhunt.js \
$(NULL)
MOCHITEST_BROWSER_FILES += \

View File

@ -8,6 +8,10 @@ let console = (function() {
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
*/
@ -37,87 +41,6 @@ registerCleanupFunction(function tearDown() {
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 {

View File

@ -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();
}
};

View File

@ -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);
}

View File

@ -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);
}
});

View File

@ -14,6 +14,8 @@ 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 \
@ -26,9 +28,13 @@ _BROWSER_TEST_FILES = \
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^ \

View File

@ -16,7 +16,7 @@
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">

View File

@ -3,15 +3,13 @@
// 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) {
@ -42,7 +40,7 @@ function testEditStatus(browser, tab) {
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]" ],
});
@ -52,7 +50,7 @@ function testEditStatus(browser, tab) {
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]" ],
});
@ -61,7 +59,7 @@ function testEditStatus(browser, tab) {
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]" ],
});

View File

@ -1,4 +1,3 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
@ -8,6 +7,10 @@ const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/styleedito
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) {

View File

@ -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();
}
};

View File

@ -1,5 +1,5 @@
// 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');

View File

@ -107,7 +107,7 @@ browser.jar:
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)

View File

@ -143,7 +143,7 @@ browser.jar:
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)

View File

@ -131,7 +131,7 @@ browser.jar:
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)
@ -334,7 +334,7 @@ browser.jar:
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)