Merge mozilla-inbound and mozilla-central.

(The mozilla-central side of the merge is entirely a merge from fx-team into mozilla-central.)
This commit is contained in:
L. David Baron 2013-05-11 22:00:23 -07:00
Родитель 7fe4255fb8 594771a12d
Коммит 3d0109705b
152 изменённых файлов: 6531 добавлений и 3653 удалений

Просмотреть файл

@ -1032,7 +1032,6 @@ pref("devtools.toolbox.host", "bottom");
pref("devtools.toolbox.selectedTool", "webconsole"); pref("devtools.toolbox.selectedTool", "webconsole");
pref("devtools.toolbox.toolbarSpec", '["paintflashing toggle","tilt toggle","scratchpad","resize toggle"]'); pref("devtools.toolbox.toolbarSpec", '["paintflashing toggle","tilt toggle","scratchpad","resize toggle"]');
pref("devtools.toolbox.sideEnabled", true); pref("devtools.toolbox.sideEnabled", true);
pref("devtools.toolbox.disabledTools", "[]");
// Enable the Inspector // Enable the Inspector
pref("devtools.inspector.enabled", true); pref("devtools.inspector.enabled", true);

Просмотреть файл

@ -30,6 +30,7 @@ MOCHITEST_BROWSER_FILES = \
browser_input.js \ browser_input.js \
browser_input_sample.html \ browser_input_sample.html \
browser_pageshow.js \ browser_pageshow.js \
browser_windowRestore_perwindowpb.js \
browser_248970_b_perwindowpb.js \ browser_248970_b_perwindowpb.js \
browser_248970_b_sample.html \ browser_248970_b_sample.html \
browser_339445.js \ browser_339445.js \

Просмотреть файл

@ -0,0 +1,30 @@
/* 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/. */
// This test checks that closed private windows can't be restored
function test() {
waitForExplicitFinish();
// Purging the list of closed windows
while(ss.getClosedWindowCount() > 0)
ss.forgetClosedWindow(0);
// Load a private window, then close it
// and verify it doesn't get remembered for restoring
var win = OpenBrowserWindow({private: true});
whenWindowLoaded(win, function onload() {
info("The private window got loaded");
win.addEventListener("SSWindowClosing", function onclosing() {
win.removeEventListener("SSWindowClosing", onclosing, false);
executeSoon(function () {
is (ss.getClosedWindowCount(), 0,
"The private window should not have been stored");
finish();
});
}, false);
win.close();
});
}

Просмотреть файл

@ -9,12 +9,14 @@ const BRAND_SHORT_NAME = Cc["@mozilla.org/intl/stringbundle;1"]
.createBundle("chrome://branding/locale/brand.properties") .createBundle("chrome://branding/locale/brand.properties")
.GetStringFromName("brandShortName"); .GetStringFromName("brandShortName");
this.EXPORTED_SYMBOLS = [ "CmdAddonFlags", "CmdCommands" ]; this.EXPORTED_SYMBOLS = [ "CmdAddonFlags", "CmdCommands", "DEFAULT_DEBUG_PORT", "connect" ];
Cu.import("resource:///modules/devtools/gcli.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
Cu.import("resource://gre/modules/osfile.jsm")
Cu.import("resource://gre/modules/devtools/gcli.jsm");
Cu.import("resource:///modules/devtools/shared/event-emitter.js"); Cu.import("resource:///modules/devtools/shared/event-emitter.js");
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
@ -37,16 +39,16 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}; };
/** /**
* 'addon' command. * 'addon' command.
*/ */
gcli.addCommand({ gcli.addCommand({
name: "addon", name: "addon",
description: gcli.lookup("addonDesc") description: gcli.lookup("addonDesc")
}); });
/** /**
* 'addon list' command. * 'addon list' command.
*/ */
gcli.addCommand({ gcli.addCommand({
name: "addon list", name: "addon list",
description: gcli.lookup("addonListDesc"), description: gcli.lookup("addonListDesc"),
@ -261,11 +263,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
params: [nameParameter], params: [nameParameter],
exec: function(aArgs, context) { exec: function(aArgs, context) {
/** /**
* Enables the addon in the passed list which has a name that matches * Enables the addon in the passed list which has a name that matches
* according to the passed name comparer, and resolves the promise which * according to the passed name comparer, and resolves the promise which
* is the scope (this) of this function to display the result of this * is the scope (this) of this function to display the result of this
* enable attempt. * enable attempt.
*/ */
function enable(aName, addons) { function enable(aName, addons) {
// Find the add-on. // Find the add-on.
let addon = null; let addon = null;
@ -298,8 +300,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}); });
/** /**
* 'addon disable' command. * 'addon disable' command.
*/ */
gcli.addCommand({ gcli.addCommand({
name: "addon disable", name: "addon disable",
description: gcli.lookup("addonDisableDesc"), description: gcli.lookup("addonDisableDesc"),
@ -410,22 +412,22 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
let debuggers = []; let debuggers = [];
/** /**
* 'calllog' command * 'calllog' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "calllog", name: "calllog",
description: gcli.lookup("calllogDesc") description: gcli.lookup("calllogDesc")
}) })
/** /**
* 'calllog start' command * 'calllog start' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "calllog start", name: "calllog start",
description: gcli.lookup("calllogStartDesc"), description: gcli.lookup("calllogStartDesc"),
exec: function(args, context) { exec: function(args, context) {
let contentWindow = context.environment.contentDocument.defaultView; let contentWindow = context.environment.window;
let dbg = new Debugger(contentWindow); let dbg = new Debugger(contentWindow);
dbg.onEnterFrame = function(frame) { dbg.onEnterFrame = function(frame) {
@ -467,8 +469,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}); });
/** /**
* 'calllog stop' command * 'calllog stop' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "calllog stop", name: "calllog stop",
description: gcli.lookup("calllogStopDesc"), description: gcli.lookup("calllogStopDesc"),
@ -506,8 +508,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
let sandboxes = []; let sandboxes = [];
/** /**
* 'calllog chromestart' command * 'calllog chromestart' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "calllog chromestart", name: "calllog chromestart",
description: gcli.lookup("calllogChromeStartDesc"), description: gcli.lookup("calllogChromeStartDesc"),
@ -529,7 +531,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
], ],
exec: function(args, context) { exec: function(args, context) {
let globalObj; let globalObj;
let contentWindow = context.environment.contentDocument.defaultView; let contentWindow = context.environment.window;
if (args.sourceType == "jsm") { if (args.sourceType == "jsm") {
try { try {
@ -608,8 +610,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}); });
/** /**
* 'calllog chromestop' command * 'calllog chromestop' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "calllog chromestop", name: "calllog chromestop",
description: gcli.lookup("calllogChromeStopDesc"), description: gcli.lookup("calllogChromeStopDesc"),
@ -652,22 +654,22 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
const PREF_DIR = "devtools.commands.dir"; const PREF_DIR = "devtools.commands.dir";
/** /**
* A place to store the names of the commands that we have added as a result of * 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 * calling refreshAutoCommands(). Used by refreshAutoCommands to remove the
* added commands. * added commands.
*/ */
let commands = []; let commands = [];
/** /**
* Exported API * Exported API
*/ */
this.CmdCommands = { this.CmdCommands = {
/** /**
* Called to look in a directory pointed at by the devtools.commands.dir pref * Called to look in a directory pointed at by the devtools.commands.dir pref
* for *.mozcmd files which are then loaded. * for *.mozcmd files which are then loaded.
* @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which * @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which
* we eval the script from the .mozcmd file. This should be a chrome window. * we eval the script from the .mozcmd file. This should be a chrome window.
*/ */
refreshAutoCommands: function GC_refreshAutoCommands(aSandboxPrincipal) { refreshAutoCommands: function GC_refreshAutoCommands(aSandboxPrincipal) {
// First get rid of the last set of commands // First get rid of the last set of commands
commands.forEach(function(name) { commands.forEach(function(name) {
@ -734,12 +736,12 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}; };
/** /**
* Load the commands from a single file * Load the commands from a single file
* @param OS.File.DirectoryIterator.Entry aFileEntry The DirectoryIterator * @param OS.File.DirectoryIterator.Entry aFileEntry The DirectoryIterator
* Entry of the file containing the commands that we should read * Entry of the file containing the commands that we should read
* @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which * @param nsIPrincipal aSandboxPrincipal Scope object for the Sandbox in which
* we eval the script from the .mozcmd file. This should be a chrome window. * we eval the script from the .mozcmd file. This should be a chrome window.
*/ */
function loadCommandFile(aFileEntry, aSandboxPrincipal) { function loadCommandFile(aFileEntry, aSandboxPrincipal) {
let promise = OS.File.read(aFileEntry.path); let promise = OS.File.read(aFileEntry.path);
promise = promise.then( promise = promise.then(
@ -773,8 +775,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
} }
/** /**
* 'cmd' command * 'cmd' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "cmd", name: "cmd",
get hidden() { return !prefBranch.prefHasUserValue(PREF_DIR); }, get hidden() { return !prefBranch.prefHasUserValue(PREF_DIR); },
@ -782,8 +784,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}); });
/** /**
* 'cmd refresh' command * 'cmd refresh' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "cmd refresh", name: "cmd refresh",
description: gcli.lookup("cmdRefreshDesc"), description: gcli.lookup("cmdRefreshDesc"),
@ -802,8 +804,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
"resource:///modules/HUDService.jsm"); "resource:///modules/HUDService.jsm");
/** /**
* 'console' command * 'console' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "console", name: "console",
description: gcli.lookup("consoleDesc"), description: gcli.lookup("consoleDesc"),
@ -811,14 +813,13 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}); });
/** /**
* 'console clear' command * 'console clear' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "console clear", name: "console clear",
description: gcli.lookup("consoleclearDesc"), description: gcli.lookup("consoleclearDesc"),
exec: function Command_consoleClear(args, context) { exec: function Command_consoleClear(args, context) {
let window = context.environment.contentDocument.defaultView; let hud = HUDService.getHudByWindow(context.environment.window);
let hud = HUDService.getHudByWindow(window);
// hud will be null if the web console has not been opened for this window // hud will be null if the web console has not been opened for this window
if (hud) { if (hud) {
hud.jsterm.clearOutput(); hud.jsterm.clearOutput();
@ -827,8 +828,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}); });
/** /**
* 'console close' command * 'console close' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "console close", name: "console close",
description: gcli.lookup("consolecloseDesc"), description: gcli.lookup("consolecloseDesc"),
@ -840,8 +841,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}); });
/** /**
* 'console open' command * 'console open' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "console open", name: "console open",
description: gcli.lookup("consoleopenDesc"), description: gcli.lookup("consoleopenDesc"),
@ -976,7 +977,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
returnType: "cookies", returnType: "cookies",
exec: function Command_cookieList(args, context) { exec: function Command_cookieList(args, context) {
let host = context.environment.document.location.host; let host = context.environment.document.location.host;
if (host == null) { if (host == null || host == "") {
throw new Error(gcli.lookup("cookieListOutNonePage")); throw new Error(gcli.lookup("cookieListOutNonePage"));
} }
@ -1023,7 +1024,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
let cookies = []; let cookies = [];
while (enm.hasMoreElements()) { while (enm.hasMoreElements()) {
let cookie = enm.getNext().QueryInterface(Components.interfaces.nsICookie); let cookie = enm.getNext().QueryInterface(Ci.nsICookie);
if (isCookieAtHost(cookie, host)) { if (isCookieAtHost(cookie, host)) {
if (cookie.name == args.name) { if (cookie.name == args.name) {
cookieMgr.remove(cookie.host, cookie.name, cookie.path, false); cookieMgr.remove(cookie.host, cookie.name, cookie.path, false);
@ -1156,45 +1157,21 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
} }
}(this)); }(this));
/* CmdEcho ----------------------------------------------------------------- */
(function(module) {
/**
* '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;
}
});
}(this));
/* CmdExport --------------------------------------------------------------- */ /* CmdExport --------------------------------------------------------------- */
(function(module) { (function(module) {
/** /**
* 'export' command * 'export' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "export", name: "export",
description: gcli.lookup("exportDesc"), description: gcli.lookup("exportDesc"),
}); });
/** /**
* The 'export html' command. This command allows the user to export the page to * The 'export html' command. This command allows the user to export the page to
* HTML after they do DOM changes. * HTML after they do DOM changes.
*/ */
gcli.addCommand({ gcli.addCommand({
name: "export html", name: "export html",
description: gcli.lookup("exportHtmlDesc"), description: gcli.lookup("exportHtmlDesc"),
@ -1216,8 +1193,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
"resource:///modules/devtools/Jsbeautify.jsm"); "resource:///modules/devtools/Jsbeautify.jsm");
/** /**
* jsb command. * jsb command.
*/ */
gcli.addCommand({ gcli.addCommand({
name: 'jsb', name: 'jsb',
description: gcli.lookup('jsbDesc'), description: gcli.lookup('jsbDesc'),
@ -1343,17 +1320,17 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
(function(module) { (function(module) {
/** /**
* 'pagemod' command * 'pagemod' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "pagemod", name: "pagemod",
description: gcli.lookup("pagemodDesc"), description: gcli.lookup("pagemodDesc"),
}); });
/** /**
* The 'pagemod replace' command. This command allows the user to search and * The 'pagemod replace' command. This command allows the user to search and
* replace within text nodes and attributes. * replace within text nodes and attributes.
*/ */
gcli.addCommand({ gcli.addCommand({
name: "pagemod replace", name: "pagemod replace",
description: gcli.lookup("pagemodReplaceDesc"), description: gcli.lookup("pagemodReplaceDesc"),
@ -1403,7 +1380,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}, },
], ],
exec: function(args, context) { exec: function(args, context) {
let document = context.environment.contentDocument;
let searchTextNodes = !args.attrOnly; let searchTextNodes = !args.attrOnly;
let searchAttributes = !args.contentOnly; let searchAttributes = !args.contentOnly;
let regexOptions = args.ignoreCase ? 'ig' : 'g'; let regexOptions = args.ignoreCase ? 'ig' : 'g';
@ -1413,7 +1389,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
attributeRegex = new RegExp(args.attributes, regexOptions); attributeRegex = new RegExp(args.attributes, regexOptions);
} }
let root = args.root || document; let root = args.root || context.environment.document;
let elements = root.querySelectorAll(args.selector); let elements = root.querySelectorAll(args.selector);
elements = Array.prototype.slice.call(elements); elements = Array.prototype.slice.call(elements);
@ -1460,8 +1436,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}); });
/** /**
* 'pagemod remove' command * 'pagemod remove' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "pagemod remove", name: "pagemod remove",
description: gcli.lookup("pagemodRemoveDesc"), description: gcli.lookup("pagemodRemoveDesc"),
@ -1469,8 +1445,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
/** /**
* The 'pagemod remove element' command. * The 'pagemod remove element' command.
*/ */
gcli.addCommand({ gcli.addCommand({
name: "pagemod remove element", name: "pagemod remove element",
description: gcli.lookup("pagemodRemoveElementDesc"), description: gcli.lookup("pagemodRemoveElementDesc"),
@ -1498,8 +1474,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}, },
], ],
exec: function(args, context) { exec: function(args, context) {
let document = context.environment.contentDocument; let root = args.root || context.environment.document;
let root = args.root || document;
let elements = Array.prototype.slice.call(root.querySelectorAll(args.search)); let elements = Array.prototype.slice.call(root.querySelectorAll(args.search));
let removed = 0; let removed = 0;
@ -1526,8 +1501,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}); });
/** /**
* The 'pagemod remove attribute' command. * The 'pagemod remove attribute' command.
*/ */
gcli.addCommand({ gcli.addCommand({
name: "pagemod remove attribute", name: "pagemod remove attribute",
description: gcli.lookup("pagemodRemoveAttributeDesc"), description: gcli.lookup("pagemodRemoveAttributeDesc"),
@ -1555,9 +1530,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}, },
], ],
exec: function(args, context) { exec: function(args, context) {
let document = context.environment.contentDocument; let root = args.root || context.environment.document;
let root = args.root || document;
let regexOptions = args.ignoreCase ? 'ig' : 'g'; let regexOptions = args.ignoreCase ? 'ig' : 'g';
let attributeRegex = new RegExp(args.searchAttributes, regexOptions); let attributeRegex = new RegExp(args.searchAttributes, regexOptions);
let elements = root.querySelectorAll(args.searchElements); let elements = root.querySelectorAll(args.searchElements);
@ -1586,13 +1559,13 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}); });
/** /**
* Make a given string safe to use in a regular expression. * Make a given string safe to use in a regular expression.
* *
* @param string aString * @param string aString
* The string you want to use in a regex. * The string you want to use in a regex.
* @return string * @return string
* The equivalent of |aString| but safe to use in a regex. * The equivalent of |aString| but safe to use in a regex.
*/ */
function escapeRegex(aString) { function escapeRegex(aString) {
return aString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); return aString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
} }
@ -1622,22 +1595,21 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
], ],
returnType: "string", returnType: "string",
exec: function(args, context) { exec: function(args, context) {
let promise = context.createPromise(); return OS.File.exists(args.srcdir + "/CLOBBER").then(function(exists) {
let existsPromise = OS.File.exists(args.srcdir + "/CLOBBER");
existsPromise.then(function(exists) {
if (exists) { if (exists) {
var str = Cc["@mozilla.org/supports-string;1"] let str = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString); .createInstance(Ci.nsISupportsString);
str.data = args.srcdir; str.data = args.srcdir;
Services.prefs.setComplexValue("devtools.loader.srcdir", Services.prefs.setComplexValue("devtools.loader.srcdir",
Components.interfaces.nsISupportsString, str); Ci.nsISupportsString, str);
devtools.reload(); devtools.reload();
promise.resolve(gcli.lookupFormat("toolsSrcdirReloaded", [args.srcdir]));
return; let msg = gcli.lookupFormat("toolsSrcdirReloaded", [args.srcdir]);
throw new Error(msg);
} }
promise.reject(gcli.lookupFormat("toolsSrcdirNotFound", [args.srcdir]));
return gcli.lookupFormat("toolsSrcdirNotFound", [args.srcdir]);
}); });
return promise;
} }
}); });
@ -1671,18 +1643,17 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
(function(module) { (function(module) {
/** /**
* Restart command * Restart command
* *
* @param boolean nocache * @param boolean nocache
* Disables loading content from cache upon restart. * Disables loading content from cache upon restart.
* *
* Examples : * Examples :
* >> restart * >> restart
* - restarts browser immediately * - restarts browser immediately
* >> restart --nocache * >> restart --nocache
* - restarts immediately and starts Firefox without using cache * - restarts immediately and starts Firefox without using cache
*/ */
gcli.addCommand({ gcli.addCommand({
name: "restart", name: "restart",
description: gcli.lookupFormat("restartBrowserDesc", [BRAND_SHORT_NAME]), description: gcli.lookupFormat("restartBrowserDesc", [BRAND_SHORT_NAME]),
@ -1727,13 +1698,13 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
const FILENAME_DEFAULT_VALUE = " "; const FILENAME_DEFAULT_VALUE = " ";
/** /**
* 'screenshot' command * 'screenshot' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: "screenshot", name: "screenshot",
description: gcli.lookup("screenshotDesc"), description: gcli.lookup("screenshotDesc"),
manual: gcli.lookup("screenshotManual"), manual: gcli.lookup("screenshotManual"),
returnType: "html", returnType: "dom",
params: [ params: [
{ {
name: "filename", name: "filename",
@ -1788,7 +1759,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
throw new Error(gcli.lookup("screenshotSelectorChromeConflict")); throw new Error(gcli.lookup("screenshotSelectorChromeConflict"));
} }
var document = args.chrome? context.environment.chromeDocument var document = args.chrome? context.environment.chromeDocument
: context.environment.contentDocument; : context.environment.document;
if (args.delay > 0) { if (args.delay > 0) {
var deferred = context.defer(); var deferred = context.defer();
document.defaultView.setTimeout(function Command_screenshotDelay() { document.defaultView.setTimeout(function Command_screenshotDelay() {
@ -1803,9 +1774,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
args.fullpage, args.selector); args.fullpage, args.selector);
} }
}, },
grabScreen: grabScreen: function(document, filename, clipboard, fullpage, node) {
function Command_screenshotGrabScreen(document, filename, clipboard,
fullpage, node) {
let window = document.defaultView; let window = document.defaultView;
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
let left = 0; let left = 0;
@ -1948,13 +1917,53 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}); });
}(this)); }(this));
/* Remoting ----------------------------------------------------------- */
const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
/**
* 'listen' command
*/
gcli.addCommand({
name: "listen",
description: gcli.lookup("listenDesc"),
manual: gcli.lookup("listenManual"),
params: [
{
name: "port",
type: "number",
get defaultValue() {
return Services.prefs.getIntPref("devtools.debugger.chrome-debugging-port");
},
description: gcli.lookup("listenPortDesc"),
}
],
exec: function Command_screenshot(args, context) {
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
var reply = DebuggerServer.openListener(args.port);
if (!reply) {
throw new Error(gcli.lookup("listenDisabledOutput"));
}
if (DebuggerServer.initialized) {
return gcli.lookupFormat("listenInitOutput", [ '' + args.port ]);
}
return gcli.lookup("listenNoInitOutput");
},
});
/* CmdPaintFlashing ------------------------------------------------------- */ /* CmdPaintFlashing ------------------------------------------------------- */
(function(module) { (function(module) {
/** /**
* 'paintflashing' command * 'paintflashing' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: 'paintflashing', name: 'paintflashing',
description: gcli.lookup('paintflashingDesc') description: gcli.lookup('paintflashingDesc')
@ -1976,15 +1985,13 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
] ]
}], }],
exec: function(args, context) { exec: function(args, context) {
var window; var window = args.chrome ?
if (args.chrome) { context.environment.chromeWindow :
window = context.environment.chromeDocument.defaultView; context.environment.window;
} else {
window = context.environment.contentDocument.defaultView; window.QueryInterface(Ci.nsIInterfaceRequestor)
} .getInterface(Ci.nsIDOMWindowUtils)
window.QueryInterface(Ci.nsIInterfaceRequestor). .paintFlashing = true;
getInterface(Ci.nsIDOMWindowUtils).
paintFlashing = true;
onPaintFlashingChanged(context); onPaintFlashingChanged(context);
} }
}); });
@ -2005,14 +2012,13 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
] ]
}], }],
exec: function(args, context) { exec: function(args, context) {
if (args.chrome) { var window = args.chrome ?
var window = context.environment.chromeDocument.defaultView; context.environment.chromeWindow :
} else { context.environment.window;
var window = context.environment.contentDocument.defaultView;
} window.QueryInterface(Ci.nsIInterfaceRequestor)
window.QueryInterface(Ci.nsIInterfaceRequestor). .getInterface(Ci.nsIDOMWindowUtils)
getInterface(Ci.nsIDOMWindowUtils). .paintFlashing = false;
paintFlashing = false;
onPaintFlashingChanged(context); onPaintFlashingChanged(context);
} }
}); });
@ -2041,7 +2047,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}, },
}, },
tooltipText: gcli.lookup("paintflashingTooltip"), tooltipText: gcli.lookup("paintflashingTooltip"),
description: gcli.lookup('paintflashingOnDesc'), description: gcli.lookup('paintflashingToggleDesc'),
manual: gcli.lookup('paintflashingManual'), manual: gcli.lookup('paintflashingManual'),
exec: function(args, context) { exec: function(args, context) {
var gBrowser = context.environment.chromeDocument.defaultView.gBrowser; var gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
@ -2071,8 +2077,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
(function(module) { (function(module) {
/** /**
* 'appcache' command * 'appcache' command
*/ */
gcli.addCommand({ gcli.addCommand({
name: 'appcache', name: 'appcache',
@ -2125,20 +2131,19 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}], }],
exec: function(args, context) { exec: function(args, context) {
let utils; let utils;
let promise = context.createPromise(); let deferred = context.defer();
if (args.uri) { if (args.uri) {
utils = new AppCacheUtils(args.uri); utils = new AppCacheUtils(args.uri);
} else { } else {
let doc = context.environment.contentDocument; utils = new AppCacheUtils(context.environment.document);
utils = new AppCacheUtils(doc);
} }
utils.validateManifest().then(function(errors) { utils.validateManifest().then(function(errors) {
promise.resolve([errors, utils.manifestURI || "-"]); deferred.resolve([errors, utils.manifestURI || "-"]);
}); });
return promise; return deferred.promise;
} }
}); });
@ -2154,62 +2159,50 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
} }
}); });
let appcacheListEntries = "" +
"<ul class='gcli-appcache-list'>" +
" <li foreach='entry in ${entries}'>" +
" <table class='gcli-appcache-detail'>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListKey") + "</td>" +
" <td>${entry.key}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListFetchCount") + "</td>" +
" <td>${entry.fetchCount}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListLastFetched") + "</td>" +
" <td>${entry.lastFetched}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListLastModified") + "</td>" +
" <td>${entry.lastModified}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListExpirationTime") + "</td>" +
" <td>${entry.expirationTime}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListDataSize") + "</td>" +
" <td>${entry.dataSize}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListDeviceID") + "</td>" +
" <td>${entry.deviceID} <span class='gcli-out-shortcut' " +
"onclick='${onclick}' ondblclick='${ondblclick}' " +
"data-command='appcache viewentry ${entry.key}'" +
">" + gcli.lookup("appCacheListViewEntry") + "</span>" +
" </td>" +
" </tr>" +
" </table>" +
" </li>" +
"</ul>";
gcli.addConverter({ gcli.addConverter({
from: "appcacheentries", from: "appcacheentries",
to: "view", to: "view",
exec: function(entries, context) { exec: function(entries, context) {
if (!entries) {
return context.createView({
html: "<span>" + gcli.lookup("appCacheManifestContainsErrors") + "</span>"
});
}
if (entries.length == 0) {
return context.createView({
html: "<span>" + gcli.lookup("appCacheNoResults") + "</span>"
});
}
let appcacheListEntries = "" +
"<ul class='gcli-appcache-list'>" +
" <li foreach='entry in ${entries}'>" +
" <table class='gcli-appcache-detail'>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListKey") + "</td>" +
" <td>${entry.key}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListFetchCount") + "</td>" +
" <td>${entry.fetchCount}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListLastFetched") + "</td>" +
" <td>${entry.lastFetched}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListLastModified") + "</td>" +
" <td>${entry.lastModified}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListExpirationTime") + "</td>" +
" <td>${entry.expirationTime}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListDataSize") + "</td>" +
" <td>${entry.dataSize}</td>" +
" </tr>" +
" <tr>" +
" <td>" + gcli.lookup("appCacheListDeviceID") + "</td>" +
" <td>${entry.deviceID} <span class='gcli-out-shortcut' " +
"onclick='${onclick}' ondblclick='${ondblclick}' " +
"data-command='appcache viewentry ${entry.key}'" +
">" + gcli.lookup("appCacheListViewEntry") + "</span>" +
" </td>" +
" </tr>" +
" </table>" +
" </li>" +
"</ul>";
return context.createView({ return context.createView({
html: appcacheListEntries, html: appcacheListEntries,
data: { data: {
@ -2238,11 +2231,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
] ]
}], }],
exec: function(args, context) { exec: function(args, context) {
let doc = context.environment.contentDocument;
let utils = new AppCacheUtils(); let utils = new AppCacheUtils();
return utils.listEntries(args.search);
let entries = utils.listEntries(args.search);
return entries;
} }
}); });
@ -2259,13 +2249,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
} }
], ],
exec: function(args, context) { exec: function(args, context) {
let doc = context.environment.contentDocument;
let utils = new AppCacheUtils(); let utils = new AppCacheUtils();
return utils.viewEntry(args.key);
let result = utils.viewEntry(args.key);
if (result) {
return result;
}
} }
}); });

Просмотреть файл

@ -31,6 +31,7 @@ MOCHITEST_BROWSER_FILES = \
browser_cmd_appcache_valid_page2.html \ browser_cmd_appcache_valid_page2.html \
browser_cmd_appcache_valid_page3.html \ browser_cmd_appcache_valid_page3.html \
browser_cmd_commands.js \ browser_cmd_commands.js \
browser_cmd_cookie.html \
browser_cmd_cookie.js \ browser_cmd_cookie.js \
browser_cmd_jsb.js \ browser_cmd_jsb.js \
browser_cmd_jsb_script.jsi \ browser_cmd_jsb_script.jsi \

Просмотреть файл

@ -16,13 +16,8 @@ function test() {
tests.gatTest = function(options) { tests.gatTest = function(options) {
let deferred = Promise.defer(); let deferred = Promise.defer();
// hack to reduce stack size as a result of bug 842347
let onGatReadyInterjection = function() {
executeSoon(onGatReady);
};
let onGatReady = function() { let onGatReady = function() {
Services.obs.removeObserver(onGatReadyInterjection, "gcli_addon_commands_ready"); Services.obs.removeObserver(onGatReady, "gcli_addon_commands_ready");
info("gcli_addon_commands_ready notification received, running tests"); info("gcli_addon_commands_ready notification received, running tests");
let auditDone = helpers.audit(options, [ let auditDone = helpers.audit(options, [
@ -121,7 +116,7 @@ tests.gatTest = function(options) {
}); });
}; };
Services.obs.addObserver(onGatReadyInterjection, "gcli_addon_commands_ready", false); Services.obs.addObserver(onGatReady, "gcli_addon_commands_ready", false);
if (CmdAddonFlags.addonsLoaded) { if (CmdAddonFlags.addonsLoaded) {
info("The call to AddonManager.getAllAddons in BuiltinCommands.jsm is done."); info("The call to AddonManager.getAllAddons in BuiltinCommands.jsm is done.");

Просмотреть файл

@ -30,6 +30,25 @@ function test() {
}, },
}, },
{
setup: function() {
Services.prefs.setBoolPref("browser.cache.disk.enable", false);
helpers.setInput(options, 'appcache list', 13);
},
check: {
input: 'appcache list',
markup: 'VVVVVVVVVVVVV',
status: 'VALID',
args: {},
},
exec: {
output: [ /cache is disabled/ ]
},
post: function(output) {
Services.prefs.setBoolPref("browser.cache.disk.enable", true);
}
},
{ {
setup: 'appcache list', setup: 'appcache list',
check: { check: {
@ -56,8 +75,8 @@ function test() {
exec: { exec: {
output: [ /page1/, /page2/, /page3/ ] output: [ /page1/, /page2/, /page3/ ]
}, },
post: function(output) { post: function(output, text) {
ok(!output.contains("index"), "index is not contained in output"); ok(!text.contains("index"), "index is not contained in output");
} }
}, },
@ -118,11 +137,11 @@ function test() {
exec: { exec: {
output: [ /no results/ ] output: [ /no results/ ]
}, },
post: function(output) { post: function(output, text) {
ok(!output.contains("index"), "index is not contained in output"); ok(!text.contains("index"), "index is not contained in output");
ok(!output.contains("page1"), "page1 is not contained in output"); ok(!text.contains("page1"), "page1 is not contained in output");
ok(!output.contains("page2"), "page1 is not contained in output"); ok(!text.contains("page2"), "page1 is not contained in output");
ok(!output.contains("page3"), "page1 is not contained in output"); ok(!text.contains("page3"), "page1 is not contained in output");
} }
}, },

Просмотреть файл

@ -15,17 +15,6 @@ function test() {
}).then(finish); }).then(finish);
} }
tests.testEcho = function(options) {
return helpers.audit(options, [
{
setup: "echo message",
exec: {
output: "message",
}
}
]);
};
tests.testConsole = function(options) { tests.testConsole = function(options) {
let deferred = Promise.defer(); let deferred = Promise.defer();
let hud = null; let hud = null;
@ -67,12 +56,7 @@ tests.testConsole = function(options) {
} }
} }
]).then(function() { ]).then(function() {
// FIXME: Remove this hack once bug 842347 is fixed deferred.resolve();
// Gak - our promises impl causes so many stack frames that we blow up the
// JS engine. Jumping to a new event with a truncated stack solves this.
executeSoon(function() {
deferred.resolve();
});
}); });
}; };

Просмотреть файл

@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>GCLI cookie command test</title>
</head>
<body>
<p>Cookie test</p>
<p id=result></p>
<script type="text/javascript">
document.cookie = "zap=zep";
document.cookie = "zip=zop";
document.getElementById("result").innerHTML = document.cookie;
</script>
</body>
</html>

Просмотреть файл

@ -3,7 +3,8 @@
// Tests that the cookie commands works as they should // Tests that the cookie commands works as they should
const TEST_URI = "data:text/html;charset=utf-8,gcli-cookie"; const TEST_URI = "http://example.com/browser/browser/devtools/commandline/"+
"test/browser_cmd_cookie.html";
function test() { function test() {
helpers.addTabWithToolbar(TEST_URI, function(options) { helpers.addTabWithToolbar(TEST_URI, function(options) {
@ -79,14 +80,14 @@ function test() {
{ {
setup: "cookie list", setup: "cookie list",
exec: { exec: {
output: 'No cookies found for host' output: [ /zap=zep/, /zip=zop/, /Edit/ ]
} }
}, },
{ {
setup: "cookie set fruit banana", setup: "cookie set zup banana",
check: { check: {
args: { args: {
name: { value: 'fruit' }, name: { value: 'zup' },
value: { value: 'banana' }, value: { value: 'banana' },
} }
}, },
@ -97,24 +98,56 @@ function test() {
{ {
setup: "cookie list", setup: "cookie list",
exec: { exec: {
output: [ /fruit=banana/, /Expires:/, /Edit/ ] output: [ /zap=zep/, /zip=zop/, /zup=banana/, /Edit/ ]
} }
}, },
{ {
setup: "cookie remove fruit", setup: "cookie remove zip",
check: { exec: { },
args: {
name: { value: 'fruit' },
}
},
exec: {
output: ""
}
}, },
{ {
setup: "cookie list", setup: "cookie list",
exec: { exec: {
output: 'No cookies found for host' output: [ /zap=zep/, /zup=banana/, /Edit/ ]
},
post: function(output, text) {
ok(!text.contains("zip"), "");
ok(!text.contains("zop"), "");
}
},
{
setup: "cookie remove zap",
exec: { },
},
{
setup: "cookie list",
exec: {
output: [ /zup=banana/, /Edit/ ]
},
post: function(output, text) {
ok(!text.contains("zap"), "");
ok(!text.contains("zep"), "");
ok(!text.contains("zip"), "");
ok(!text.contains("zop"), "");
}
},
{
setup: "cookie remove zup",
exec: { }
},
{
setup: "cookie list",
exec: {
output: 'No cookies found for host example.com'
},
post: function(output, text) {
ok(!text.contains("zap"), "");
ok(!text.contains("zep"), "");
ok(!text.contains("zip"), "");
ok(!text.contains("zop"), "");
ok(!text.contains("zup"), "");
ok(!text.contains("banana"), "");
ok(!text.contains("Edit"), "");
} }
}, },
]); ]);

Просмотреть файл

@ -38,6 +38,7 @@ function test() {
// var helpers = require('gclitest/helpers'); // var helpers = require('gclitest/helpers');
var canon = require('gcli/canon'); var canon = require('gcli/canon');
// var assert = require('test/assert'); // var assert = require('test/assert');
var Canon = canon.Canon;
var startCount = undefined; var startCount = undefined;
var events = undefined; var events = undefined;
@ -157,8 +158,6 @@ exports.testAddRemove2 = function(options) {
return helpers.audit(options, [ return helpers.audit(options, [
{ {
setup: 'testadd', setup: 'testadd',
check: {
},
exec: { exec: {
output: /^3$/ output: /^3$/
}, },
@ -199,4 +198,74 @@ exports.testAddRemove3 = function(options) {
canon.onCanonChange.remove(canonChange); canon.onCanonChange.remove(canonChange);
}; };
exports.testAltCanon = function(options) {
var altCanon = new Canon();
var tss = {
name: 'tss',
params: [
{ name: 'str', type: 'string' },
{ name: 'num', type: 'number' },
{ name: 'opt', type: { name: 'selection', data: [ '1', '2', '3' ] } },
],
exec: function(args, context) {
return context.commandName + ':' +
args.str + ':' + args.num + ':' + args.opt;
}
};
altCanon.addCommand(tss);
var commandSpecs = altCanon.getCommandSpecs();
assert.is(JSON.stringify(commandSpecs),
'{"tss":{"name":"tss","params":[' +
'{"name":"str","type":"string"},' +
'{"name":"num","type":"number"},' +
'{"name":"opt","type":{"name":"selection","data":["1","2","3"]}}]}}',
'JSON.stringify(commandSpecs)');
var remoter = function(args, context) {
assert.is(context.commandName, 'tss', 'commandName is tss');
var cmd = altCanon.getCommand(context.commandName);
return cmd.exec(args, context);
};
canon.addProxyCommands('proxy', commandSpecs, remoter, 'test');
var parent = canon.getCommand('proxy');
assert.is(parent.name, 'proxy', 'Parent command called proxy');
var child = canon.getCommand('proxy tss');
assert.is(child.name, 'proxy tss', 'child command called proxy tss');
return helpers.audit(options, [
{
setup: 'proxy tss foo 6 3',
check: {
input: 'proxy tss foo 6 3',
hints: '',
markup: 'VVVVVVVVVVVVVVVVV',
cursor: 17,
status: 'VALID',
args: {
str: { value: 'foo', status: 'VALID' },
num: { value: 6, status: 'VALID' },
opt: { value: '3', status: 'VALID' }
}
},
exec: {
output: 'tss:foo:6:3'
},
post: function() {
canon.removeCommand('proxy');
canon.removeCommand('proxy tss');
assert.is(canon.getCommand('proxy'), undefined, 'remove proxy');
assert.is(canon.getCommand('proxy tss'), undefined, 'remove proxy tss');
}
}
]);
};
// }); // });

Просмотреть файл

@ -758,7 +758,6 @@ exports.testSingleFloat = function(options) {
current: '__command', current: '__command',
status: 'ERROR', status: 'ERROR',
error: '', error: '',
predictions: [ ],
unassigned: [ ], unassigned: [ ],
args: { args: {
command: { name: 'tsf' }, command: { name: 'tsf' },

Просмотреть файл

@ -519,9 +519,9 @@ exports.testSpaceComplete = function(options) {
msg: { status: 'INCOMPLETE', message: '' }, msg: { status: 'INCOMPLETE', message: '' },
num: { status: 'VALID' }, num: { status: 'VALID' },
sel: { status: 'VALID' }, sel: { status: 'VALID' },
bool: { value: false,status: 'VALID' }, bool: { value: false, status: 'VALID' },
num2: { status: 'VALID' }, num2: { status: 'VALID' },
bool2: { value: false,status: 'VALID' }, bool2: { value: false, status: 'VALID' },
sel2: { sel2: {
value: 'with space', value: 'with space',
arg: ' --sel2 \'with space\' ', arg: ' --sel2 \'with space\' ',

Просмотреть файл

@ -0,0 +1,276 @@
/*
* Copyright 2012, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// define(function(require, exports, module) {
// <INJECTED SOURCE:START>
// THIS FILE IS GENERATED FROM SOURCE IN THE GCLI PROJECT
// DO NOT EDIT IT DIRECTLY
var exports = {};
const TEST_URI = "data:text/html;charset=utf-8,<p id='gcli-input'>gcli-testContext.js</p>";
function test() {
helpers.addTabWithToolbar(TEST_URI, function(options) {
return helpers.runTests(options, exports);
}).then(finish);
}
// <INJECTED SOURCE:END>
'use strict';
// var helpers = require('gclitest/helpers');
// var mockCommands = require('gclitest/mockCommands');
var cli = require('gcli/cli');
var origLogErrors = undefined;
exports.setup = function(options) {
mockCommands.setup();
origLogErrors = cli.logErrors;
cli.logErrors = false;
};
exports.shutdown = function(options) {
mockCommands.shutdown();
cli.logErrors = origLogErrors;
origLogErrors = undefined;
};
exports.testBaseline = function(options) {
helpers.audit(options, [
// These 3 establish a baseline for comparison when we have used the
// context command
{
setup: 'ext',
check: {
input: 'ext',
hints: ' -> context',
markup: 'III',
message: '',
predictions: [ 'context', 'tsn ext', 'tsn exte', 'tsn exten', 'tsn extend' ],
unassigned: [ ],
}
},
{
setup: 'ext test',
check: {
input: 'ext test',
hints: '',
markup: 'IIIVEEEE',
status: 'ERROR',
message: 'Too many arguments',
unassigned: [ ' test' ],
}
},
{
setup: 'tsn',
check: {
input: 'tsn',
hints: '',
markup: 'III',
cursor: 3,
current: '__command',
status: 'ERROR',
predictionsContains: [ 'tsn', 'tsn deep', 'tsn ext', 'tsn exte' ],
args: {
command: { name: 'tsn' },
}
}
}
]);
};
exports.testContext = function(options) {
helpers.audit(options, [
// Use the 'tsn' context
{
setup: 'context tsn',
check: {
input: 'context tsn',
hints: '',
markup: 'VVVVVVVVVVV',
message: '',
predictionsContains: [ 'tsn', 'tsn deep', 'tsn ext', 'tsn exte' ],
args: {
command: { name: 'context' },
prefix: {
value: mockCommands.commands.tsn,
status: 'VALID',
message: ''
},
}
},
exec: {
output: 'Using tsn as a command prefix',
completed: true,
}
},
// For comparison with earlier
{
setup: 'ext',
check: {
input: 'ext',
hints: ' <text>',
markup: 'VVV',
predictions: [ 'tsn ext', 'tsn exte', 'tsn exten', 'tsn extend' ],
args: {
command: { name: 'tsn ext' },
text: {
value: undefined,
arg: '',
status: 'INCOMPLETE',
message: ''
},
}
}
},
{
setup: 'ext test',
check: {
input: 'ext test',
hints: '',
markup: 'VVVVVVVV',
args: {
command: { name: 'tsn ext' },
text: {
value: 'test',
arg: ' test',
status: 'VALID',
message: ''
},
}
},
exec: {
output: 'Exec: tsnExt text=test',
completed: true,
}
},
{
setup: 'tsn',
check: {
input: 'tsn',
hints: '',
markup: 'III',
message: '',
predictionsContains: [ 'tsn', 'tsn deep', 'tsn ext', 'tsn exte' ],
args: {
command: { name: 'tsn' },
}
}
},
// Does it actually work?
{
setup: 'tsb true',
check: {
input: 'tsb true',
hints: '',
markup: 'VVVVVVVV',
options: [ 'true' ],
message: '',
predictions: [ 'true' ],
unassigned: [ ],
args: {
command: { name: 'tsb' },
toggle: { value: true, arg: ' true', status: 'VALID', message: '' },
}
}
},
{
// Bug 866710 - GCLI should allow argument merging for non-string parameters
setup: 'context tsn ext',
skip: true
},
{
setup: 'context "tsn ext"',
check: {
input: 'context "tsn ext"',
hints: '',
markup: 'VVVVVVVVVVVVVVVVV',
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'context' },
prefix: {
value: mockCommands.commands.tsnExt,
status: 'VALID',
message: ''
}
}
},
exec: {
output: 'Error: Can\'t use \'tsn ext\' as a prefix because it is not a parent command.',
completed: true,
error: true
}
},
/*
{
setup: 'context "tsn deep"',
check: {
input: 'context "tsn deep"',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVV',
status: 'ERROR',
message: '',
predictions: [ 'tsn deep' ],
unassigned: [ ],
args: {
command: { name: 'context' },
prefix: {
value: mockCommands.commands.tsnDeep,
status: 'VALID',
message: ''
}
}
},
exec: {
output: '',
completed: true,
}
},
*/
{
setup: 'context',
check: {
input: 'context',
hints: ' [prefix]',
markup: 'VVVVVVV',
status: 'VALID',
unassigned: [ ],
args: {
command: { name: 'context' },
prefix: { value: undefined, arg: '', status: 'VALID', message: '' },
}
},
exec: {
output: 'Command prefix is unset',
completed: true,
type: 'string',
error: false
}
}
]);
};
// });

Просмотреть файл

@ -0,0 +1,99 @@
/*
* Copyright 2009-2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE.txt or:
* http://opensource.org/licenses/BSD-3-Clause
*/
// define(function(require, exports, module) {
// <INJECTED SOURCE:START>
// THIS FILE IS GENERATED FROM SOURCE IN THE GCLI PROJECT
// DO NOT EDIT IT DIRECTLY
var exports = {};
const TEST_URI = "data:text/html;charset=utf-8,<p id='gcli-input'>gcli-testFail.js</p>";
function test() {
helpers.addTabWithToolbar(TEST_URI, function(options) {
return helpers.runTests(options, exports);
}).then(finish);
}
// <INJECTED SOURCE:END>
'use strict';
// var helpers = require('gclitest/helpers');
// var mockCommands = require('gclitest/mockCommands');
var cli = require('gcli/cli');
var origLogErrors = undefined;
exports.setup = function(options) {
mockCommands.setup();
origLogErrors = cli.logErrors;
cli.logErrors = false;
};
exports.shutdown = function(options) {
mockCommands.shutdown();
cli.logErrors = origLogErrors;
origLogErrors = undefined;
};
exports.testBasic = function(options) {
return helpers.audit(options, [
{
setup: 'tsfail reject',
exec: {
completed: false,
output: 'rejected promise',
type: 'error',
error: true
}
},
{
setup: 'tsfail rejecttyped',
exec: {
completed: false,
output: '54',
type: 'number',
error: true
}
},
{
setup: 'tsfail throwerror',
exec: {
completed: true,
output: 'Error: thrown error',
type: 'error',
error: true
}
},
{
setup: 'tsfail throwstring',
exec: {
completed: true,
output: 'thrown string',
type: 'error',
error: true
}
},
{
setup: 'tsfail noerror',
exec: {
completed: true,
output: 'no error',
type: 'string',
error: false
}
}
]);
};
// });

Просмотреть файл

@ -40,22 +40,15 @@ var KeyEvent = require('util/util').KeyEvent;
// var mockCommands = require('gclitest/mockCommands'); // var mockCommands = require('gclitest/mockCommands');
var latestEvent = undefined; var latestEvent = undefined;
var latestOutput = undefined;
var latestData = undefined; var latestData = undefined;
var outputted = function(ev) { var outputted = function(ev) {
function updateData() {
latestData = latestOutput.data;
}
if (latestOutput != null) {
ev.output.onChange.remove(updateData);
}
latestEvent = ev; latestEvent = ev;
latestOutput = ev.output;
ev.output.onChange.add(updateData); ev.output.promise.then(function() {
latestData = ev.output.data;
ev.output.onClose();
});
}; };
@ -71,7 +64,6 @@ exports.shutdown = function(options) {
exports.testOutput = function(options) { exports.testOutput = function(options) {
latestEvent = undefined; latestEvent = undefined;
latestOutput = undefined;
latestData = undefined; latestData = undefined;
var inputter = options.display.inputter; var inputter = options.display.inputter;
@ -103,8 +95,6 @@ exports.testOutput = function(options) {
var ev3 = { keyCode: KeyEvent.DOM_VK_ESCAPE }; var ev3 = { keyCode: KeyEvent.DOM_VK_ESCAPE };
return inputter.handleKeyUp(ev3).then(function() { return inputter.handleKeyUp(ev3).then(function() {
assert.ok(!focusManager._helpRequested, 'ESCAPE = anti help'); assert.ok(!focusManager._helpRequested, 'ESCAPE = anti help');
latestOutput.onClose();
}); });
}); });

Просмотреть файл

@ -61,7 +61,7 @@ exports.testAllPredictions1 = function(options) {
return; return;
} }
var resource = types.getType('resource'); var resource = types.createType('resource');
return resource.getLookup().then(function(opts) { return resource.getLookup().then(function(opts) {
assert.ok(opts.length > 1, 'have all resources'); assert.ok(opts.length > 1, 'have all resources');
@ -77,7 +77,7 @@ exports.testScriptPredictions = function(options) {
return; return;
} }
var resource = types.getType({ name: 'resource', include: 'text/javascript' }); var resource = types.createType({ name: 'resource', include: 'text/javascript' });
return resource.getLookup().then(function(opts) { return resource.getLookup().then(function(opts) {
assert.ok(opts.length > 1, 'have js resources'); assert.ok(opts.length > 1, 'have js resources');
@ -93,7 +93,7 @@ exports.testStylePredictions = function(options) {
return; return;
} }
var resource = types.getType({ name: 'resource', include: 'text/css' }); var resource = types.createType({ name: 'resource', include: 'text/css' });
return resource.getLookup().then(function(opts) { return resource.getLookup().then(function(opts) {
assert.ok(opts.length >= 1, 'have css resources'); assert.ok(opts.length >= 1, 'have css resources');
@ -109,11 +109,11 @@ exports.testAllPredictions2 = function(options) {
return; return;
} }
var scriptRes = types.getType({ name: 'resource', include: 'text/javascript' }); var scriptRes = types.createType({ name: 'resource', include: 'text/javascript' });
return scriptRes.getLookup().then(function(scriptOptions) { return scriptRes.getLookup().then(function(scriptOptions) {
var styleRes = types.getType({ name: 'resource', include: 'text/css' }); var styleRes = types.createType({ name: 'resource', include: 'text/css' });
return styleRes.getLookup().then(function(styleOptions) { return styleRes.getLookup().then(function(styleOptions) {
var allRes = types.getType({ name: 'resource' }); var allRes = types.createType({ name: 'resource' });
return allRes.getLookup().then(function(allOptions) { return allRes.getLookup().then(function(allOptions) {
assert.is(scriptOptions.length + styleOptions.length, assert.is(scriptOptions.length + styleOptions.length,
allOptions.length, allOptions.length,
@ -129,9 +129,9 @@ exports.testAllPredictions3 = function(options) {
return; return;
} }
var res1 = types.getType({ name: 'resource' }); var res1 = types.createType({ name: 'resource' });
return res1.getLookup().then(function(options1) { return res1.getLookup().then(function(options1) {
var res2 = types.getType('resource'); var res2 = types.createType('resource');
return res2.getLookup().then(function(options2) { return res2.getLookup().then(function(options2) {
assert.is(options1.length, options2.length, 'type spec'); assert.is(options1.length, options2.length, 'type spec');
}); });

Просмотреть файл

@ -36,6 +36,7 @@ function test() {
'use strict'; 'use strict';
// var assert = require('test/assert'); // var assert = require('test/assert');
var cli = require('gcli/cli');
var Requisition = require('gcli/cli').Requisition; var Requisition = require('gcli/cli').Requisition;
var canon = require('gcli/canon'); var canon = require('gcli/canon');
// var mockCommands = require('gclitest/mockCommands'); // var mockCommands = require('gclitest/mockCommands');
@ -51,26 +52,26 @@ exports.shutdown = function(options) {
exports.testSplitSimple = function(options) { exports.testSplitSimple = function(options) {
var args; var args;
var requ = new Requisition(); var requisition = new Requisition();
args = requ._tokenize('s'); args = cli.tokenize('s');
requ._split(args); requisition._split(args);
assert.is(0, args.length); assert.is(0, args.length);
assert.is('s', requ.commandAssignment.arg.text); assert.is('s', requisition.commandAssignment.arg.text);
}; };
exports.testFlatCommand = function(options) { exports.testFlatCommand = function(options) {
var args; var args;
var requ = new Requisition(); var requisition = new Requisition();
args = requ._tokenize('tsv'); args = cli.tokenize('tsv');
requ._split(args); requisition._split(args);
assert.is(0, args.length); assert.is(0, args.length);
assert.is('tsv', requ.commandAssignment.value.name); assert.is('tsv', requisition.commandAssignment.value.name);
args = requ._tokenize('tsv a b'); args = cli.tokenize('tsv a b');
requ._split(args); requisition._split(args);
assert.is('tsv', requ.commandAssignment.value.name); assert.is('tsv', requisition.commandAssignment.value.name);
assert.is(2, args.length); assert.is(2, args.length);
assert.is('a', args[0].text); assert.is('a', args[0].text);
assert.is('b', args[1].text); assert.is('b', args[1].text);
@ -83,14 +84,14 @@ exports.testJavascript = function(options) {
} }
var args; var args;
var requ = new Requisition(); var requisition = new Requisition();
args = requ._tokenize('{'); args = cli.tokenize('{');
requ._split(args); requisition._split(args);
assert.is(1, args.length); assert.is(1, args.length);
assert.is('', args[0].text); assert.is('', args[0].text);
assert.is('', requ.commandAssignment.arg.text); assert.is('', requisition.commandAssignment.arg.text);
assert.is('{', requ.commandAssignment.value.name); assert.is('{', requisition.commandAssignment.value.name);
}; };
// BUG 663081 - add tests for sub commands // BUG 663081 - add tests for sub commands

Просмотреть файл

@ -36,19 +36,18 @@ function test() {
'use strict'; 'use strict';
// var assert = require('test/assert'); // var assert = require('test/assert');
var Requisition = require('gcli/cli').Requisition; var cli = require('gcli/cli');
exports.testBlanks = function(options) { exports.testBlanks = function(options) {
var args; var args;
var requ = new Requisition();
args = requ._tokenize(''); args = cli.tokenize('');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('', args[0].text); assert.is('', args[0].text);
assert.is('', args[0].prefix); assert.is('', args[0].prefix);
assert.is('', args[0].suffix); assert.is('', args[0].suffix);
args = requ._tokenize(' '); args = cli.tokenize(' ');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('', args[0].text); assert.is('', args[0].text);
assert.is(' ', args[0].prefix); assert.is(' ', args[0].prefix);
@ -57,16 +56,15 @@ exports.testBlanks = function(options) {
exports.testTokSimple = function(options) { exports.testTokSimple = function(options) {
var args; var args;
var requ = new Requisition();
args = requ._tokenize('s'); args = cli.tokenize('s');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('s', args[0].text); assert.is('s', args[0].text);
assert.is('', args[0].prefix); assert.is('', args[0].prefix);
assert.is('', args[0].suffix); assert.is('', args[0].suffix);
assert.is('Argument', args[0].type); assert.is('Argument', args[0].type);
args = requ._tokenize('s s'); args = cli.tokenize('s s');
assert.is(2, args.length); assert.is(2, args.length);
assert.is('s', args[0].text); assert.is('s', args[0].text);
assert.is('', args[0].prefix); assert.is('', args[0].prefix);
@ -80,23 +78,22 @@ exports.testTokSimple = function(options) {
exports.testJavascript = function(options) { exports.testJavascript = function(options) {
var args; var args;
var requ = new Requisition();
args = requ._tokenize('{x}'); args = cli.tokenize('{x}');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('x', args[0].text); assert.is('x', args[0].text);
assert.is('{', args[0].prefix); assert.is('{', args[0].prefix);
assert.is('}', args[0].suffix); assert.is('}', args[0].suffix);
assert.is('ScriptArgument', args[0].type); assert.is('ScriptArgument', args[0].type);
args = requ._tokenize('{ x }'); args = cli.tokenize('{ x }');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('x', args[0].text); assert.is('x', args[0].text);
assert.is('{ ', args[0].prefix); assert.is('{ ', args[0].prefix);
assert.is(' }', args[0].suffix); assert.is(' }', args[0].suffix);
assert.is('ScriptArgument', args[0].type); assert.is('ScriptArgument', args[0].type);
args = requ._tokenize('{x} {y}'); args = cli.tokenize('{x} {y}');
assert.is(2, args.length); assert.is(2, args.length);
assert.is('x', args[0].text); assert.is('x', args[0].text);
assert.is('{', args[0].prefix); assert.is('{', args[0].prefix);
@ -107,7 +104,7 @@ exports.testJavascript = function(options) {
assert.is('}', args[1].suffix); assert.is('}', args[1].suffix);
assert.is('ScriptArgument', args[1].type); assert.is('ScriptArgument', args[1].type);
args = requ._tokenize('{x}{y}'); args = cli.tokenize('{x}{y}');
assert.is(2, args.length); assert.is(2, args.length);
assert.is('x', args[0].text); assert.is('x', args[0].text);
assert.is('{', args[0].prefix); assert.is('{', args[0].prefix);
@ -118,21 +115,21 @@ exports.testJavascript = function(options) {
assert.is('}', args[1].suffix); assert.is('}', args[1].suffix);
assert.is('ScriptArgument', args[1].type); assert.is('ScriptArgument', args[1].type);
args = requ._tokenize('{'); args = cli.tokenize('{');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('', args[0].text); assert.is('', args[0].text);
assert.is('{', args[0].prefix); assert.is('{', args[0].prefix);
assert.is('', args[0].suffix); assert.is('', args[0].suffix);
assert.is('ScriptArgument', args[0].type); assert.is('ScriptArgument', args[0].type);
args = requ._tokenize('{ '); args = cli.tokenize('{ ');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('', args[0].text); assert.is('', args[0].text);
assert.is('{ ', args[0].prefix); assert.is('{ ', args[0].prefix);
assert.is('', args[0].suffix); assert.is('', args[0].suffix);
assert.is('ScriptArgument', args[0].type); assert.is('ScriptArgument', args[0].type);
args = requ._tokenize('{x'); args = cli.tokenize('{x');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('x', args[0].text); assert.is('x', args[0].text);
assert.is('{', args[0].prefix); assert.is('{', args[0].prefix);
@ -142,30 +139,29 @@ exports.testJavascript = function(options) {
exports.testRegularNesting = function(options) { exports.testRegularNesting = function(options) {
var args; var args;
var requ = new Requisition();
args = requ._tokenize('{"x"}'); args = cli.tokenize('{"x"}');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('"x"', args[0].text); assert.is('"x"', args[0].text);
assert.is('{', args[0].prefix); assert.is('{', args[0].prefix);
assert.is('}', args[0].suffix); assert.is('}', args[0].suffix);
assert.is('ScriptArgument', args[0].type); assert.is('ScriptArgument', args[0].type);
args = requ._tokenize('{\'x\'}'); args = cli.tokenize('{\'x\'}');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('\'x\'', args[0].text); assert.is('\'x\'', args[0].text);
assert.is('{', args[0].prefix); assert.is('{', args[0].prefix);
assert.is('}', args[0].suffix); assert.is('}', args[0].suffix);
assert.is('ScriptArgument', args[0].type); assert.is('ScriptArgument', args[0].type);
args = requ._tokenize('"{x}"'); args = cli.tokenize('"{x}"');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('{x}', args[0].text); assert.is('{x}', args[0].text);
assert.is('"', args[0].prefix); assert.is('"', args[0].prefix);
assert.is('"', args[0].suffix); assert.is('"', args[0].suffix);
assert.is('Argument', args[0].type); assert.is('Argument', args[0].type);
args = requ._tokenize('\'{x}\''); args = cli.tokenize('\'{x}\'');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('{x}', args[0].text); assert.is('{x}', args[0].text);
assert.is('\'', args[0].prefix); assert.is('\'', args[0].prefix);
@ -175,23 +171,22 @@ exports.testRegularNesting = function(options) {
exports.testDeepNesting = function(options) { exports.testDeepNesting = function(options) {
var args; var args;
var requ = new Requisition();
args = requ._tokenize('{{}}'); args = cli.tokenize('{{}}');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('{}', args[0].text); assert.is('{}', args[0].text);
assert.is('{', args[0].prefix); assert.is('{', args[0].prefix);
assert.is('}', args[0].suffix); assert.is('}', args[0].suffix);
assert.is('ScriptArgument', args[0].type); assert.is('ScriptArgument', args[0].type);
args = requ._tokenize('{{x} {y}}'); args = cli.tokenize('{{x} {y}}');
assert.is(1, args.length); assert.is(1, args.length);
assert.is('{x} {y}', args[0].text); assert.is('{x} {y}', args[0].text);
assert.is('{', args[0].prefix); assert.is('{', args[0].prefix);
assert.is('}', args[0].suffix); assert.is('}', args[0].suffix);
assert.is('ScriptArgument', args[0].type); assert.is('ScriptArgument', args[0].type);
args = requ._tokenize('{{w} {{{x}}}} {y} {{{z}}}'); args = cli.tokenize('{{w} {{{x}}}} {y} {{{z}}}');
assert.is(3, args.length); assert.is(3, args.length);
@ -210,7 +205,7 @@ exports.testDeepNesting = function(options) {
assert.is('}', args[2].suffix); assert.is('}', args[2].suffix);
assert.is('ScriptArgument', args[2].type); assert.is('ScriptArgument', args[2].type);
args = requ._tokenize('{{w} {{{x}}} {y} {{{z}}}'); args = cli.tokenize('{{w} {{{x}}} {y} {{{z}}}');
assert.is(1, args.length); assert.is(1, args.length);
@ -222,10 +217,9 @@ exports.testDeepNesting = function(options) {
exports.testStrangeNesting = function(options) { exports.testStrangeNesting = function(options) {
var args; var args;
var requ = new Requisition();
// Note: When we get real JS parsing this should break // Note: When we get real JS parsing this should break
args = requ._tokenize('{"x}"}'); args = cli.tokenize('{"x}"}');
assert.is(2, args.length); assert.is(2, args.length);
@ -242,9 +236,8 @@ exports.testStrangeNesting = function(options) {
exports.testComplex = function(options) { exports.testComplex = function(options) {
var args; var args;
var requ = new Requisition();
args = requ._tokenize(' 1234 \'12 34\''); args = cli.tokenize(' 1234 \'12 34\'');
assert.is(2, args.length); assert.is(2, args.length);
@ -258,7 +251,7 @@ exports.testComplex = function(options) {
assert.is('\'', args[1].suffix); assert.is('\'', args[1].suffix);
assert.is('Argument', args[1].type); assert.is('Argument', args[1].type);
args = requ._tokenize('12\'34 "12 34" \\'); // 12'34 "12 34" \ args = cli.tokenize('12\'34 "12 34" \\'); // 12'34 "12 34" \
assert.is(3, args.length); assert.is(3, args.length);
@ -280,9 +273,8 @@ exports.testComplex = function(options) {
exports.testPathological = function(options) { exports.testPathological = function(options) {
var args; var args;
var requ = new Requisition();
args = requ._tokenize('a\\ b \\t\\n\\r \\\'x\\\" \'d'); // a_b \t\n\r \'x\" 'd args = cli.tokenize('a\\ b \\t\\n\\r \\\'x\\\" \'d'); // a_b \t\n\r \'x\" 'd
assert.is(4, args.length); assert.is(4, args.length);

Просмотреть файл

@ -49,15 +49,22 @@ function forEachType(options, typeSpec, callback) {
} }
else if (name === 'delegate') { else if (name === 'delegate') {
typeSpec.delegateType = function() { typeSpec.delegateType = function() {
return types.getType('string'); return types.createType('string');
}; };
} }
else if (name === 'array') { else if (name === 'array') {
typeSpec.subtype = 'string'; typeSpec.subtype = 'string';
} }
var type = types.getType(typeSpec); var type = types.createType(typeSpec);
callback(type); callback(type);
// Clean up
delete typeSpec.name;
delete typeSpec.requisition;
delete typeSpec.data;
delete typeSpec.delegateType;
delete typeSpec.subtype;
}); });
} }

Просмотреть файл

@ -21,11 +21,10 @@ this.EXPORTED_SYMBOLS = [ 'helpers' ];
var helpers = {}; var helpers = {};
this.helpers = helpers; this.helpers = helpers;
let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require; let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require;
Components.utils.import("resource:///modules/devtools/gcli.jsm", {}); Components.utils.import("resource://gre/modules/devtools/gcli.jsm", {});
let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console; let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
let devtools = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools; let TargetFactory = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools.TargetFactory;
let TargetFactory = devtools.TargetFactory;
let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise; let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise;
let assert = { ok: ok, is: is, log: info }; let assert = { ok: ok, is: is, log: info };
@ -158,6 +157,7 @@ helpers.runTests = function(options, tests) {
}); });
var recover = function(error) { var recover = function(error) {
ok(false, error);
console.error(error); console.error(error);
}; };
@ -341,7 +341,7 @@ helpers._createDebugCheck = function(options) {
output += ' current: \'' + helpers._actual.current(options) + '\',\n'; output += ' current: \'' + helpers._actual.current(options) + '\',\n';
output += ' status: \'' + helpers._actual.status(options) + '\',\n'; output += ' status: \'' + helpers._actual.status(options) + '\',\n';
output += ' options: ' + outputArray(helpers._actual.options(options)) + ',\n'; output += ' options: ' + outputArray(helpers._actual.options(options)) + ',\n';
output += ' error: \'' + helpers._actual.message(options) + '\',\n'; output += ' message: \'' + helpers._actual.message(options) + '\',\n';
output += ' predictions: ' + outputArray(predictions) + ',\n'; output += ' predictions: ' + outputArray(predictions) + ',\n';
output += ' unassigned: ' + outputArray(requisition._unassigned) + ',\n'; output += ' unassigned: ' + outputArray(requisition._unassigned) + ',\n';
output += ' outputState: \'' + helpers._actual.outputState(options) + '\',\n'; output += ' outputState: \'' + helpers._actual.outputState(options) + '\',\n';
@ -378,6 +378,8 @@ helpers._createDebugCheck = function(options) {
output += ' exec: {\n'; output += ' exec: {\n';
output += ' output: \'\',\n'; output += ' output: \'\',\n';
output += ' completed: true,\n'; output += ' completed: true,\n';
output += ' type: \'string\',\n';
output += ' error: false\n';
output += ' }\n'; output += ' }\n';
output += ' }\n'; output += ' }\n';
output += ']);'; output += ']);';
@ -702,7 +704,7 @@ helpers._check = function(options, name, checks) {
*/ */
helpers._exec = function(options, name, expected) { helpers._exec = function(options, name, expected) {
if (expected == null) { if (expected == null) {
return Promise.resolve(); return Promise.resolve({});
} }
var output = options.display.requisition.exec({ hidden: true }); var output = options.display.requisition.exec({ hidden: true });
@ -715,20 +717,32 @@ helpers._exec = function(options, name, expected) {
if (!options.window.document.createElement) { if (!options.window.document.createElement) {
assert.log('skipping output tests (missing doc.createElement) for ' + name); assert.log('skipping output tests (missing doc.createElement) for ' + name);
return Promise.resolve(); return Promise.resolve({ output: output });
} }
if (!('output' in expected)) { if (!('output' in expected)) {
return Promise.resolve(); return Promise.resolve({ output: output });
} }
var deferred = Promise.defer();
var checkOutput = function() { var checkOutput = function() {
var div = options.window.document.createElement('div'); var div = options.window.document.createElement('div');
var nodePromise = converters.convert(output.data, output.type, 'dom', var conversionContext = options.display.requisition.conversionContext;
options.display.requisition.context);
nodePromise.then(function(node) { if ('type' in expected) {
assert.is(output.type,
expected.type,
'output.type for: ' + name);
}
if ('error' in expected) {
assert.is(output.error,
expected.error,
'output.error for: ' + name);
}
var convertPromise = converters.convert(output.data, output.type, 'dom',
conversionContext);
return convertPromise.then(function(node) {
div.appendChild(node); div.appendChild(node);
var actualOutput = div.textContent.trim(); var actualOutput = div.textContent.trim();
@ -757,24 +771,11 @@ helpers._exec = function(options, name, expected) {
doTest(expected.output, actualOutput); doTest(expected.output, actualOutput);
} }
deferred.resolve(actualOutput); return { output: output, text: actualOutput };
}); });
}; };
if (output.completed !== false) { return output.promise.then(checkOutput, checkOutput);
checkOutput();
}
else {
var changed = function() {
if (output.completed !== false) {
checkOutput();
output.onChange.remove(changed);
}
};
output.onChange.add(changed);
}
return deferred.promise;
}; };
/** /**
@ -789,15 +790,15 @@ helpers._setup = function(options, name, action) {
return Promise.resolve(action()); return Promise.resolve(action());
} }
return Promise.reject('setup must be a string or a function'); return Promise.reject('\'setup\' property must be a string or a function. Is ' + action);
}; };
/** /**
* Helper to shutdown the test * Helper to shutdown the test
*/ */
helpers._post = function(name, action, output) { helpers._post = function(name, action, data) {
if (typeof action === 'function') { if (typeof action === 'function') {
return Promise.resolve(action(output)); return Promise.resolve(action(data.output, data.text));
} }
return Promise.resolve(action); return Promise.resolve(action);
}; };
@ -943,19 +944,22 @@ helpers.audit = function(options, audits) {
if (typeof chunkLen !== 'number') { if (typeof chunkLen !== 'number') {
chunkLen = 1; chunkLen = 1;
} }
var responseTime = (new Date().getTime() - start) / chunkLen;
totalResponseTime += responseTime; if (assert.currentTest) {
if (responseTime > maxResponseTime) { var responseTime = (new Date().getTime() - start) / chunkLen;
maxResponseTime = responseTime; totalResponseTime += responseTime;
maxResponseCulprit = assert.currentTest + '/' + name; if (responseTime > maxResponseTime) {
maxResponseTime = responseTime;
maxResponseCulprit = assert.currentTest + '/' + name;
}
averageOver++;
} }
averageOver++;
var checkDone = helpers._check(options, name, audit.check); var checkDone = helpers._check(options, name, audit.check);
return checkDone.then(function() { return checkDone.then(function() {
var execDone = helpers._exec(options, name, audit.exec); var execDone = helpers._exec(options, name, audit.exec);
return execDone.then(function(output) { return execDone.then(function(data) {
return helpers._post(name, audit.post, output).then(function() { return helpers._post(name, audit.post, data).then(function() {
if (assert.testLogging) { if (assert.testLogging) {
log('- END \'' + name + '\' in ' + assert.currentTest); log('- END \'' + name + '\' in ' + assert.currentTest);
} }
@ -963,9 +967,8 @@ helpers.audit = function(options, audits) {
}); });
}); });
}); });
}).then(null, function(ex) { }).then(function() {
console.error(ex.stack); return options.display.inputter.setInput('');
throw(ex);
}); });
}; };

Просмотреть файл

@ -23,133 +23,56 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components; const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
let { require: require, define: define } = Cu.import("resource://gre/modules/devtools/Require.jsm", {}); let { require: require, define: define } = Cu.import("resource://gre/modules/devtools/Require.jsm", {});
Cu.import("resource:///modules/devtools/gcli.jsm", {}); Cu.import("resource://gre/modules/devtools/gcli.jsm", {});
// <INJECTED SOURCE:END> // <INJECTED SOURCE:END>
var mockCommands = {}; var mockCommands = {};
// We use an alias for exports here because this module is used in Firefox
// mochitests where we don't have define/require
'use strict';
var util = require('util/util'); var util = require('util/util');
var canon = require('gcli/canon'); var canon = require('gcli/canon');
var types = require('gcli/types'); var types = require('gcli/types');
var SelectionType = require('gcli/types/selection').SelectionType;
var DelegateType = require('gcli/types/basic').DelegateType;
mockCommands.option1 = { };
mockCommands.option2 = { };
mockCommands.option3 = { };
/** mockCommands.optionType = {
* Registration and de-registration.
*/
mockCommands.setup = function(opts) {
// setup/shutdown needs to register/unregister types, however that means we
// need to re-initialize mockCommands.option1 and mockCommands.option2 with
// the actual types
mockCommands.option1.type = types.getType('string');
mockCommands.option2.type = types.getType('number');
types.registerType(mockCommands.optionType);
types.registerType(mockCommands.optionValue);
canon.addCommand(mockCommands.tsv);
canon.addCommand(mockCommands.tsr);
canon.addCommand(mockCommands.tso);
canon.addCommand(mockCommands.tse);
canon.addCommand(mockCommands.tsj);
canon.addCommand(mockCommands.tsb);
canon.addCommand(mockCommands.tss);
canon.addCommand(mockCommands.tsu);
canon.addCommand(mockCommands.tsf);
canon.addCommand(mockCommands.tsn);
canon.addCommand(mockCommands.tsnDif);
canon.addCommand(mockCommands.tsnExt);
canon.addCommand(mockCommands.tsnExte);
canon.addCommand(mockCommands.tsnExten);
canon.addCommand(mockCommands.tsnExtend);
canon.addCommand(mockCommands.tsnDeep);
canon.addCommand(mockCommands.tsnDeepDown);
canon.addCommand(mockCommands.tsnDeepDownNested);
canon.addCommand(mockCommands.tsnDeepDownNestedCmd);
canon.addCommand(mockCommands.tselarr);
canon.addCommand(mockCommands.tsm);
canon.addCommand(mockCommands.tsg);
canon.addCommand(mockCommands.tshidden);
canon.addCommand(mockCommands.tscook);
canon.addCommand(mockCommands.tslong);
};
mockCommands.shutdown = function(opts) {
canon.removeCommand(mockCommands.tsv);
canon.removeCommand(mockCommands.tsr);
canon.removeCommand(mockCommands.tso);
canon.removeCommand(mockCommands.tse);
canon.removeCommand(mockCommands.tsj);
canon.removeCommand(mockCommands.tsb);
canon.removeCommand(mockCommands.tss);
canon.removeCommand(mockCommands.tsu);
canon.removeCommand(mockCommands.tsf);
canon.removeCommand(mockCommands.tsn);
canon.removeCommand(mockCommands.tsnDif);
canon.removeCommand(mockCommands.tsnExt);
canon.removeCommand(mockCommands.tsnExte);
canon.removeCommand(mockCommands.tsnExten);
canon.removeCommand(mockCommands.tsnExtend);
canon.removeCommand(mockCommands.tsnDeep);
canon.removeCommand(mockCommands.tsnDeepDown);
canon.removeCommand(mockCommands.tsnDeepDownNested);
canon.removeCommand(mockCommands.tsnDeepDownNestedCmd);
canon.removeCommand(mockCommands.tselarr);
canon.removeCommand(mockCommands.tsm);
canon.removeCommand(mockCommands.tsg);
canon.removeCommand(mockCommands.tshidden);
canon.removeCommand(mockCommands.tscook);
canon.removeCommand(mockCommands.tslong);
types.deregisterType(mockCommands.optionType);
types.deregisterType(mockCommands.optionValue);
};
mockCommands.option1 = { type: types.getType('string') };
mockCommands.option2 = { type: types.getType('number') };
mockCommands.option3 = { type: types.getType({
name: 'selection',
lookup: [
{ name: 'one', value: 1 },
{ name: 'two', value: 2 },
{ name: 'three', value: 3 }
]
})};
mockCommands.optionType = new SelectionType({
name: 'optionType', name: 'optionType',
parent: 'selection',
lookup: [ lookup: [
{ name: 'option1', value: mockCommands.option1 }, { name: 'option1', value: mockCommands.option1 },
{ name: 'option2', value: mockCommands.option2 }, { name: 'option2', value: mockCommands.option2 },
{ name: 'option3', value: mockCommands.option3 } { name: 'option3', value: mockCommands.option3 }
] ]
}); };
mockCommands.optionValue = new DelegateType({ mockCommands.optionValue = {
name: 'optionValue', name: 'optionValue',
delegateType: function(context) { parent: 'delegate',
if (context != null) { delegateType: function(executionContext) {
var option = context.getArgsObject().optionType; if (executionContext != null) {
var option = executionContext.getArgsObject().optionType;
if (option != null) { if (option != null) {
return option.type; return option.type;
} }
} }
return types.getType('blank'); return types.createType('blank');
} }
}); };
mockCommands.onCommandExec = util.createEvent('commands.onCommandExec'); mockCommands.onCommandExec = util.createEvent('commands.onCommandExec');
function createExec(name) { function createExec(name) {
return function(args, context) { return function(args, executionContext) {
var data = { var data = {
command: mockCommands[name],
args: args, args: args,
context: context context: executionContext
}; };
mockCommands.onCommandExec(data); mockCommands.onCommandExec(data);
var argsOut = Object.keys(args).map(function(key) { var argsOut = Object.keys(args).map(function(key) {
@ -159,7 +82,7 @@ function createExec(name) {
}; };
} }
mockCommands.tsv = { var tsv = {
name: 'tsv', name: 'tsv',
params: [ params: [
{ name: 'optionType', type: 'optionType' }, { name: 'optionType', type: 'optionType' },
@ -168,19 +91,19 @@ mockCommands.tsv = {
exec: createExec('tsv') exec: createExec('tsv')
}; };
mockCommands.tsr = { var tsr = {
name: 'tsr', name: 'tsr',
params: [ { name: 'text', type: 'string' } ], params: [ { name: 'text', type: 'string' } ],
exec: createExec('tsr') exec: createExec('tsr')
}; };
mockCommands.tso = { var tso = {
name: 'tso', name: 'tso',
params: [ { name: 'text', type: 'string', defaultValue: null } ], params: [ { name: 'text', type: 'string', defaultValue: null } ],
exec: createExec('tso') exec: createExec('tso')
}; };
mockCommands.tse = { var tse = {
name: 'tse', name: 'tse',
params: [ params: [
{ name: 'node', type: 'node' }, { name: 'node', type: 'node' },
@ -195,88 +118,88 @@ mockCommands.tse = {
exec: createExec('tse') exec: createExec('tse')
}; };
mockCommands.tsj = { var tsj = {
name: 'tsj', name: 'tsj',
params: [ { name: 'javascript', type: 'javascript' } ], params: [ { name: 'javascript', type: 'javascript' } ],
exec: createExec('tsj') exec: createExec('tsj')
}; };
mockCommands.tsb = { var tsb = {
name: 'tsb', name: 'tsb',
params: [ { name: 'toggle', type: 'boolean' } ], params: [ { name: 'toggle', type: 'boolean' } ],
exec: createExec('tsb') exec: createExec('tsb')
}; };
mockCommands.tss = { var tss = {
name: 'tss', name: 'tss',
exec: createExec('tss') exec: createExec('tss')
}; };
mockCommands.tsu = { var tsu = {
name: 'tsu', name: 'tsu',
params: [ { name: 'num', type: { name: 'number', max: 10, min: -5, step: 3 } } ], params: [ { name: 'num', type: { name: 'number', max: 10, min: -5, step: 3 } } ],
exec: createExec('tsu') exec: createExec('tsu')
}; };
mockCommands.tsf = { var tsf = {
name: 'tsf', name: 'tsf',
params: [ { name: 'num', type: { name: 'number', allowFloat: true, max: 11.5, min: -6.5, step: 1.5 } } ], params: [ { name: 'num', type: { name: 'number', allowFloat: true, max: 11.5, min: -6.5, step: 1.5 } } ],
exec: createExec('tsf') exec: createExec('tsf')
}; };
mockCommands.tsn = { var tsn = {
name: 'tsn' name: 'tsn'
}; };
mockCommands.tsnDif = { var tsnDif = {
name: 'tsn dif', name: 'tsn dif',
description: 'tsn dif', description: 'tsn dif',
params: [ { name: 'text', type: 'string', description: 'tsn dif text' } ], params: [ { name: 'text', type: 'string', description: 'tsn dif text' } ],
exec: createExec('tsnDif') exec: createExec('tsnDif')
}; };
mockCommands.tsnExt = { var tsnExt = {
name: 'tsn ext', name: 'tsn ext',
params: [ { name: 'text', type: 'string' } ], params: [ { name: 'text', type: 'string' } ],
exec: createExec('tsnExt') exec: createExec('tsnExt')
}; };
mockCommands.tsnExte = { var tsnExte = {
name: 'tsn exte', name: 'tsn exte',
params: [ { name: 'text', type: 'string' } ], params: [ { name: 'text', type: 'string' } ],
exec: createExec('tsnExte') exec: createExec('tsnExte')
}; };
mockCommands.tsnExten = { var tsnExten = {
name: 'tsn exten', name: 'tsn exten',
params: [ { name: 'text', type: 'string' } ], params: [ { name: 'text', type: 'string' } ],
exec: createExec('tsnExten') exec: createExec('tsnExten')
}; };
mockCommands.tsnExtend = { var tsnExtend = {
name: 'tsn extend', name: 'tsn extend',
params: [ { name: 'text', type: 'string' } ], params: [ { name: 'text', type: 'string' } ],
exec: createExec('tsnExtend') exec: createExec('tsnExtend')
}; };
mockCommands.tsnDeep = { var tsnDeep = {
name: 'tsn deep' name: 'tsn deep'
}; };
mockCommands.tsnDeepDown = { var tsnDeepDown = {
name: 'tsn deep down' name: 'tsn deep down'
}; };
mockCommands.tsnDeepDownNested = { var tsnDeepDownNested = {
name: 'tsn deep down nested' name: 'tsn deep down nested'
}; };
mockCommands.tsnDeepDownNestedCmd = { var tsnDeepDownNestedCmd = {
name: 'tsn deep down nested cmd', name: 'tsn deep down nested cmd',
exec: createExec('tsnDeepDownNestedCmd') exec: createExec('tsnDeepDownNestedCmd')
}; };
mockCommands.tshidden = { var tshidden = {
name: 'tshidden', name: 'tshidden',
hidden: true, hidden: true,
params: [ params: [
@ -308,7 +231,7 @@ mockCommands.tshidden = {
exec: createExec('tshidden') exec: createExec('tshidden')
}; };
mockCommands.tselarr = { var tselarr = {
name: 'tselarr', name: 'tselarr',
params: [ params: [
{ name: 'num', type: { name: 'selection', data: [ '1', '2', '3' ] } }, { name: 'num', type: { name: 'selection', data: [ '1', '2', '3' ] } },
@ -317,7 +240,7 @@ mockCommands.tselarr = {
exec: createExec('tselarr') exec: createExec('tselarr')
}; };
mockCommands.tsm = { var tsm = {
name: 'tsm', name: 'tsm',
description: 'a 3-param test selection|string|number', description: 'a 3-param test selection|string|number',
params: [ params: [
@ -328,7 +251,7 @@ mockCommands.tsm = {
exec: createExec('tsm') exec: createExec('tsm')
}; };
mockCommands.tsg = { var tsg = {
name: 'tsg', name: 'tsg',
description: 'a param group test', description: 'a param group test',
params: [ params: [
@ -371,7 +294,7 @@ mockCommands.tsg = {
exec: createExec('tsg') exec: createExec('tsg')
}; };
mockCommands.tscook = { var tscook = {
name: 'tscook', name: 'tscook',
description: 'param group test to catch problems with cookie command', description: 'param group test to catch problems with cookie command',
params: [ params: [
@ -411,10 +334,9 @@ mockCommands.tscook = {
exec: createExec('tscook') exec: createExec('tscook')
}; };
mockCommands.tslong = { var tslong = {
name: 'tslong', name: 'tslong',
description: 'long param tests to catch problems with the jsb command', description: 'long param tests to catch problems with the jsb command',
returnValue:'string',
params: [ params: [
{ {
name: 'msg', name: 'msg',
@ -473,5 +395,146 @@ mockCommands.tslong = {
exec: createExec('tslong') exec: createExec('tslong')
}; };
var tsfail = {
name: 'tsfail',
description: 'test errors',
params: [
{
name: 'method',
type: {
name: 'selection',
data: [
'reject', 'rejecttyped',
'throwerror', 'throwstring', 'throwinpromise',
'noerror'
]
}
}
],
exec: function(args, context) {
if (args.method === 'reject') {
var deferred = context.defer();
setTimeout(function() {
deferred.reject('rejected promise');
}, 10);
return deferred.promise;
}
if (args.method === 'rejecttyped') {
var deferred = context.defer();
setTimeout(function() {
deferred.reject(context.typedData('number', 54));
}, 10);
return deferred.promise;
}
if (args.method === 'throwinpromise') {
var deferred = context.defer();
setTimeout(function() {
deferred.resolve('should be lost');
}, 10);
return deferred.promise.then(function() {
var t = null;
return t.foo;
});
}
if (args.method === 'throwerror') {
throw new Error('thrown error');
}
if (args.method === 'throwstring') {
throw 'thrown string';
}
return 'no error';
}
};
mockCommands.commands = {};
/**
* Registration and de-registration.
*/
mockCommands.setup = function(opts) {
// setup/shutdown needs to register/unregister types, however that means we
// need to re-initialize mockCommands.option1 and mockCommands.option2 with
// the actual types
mockCommands.option1.type = types.createType('string');
mockCommands.option2.type = types.createType('number');
mockCommands.option3.type = types.createType({
name: 'selection',
lookup: [
{ name: 'one', value: 1 },
{ name: 'two', value: 2 },
{ name: 'three', value: 3 }
]
});
types.addType(mockCommands.optionType);
types.addType(mockCommands.optionValue);
mockCommands.commands.tsv = canon.addCommand(tsv);
mockCommands.commands.tsr = canon.addCommand(tsr);
mockCommands.commands.tso = canon.addCommand(tso);
mockCommands.commands.tse = canon.addCommand(tse);
mockCommands.commands.tsj = canon.addCommand(tsj);
mockCommands.commands.tsb = canon.addCommand(tsb);
mockCommands.commands.tss = canon.addCommand(tss);
mockCommands.commands.tsu = canon.addCommand(tsu);
mockCommands.commands.tsf = canon.addCommand(tsf);
mockCommands.commands.tsn = canon.addCommand(tsn);
mockCommands.commands.tsnDif = canon.addCommand(tsnDif);
mockCommands.commands.tsnExt = canon.addCommand(tsnExt);
mockCommands.commands.tsnExte = canon.addCommand(tsnExte);
mockCommands.commands.tsnExten = canon.addCommand(tsnExten);
mockCommands.commands.tsnExtend = canon.addCommand(tsnExtend);
mockCommands.commands.tsnDeep = canon.addCommand(tsnDeep);
mockCommands.commands.tsnDeepDown = canon.addCommand(tsnDeepDown);
mockCommands.commands.tsnDeepDownNested = canon.addCommand(tsnDeepDownNested);
mockCommands.commands.tsnDeepDownNestedCmd = canon.addCommand(tsnDeepDownNestedCmd);
mockCommands.commands.tselarr = canon.addCommand(tselarr);
mockCommands.commands.tsm = canon.addCommand(tsm);
mockCommands.commands.tsg = canon.addCommand(tsg);
mockCommands.commands.tshidden = canon.addCommand(tshidden);
mockCommands.commands.tscook = canon.addCommand(tscook);
mockCommands.commands.tslong = canon.addCommand(tslong);
mockCommands.commands.tsfail = canon.addCommand(tsfail);
};
mockCommands.shutdown = function(opts) {
canon.removeCommand(tsv);
canon.removeCommand(tsr);
canon.removeCommand(tso);
canon.removeCommand(tse);
canon.removeCommand(tsj);
canon.removeCommand(tsb);
canon.removeCommand(tss);
canon.removeCommand(tsu);
canon.removeCommand(tsf);
canon.removeCommand(tsn);
canon.removeCommand(tsnDif);
canon.removeCommand(tsnExt);
canon.removeCommand(tsnExte);
canon.removeCommand(tsnExten);
canon.removeCommand(tsnExtend);
canon.removeCommand(tsnDeep);
canon.removeCommand(tsnDeepDown);
canon.removeCommand(tsnDeepDownNested);
canon.removeCommand(tsnDeepDownNestedCmd);
canon.removeCommand(tselarr);
canon.removeCommand(tsm);
canon.removeCommand(tsg);
canon.removeCommand(tshidden);
canon.removeCommand(tscook);
canon.removeCommand(tslong);
canon.removeCommand(tsfail);
types.removeType(mockCommands.optionType);
types.removeType(mockCommands.optionValue);
mockCommands.commands = {};
};
// }); // });

Просмотреть файл

@ -5,7 +5,7 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components; const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
this.EXPORTED_SYMBOLS = [ ]; this.EXPORTED_SYMBOLS = [ ];
Cu.import("resource:///modules/devtools/gcli.jsm"); Cu.import("resource://gre/modules/devtools/gcli.jsm");
Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
@ -157,7 +157,7 @@ gcli.addCommand({
description: gcli.lookup("breakaddlineLineDesc") description: gcli.lookup("breakaddlineLineDesc")
} }
], ],
returnType: "html", returnType: "string",
exec: function(args, context) { exec: function(args, context) {
args.type = "line"; args.type = "line";
@ -201,7 +201,7 @@ gcli.addCommand({
description: gcli.lookup("breakdelBreakidDesc") description: gcli.lookup("breakdelBreakidDesc")
} }
], ],
returnType: "html", returnType: "string",
exec: function(args, context) { exec: function(args, context) {
let dbg = getPanel(context, "jsdebugger"); let dbg = getPanel(context, "jsdebugger");
if (!dbg) { if (!dbg) {
@ -381,7 +381,7 @@ gcli.addCommand({
name: "dbg list", name: "dbg list",
description: gcli.lookup("dbgListSourcesDesc"), description: gcli.lookup("dbgListSourcesDesc"),
params: [], params: [],
returnType: "html", returnType: "dom",
exec: function(args, context) { exec: function(args, context) {
let dbg = getPanel(context, "jsdebugger"); let dbg = getPanel(context, "jsdebugger");
let doc = context.environment.chromeDocument; let doc = context.environment.chromeDocument;

Просмотреть файл

@ -797,7 +797,9 @@ StackFrames.prototype = {
case "object": case "object":
// Add nodes for every variable in scope. // Add nodes for every variable in scope.
this.activeThread.pauseGrip(env.object).getPrototypeAndProperties(function(aResponse) { this.activeThread.pauseGrip(env.object).getPrototypeAndProperties(function(aResponse) {
this._insertScopeVariables(aResponse.ownProperties, aScope); let { ownProperties, safeGetterValues } = aResponse;
this._mergeSafeGetterValues(ownProperties, safeGetterValues);
this._insertScopeVariables(ownProperties, aScope);
// Signal that variables have been fetched. // Signal that variables have been fetched.
window.dispatchEvent(document, "Debugger:FetchedVariables"); window.dispatchEvent(document, "Debugger:FetchedVariables");
@ -903,9 +905,11 @@ StackFrames.prototype = {
let grip = aVar._sourceGrip; let grip = aVar._sourceGrip;
this.activeThread.pauseGrip(grip).getPrototypeAndProperties(function(aResponse) { this.activeThread.pauseGrip(grip).getPrototypeAndProperties(function(aResponse) {
let { ownProperties, prototype } = aResponse; let { ownProperties, prototype, safeGetterValues } = aResponse;
let sortable = VariablesView.NON_SORTABLE_CLASSES.indexOf(grip.class) == -1; let sortable = VariablesView.NON_SORTABLE_CLASSES.indexOf(grip.class) == -1;
this._mergeSafeGetterValues(ownProperties, safeGetterValues);
// Add all the variable properties. // Add all the variable properties.
if (ownProperties) { if (ownProperties) {
aVar.addProperties(ownProperties, { aVar.addProperties(ownProperties, {
@ -932,6 +936,33 @@ StackFrames.prototype = {
}.bind(this)); }.bind(this));
}, },
/**
* Merge the safe getter values descriptors into the "own properties" object
* that comes from a "prototypeAndProperties" response packet. This is needed
* for Variables View.
*
* @private
* @param object aOwnProperties
* The |ownProperties| object that will get the new safe getter values.
* @param object aSafeGetterValues
* The |safeGetterValues| object.
*/
_mergeSafeGetterValues:
function SF__mergeSafeGetterValues(aOwnProperties, aSafeGetterValues) {
// Merge the safe getter values into one object such that we can use it
// in VariablesView.
for (let name of Object.keys(aSafeGetterValues)) {
if (name in aOwnProperties) {
aOwnProperties[name].getterValue = aSafeGetterValues[name].getterValue;
aOwnProperties[name].getterPrototypeLevel = aSafeGetterValues[name]
.getterPrototypeLevel;
}
else {
aOwnProperties[name] = aSafeGetterValues[name];
}
}
},
/** /**
* Adds the specified stack frame to the list. * Adds the specified stack frame to the list.
* *

Просмотреть файл

@ -406,7 +406,6 @@ create({ constructor: StackFramesView, proto: MenuContainer.prototype }, {
// Append a stack frame item to this container. // Append a stack frame item to this container.
let stackframeItem = this.push(frameView, { let stackframeItem = this.push(frameView, {
index: 0, /* specifies on which position should the item be appended */ index: 0, /* specifies on which position should the item be appended */
relaxed: true, /* this container should allow dupes & degenerates */
attachment: { attachment: {
popup: menuEntry, popup: menuEntry,
depth: aDepth depth: aDepth
@ -946,7 +945,7 @@ FilterView.prototype = {
view.node.hideEmptyGroups(); view.node.hideEmptyGroups();
// Ensure the currently selected item is visible. // Ensure the currently selected item is visible.
view.node.ensureSelectionIsVisible(true); view.node.ensureSelectionIsVisible({ withGroup: true });
// Remember the previously searched file to avoid redundant filtering. // Remember the previously searched file to avoid redundant filtering.
this._prevSearchedFile = aFile; this._prevSearchedFile = aFile;

Просмотреть файл

@ -56,10 +56,13 @@ function testFrameParameters()
is(globalNodes[1].querySelector(".value").getAttribute("value"), "[object Object]", is(globalNodes[1].querySelector(".value").getAttribute("value"), "[object Object]",
"Should have the right property value for |SpecialPowers|."); "Should have the right property value for |SpecialPowers|.");
is(globalNodes[3].querySelector(".name").getAttribute("value"), "document", let globalScopeObject = gDebugger.DebuggerView.Variables.getScopeForNode(globalScope);
let documentNode = globalScopeObject.get("document");
is(documentNode.target.querySelector(".name").getAttribute("value"), "document",
"Should have the right property name for |document|."); "Should have the right property name for |document|.");
is(globalNodes[3].querySelector(".value").getAttribute("value"), "[object HTMLDocument]", is(documentNode.target.querySelector(".value").getAttribute("value"), "[object HTMLDocument]",
"Should have the right property value for |document|."); "Should have the right property value for |document|.");
let len = globalNodes.length - 1; let len = globalNodes.length - 1;

Просмотреть файл

@ -61,10 +61,13 @@ function testWithFrame()
is(innerNodes[1].querySelector(".value").getAttribute("value"), "1", is(innerNodes[1].querySelector(".value").getAttribute("value"), "1",
"Should have the right property value for |one|."); "Should have the right property value for |one|.");
is(globalNodes[3].querySelector(".name").getAttribute("value"), "document", let globalScopeObject = gDebugger.DebuggerView.Variables.getScopeForNode(globalScope);
let documentNode = globalScopeObject.get("document");
is(documentNode.target.querySelector(".name").getAttribute("value"), "document",
"Should have the right property name for |document|."); "Should have the right property name for |document|.");
is(globalNodes[3].querySelector(".value").getAttribute("value"), "[object HTMLDocument]", is(documentNode.target.querySelector(".value").getAttribute("value"), "[object HTMLDocument]",
"Should have the right property value for |document|."); "Should have the right property value for |document|.");
let len = globalNodes.length - 1; let len = globalNodes.length - 1;

Просмотреть файл

@ -61,15 +61,17 @@ function testFrameParameters()
is(anonymousNodes[2].querySelector(".value").getAttribute("value"), "[object Object]", is(anonymousNodes[2].querySelector(".value").getAttribute("value"), "[object Object]",
"Should have the right property value for |buttonAsProto|."); "Should have the right property value for |buttonAsProto|.");
is(globalNodes[3].querySelector(".name").getAttribute("value"), "document", let globalScopeObject = gVars.getScopeForNode(globalScope);
let documentNode = globalScopeObject.get("document");
is(documentNode.target.querySelector(".name").getAttribute("value"), "document",
"Should have the right property name for |document|."); "Should have the right property name for |document|.");
is(globalNodes[3].querySelector(".value").getAttribute("value"), "[object HTMLDocument]", is(documentNode.target.querySelector(".value").getAttribute("value"), "[object HTMLDocument]",
"Should have the right property value for |document|."); "Should have the right property value for |document|.");
let buttonNode = gVars.getItemForNode(anonymousNodes[1]); let buttonNode = gVars.getItemForNode(anonymousNodes[1]);
let buttonAsProtoNode = gVars.getItemForNode(anonymousNodes[2]); let buttonAsProtoNode = gVars.getItemForNode(anonymousNodes[2]);
let documentNode = gVars.getItemForNode(globalNodes[3]);
is(buttonNode.expanded, false, is(buttonNode.expanded, false,
"The buttonNode should not be expanded at this point."); "The buttonNode should not be expanded at this point.");
@ -131,30 +133,46 @@ function testFrameParameters()
.getAttribute("value").search(/object/) != -1, .getAttribute("value").search(/object/) != -1,
"'__proto__' in documentNode should be an object."); "'__proto__' in documentNode should be an object.");
let buttonProtoNode = buttonNode.get("__proto__"); // Now the main course: make sure that the native getters for WebIDL
let buttonAsProtoProtoNode = buttonAsProtoNode.get("__proto__"); // attributes have been called and a value has been returned.
let documentProtoNode = documentNode.get("__proto__"); is(buttonNode.get("type").target.querySelector(".name")
.getAttribute("value"), "type",
"Should have the right property name for 'type' in buttonProtoNode.");
is(buttonNode.get("type").target.querySelector(".value")
.getAttribute("value"), '"submit"',
"'type' in buttonProtoNode should have the right value.");
is(buttonNode.get("formMethod").target.querySelector(".name")
.getAttribute("value"), "formMethod",
"Should have the right property name for 'formMethod' in buttonProtoNode.");
is(buttonNode.get("formMethod").target.querySelector(".value")
.getAttribute("value"), '""',
"'formMethod' in buttonProtoNode should have the right value.");
is(documentNode.get("domain").target.querySelector(".name")
.getAttribute("value"), "domain",
"Should have the right property name for 'domain' in documentProtoNode.");
is(documentNode.get("domain").target.querySelector(".value")
.getAttribute("value"), '"example.com"',
"'domain' in documentProtoNode should have the right value.");
is(documentNode.get("cookie").target.querySelector(".name")
.getAttribute("value"), "cookie",
"Should have the right property name for 'cookie' in documentProtoNode.");
is(documentNode.get("cookie").target.querySelector(".value")
.getAttribute("value"), '""',
"'cookie' in documentProtoNode should have the right value.");
let buttonAsProtoProtoNode = buttonAsProtoNode.get("__proto__");
is(buttonProtoNode.expanded, false,
"The buttonProtoNode should not be expanded at this point.");
is(buttonAsProtoProtoNode.expanded, false, is(buttonAsProtoProtoNode.expanded, false,
"The buttonAsProtoProtoNode should not be expanded at this point."); "The buttonAsProtoProtoNode should not be expanded at this point.");
is(documentProtoNode.expanded, false,
"The documentProtoNode should not be expanded at this point.");
// Expand the prototypes of 'button', 'buttonAsProto' and 'document' // Expand the prototypes of 'button', 'buttonAsProto' and 'document'
// tree nodes. This causes their properties to be retrieved and // tree nodes. This causes their properties to be retrieved and
// displayed. // displayed.
buttonProtoNode.expand();
buttonAsProtoProtoNode.expand(); buttonAsProtoProtoNode.expand();
documentProtoNode.expand();
is(buttonProtoNode.expanded, true,
"The buttonProtoNode should be expanded at this point.");
is(buttonAsProtoProtoNode.expanded, true, is(buttonAsProtoProtoNode.expanded, true,
"The buttonAsProtoProtoNode should be expanded at this point."); "The buttonAsProtoProtoNode should be expanded at this point.");
is(documentProtoNode.expanded, true,
"The documentProtoNode should be expanded at this point.");
// Poll every few milliseconds until the properties are retrieved. // Poll every few milliseconds until the properties are retrieved.
@ -168,86 +186,27 @@ function testFrameParameters()
window.clearInterval(intervalID1); window.clearInterval(intervalID1);
return resumeAndFinish(); return resumeAndFinish();
} }
if (!buttonProtoNode._retrieved || if (!buttonAsProtoProtoNode._retrieved) {
!buttonAsProtoProtoNode._retrieved ||
!documentProtoNode._retrieved) {
return; return;
} }
window.clearInterval(intervalID1); window.clearInterval(intervalID1);
// Now the main course: make sure that the native getters for WebIDL // Test this more involved case that reuses an object that is
// attributes have been called and a value has been returned. // present in another cache line.
is(buttonProtoNode.get("type").target.querySelector(".name") is(buttonAsProtoProtoNode.get("type").target.querySelector(".name")
.getAttribute("value"), "type", .getAttribute("value"), "type",
"Should have the right property name for 'type' in buttonProtoNode."); "Should have the right property name for 'type' in buttonAsProtoProtoProtoNode.");
is(buttonProtoNode.get("type").target.querySelector(".value") is(buttonAsProtoProtoNode.get("type").target.querySelector(".value")
.getAttribute("value"), '"submit"', .getAttribute("value"), '"submit"',
"'type' in buttonProtoNode should have the right value."); "'type' in buttonAsProtoProtoProtoNode should have the right value.");
is(buttonProtoNode.get("formMethod").target.querySelector(".name") is(buttonAsProtoProtoNode.get("formMethod").target.querySelector(".name")
.getAttribute("value"), "formMethod", .getAttribute("value"), "formMethod",
"Should have the right property name for 'formMethod' in buttonProtoNode."); "Should have the right property name for 'formMethod' in buttonAsProtoProtoProtoNode.");
is(buttonProtoNode.get("formMethod").target.querySelector(".value") is(buttonAsProtoProtoNode.get("formMethod").target.querySelector(".value")
.getAttribute("value"), '""', .getAttribute("value"), '""',
"'formMethod' in buttonProtoNode should have the right value."); "'formMethod' in buttonAsProtoProtoProtoNode should have the right value.");
is(documentProtoNode.get("domain").target.querySelector(".name") resumeAndFinish();
.getAttribute("value"), "domain",
"Should have the right property name for 'domain' in documentProtoNode.");
is(documentProtoNode.get("domain").target.querySelector(".value")
.getAttribute("value"), '"example.com"',
"'domain' in documentProtoNode should have the right value.");
is(documentProtoNode.get("cookie").target.querySelector(".name")
.getAttribute("value"), "cookie",
"Should have the right property name for 'cookie' in documentProtoNode.");
is(documentProtoNode.get("cookie").target.querySelector(".value")
.getAttribute("value"), '""',
"'cookie' in documentProtoNode should have the right value.");
let buttonAsProtoProtoProtoNode = buttonAsProtoProtoNode.get("__proto__");
is(buttonAsProtoProtoProtoNode.expanded, false,
"The buttonAsProtoProtoProtoNode should not be expanded at this point.");
// Expand the prototype of the prototype of 'buttonAsProto' tree
// node. This causes its properties to be retrieved and displayed.
buttonAsProtoProtoProtoNode.expand();
is(buttonAsProtoProtoProtoNode.expanded, true,
"The buttonAsProtoProtoProtoNode should be expanded at this point.");
// Poll every few milliseconds until the properties are retrieved.
// It's important to set the timer in the chrome window, because the
// content window timers are disabled while the debuggee is paused.
let count3 = 0;
let intervalID2 = window.setInterval(function(){
info("count3: " + count3);
if (++count3 > 50) {
ok(false, "Timed out while polling for the properties.");
window.clearInterval(intervalID2);
return resumeAndFinish();
}
if (!buttonAsProtoProtoProtoNode._retrieved) {
return;
}
window.clearInterval(intervalID2);
// Test this more involved case that reuses an object that is
// present in another cache line.
is(buttonAsProtoProtoProtoNode.get("type").target.querySelector(".name")
.getAttribute("value"), "type",
"Should have the right property name for 'type' in buttonAsProtoProtoProtoNode.");
is(buttonAsProtoProtoProtoNode.get("type").target.querySelector(".value")
.getAttribute("value"), '"submit"',
"'type' in buttonAsProtoProtoProtoNode should have the right value.");
is(buttonAsProtoProtoProtoNode.get("formMethod").target.querySelector(".name")
.getAttribute("value"), "formMethod",
"Should have the right property name for 'formMethod' in buttonAsProtoProtoProtoNode.");
is(buttonAsProtoProtoProtoNode.get("formMethod").target.querySelector(".value")
.getAttribute("value"), '""',
"'formMethod' in buttonAsProtoProtoProtoNode should have the right value.");
resumeAndFinish();
}, 100);
}, 100); }, 100);
}, 100); }, 100);
}}, 0); }}, 0);

Просмотреть файл

@ -149,11 +149,11 @@ function testVariablesFiltering()
"There should be 0 variables displayed in the test scope"); "There should be 0 variables displayed in the test scope");
is(loadScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0, is(loadScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
"There should be 0 variables displayed in the load scope"); "There should be 0 variables displayed in the load scope");
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 1, is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 3,
"There should be 1 variable displayed in the global scope"); "There should be 3 variables displayed in the global scope");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length, 6, ok(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length > 6,
"There should be 6 properties displayed in the inner scope"); "There should be more than 6 properties displayed in the inner scope");
is(mathScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, is(mathScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
"There should be 0 properties displayed in the math scope"); "There should be 0 properties displayed in the math scope");
is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
@ -165,21 +165,19 @@ function testVariablesFiltering()
is(innerScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"), is(innerScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
"this", "The only inner variable displayed should be 'this'"); "this", "The only inner variable displayed should be 'this'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[0].getAttribute("value"),
"window", "The first inner property displayed should be 'window'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[1].getAttribute("value"),
"document", "The second inner property displayed should be 'document'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"), is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"),
"location", "The third inner property displayed should be 'location'"); "window", "The third inner property displayed should be 'window'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[3].getAttribute("value"), is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[3].getAttribute("value"),
"__proto__", "The fourth inner property displayed should be '__proto__'"); "document", "The fourth inner property displayed should be 'document'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[4].getAttribute("value"), is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[4].getAttribute("value"),
"Location", "The fifth inner property displayed should be 'Location'"); "location", "The fifth inner property displayed should be 'location'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[5].getAttribute("value"),
"Location", "The sixth inner property displayed should be 'Location'");
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"), is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
"Location", "The only global variable displayed should be 'Location'"); "location", "The first global variable displayed should be 'location'");
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[1].getAttribute("value"),
"locationbar", "The second global variable displayed should be 'locationbar'");
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[2].getAttribute("value"),
"Location", "The third global variable displayed should be 'Location'");
} }
function test2() function test2()
@ -237,11 +235,11 @@ function testVariablesFiltering()
"There should be 0 variables displayed in the test scope"); "There should be 0 variables displayed in the test scope");
is(loadScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0, is(loadScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
"There should be 0 variables displayed in the load scope"); "There should be 0 variables displayed in the load scope");
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 1, is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 3,
"There should be 1 variable displayed in the global scope"); "There should be 3 variables displayed in the global scope");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length, 6, ok(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length > 6,
"There should be 6 properties displayed in the inner scope"); "There should be more than 6 properties displayed in the inner scope");
is(mathScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, is(mathScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
"There should be 0 properties displayed in the math scope"); "There should be 0 properties displayed in the math scope");
is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
@ -253,21 +251,19 @@ function testVariablesFiltering()
is(innerScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"), is(innerScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
"this", "The only inner variable displayed should be 'this'"); "this", "The only inner variable displayed should be 'this'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[0].getAttribute("value"),
"window", "The first inner property displayed should be 'window'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[1].getAttribute("value"),
"document", "The second inner property displayed should be 'document'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"), is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"),
"location", "The third inner property displayed should be 'location'"); "window", "The third inner property displayed should be 'window'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[3].getAttribute("value"), is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[3].getAttribute("value"),
"__proto__", "The fourth inner property displayed should be '__proto__'"); "document", "The fourth inner property displayed should be 'document'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[4].getAttribute("value"), is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[4].getAttribute("value"),
"Location", "The fifth inner property displayed should be 'Location'"); "location", "The fifth inner property displayed should be 'location'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[5].getAttribute("value"),
"Location", "The sixth inner property displayed should be 'Location'");
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"), is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
"Location", "The only global variable displayed should be 'Location'"); "location", "The first global variable displayed should be 'location'");
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[1].getAttribute("value"),
"locationbar", "The second global variable displayed should be 'locationbar'");
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[2].getAttribute("value"),
"Location", "The second global variable displayed should be 'Location'");
} }
var scopes = gDebugger.DebuggerView.Variables._list, var scopes = gDebugger.DebuggerView.Variables._list,

Просмотреть файл

@ -92,8 +92,8 @@ function testVariablesFiltering()
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 2, is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 2,
"There should be 2 variables displayed in the global scope"); "There should be 2 variables displayed in the global scope");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length, 8, ok(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length > 3,
"There should be 8 properties displayed in the inner scope"); "There should be more than 3 properties displayed in the inner scope");
is(mathScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, is(mathScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
"There should be 0 properties displayed in the math scope"); "There should be 0 properties displayed in the math scope");
is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
@ -111,16 +111,6 @@ function testVariablesFiltering()
"window", "The second inner property displayed should be 'window'"); "window", "The second inner property displayed should be 'window'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"), is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"),
"document", "The third inner property displayed should be 'document'"); "document", "The third inner property displayed should be 'document'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[3].getAttribute("value"),
"location", "The fourth inner property displayed should be 'location'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[4].getAttribute("value"),
"__proto__", "The fifth inner property displayed should be '__proto__'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[5].getAttribute("value"),
"__proto__", "The sixth inner property displayed should be '__proto__'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[6].getAttribute("value"),
"HTMLDocument", "The seventh inner property displayed should be 'HTMLDocument'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[7].getAttribute("value"),
"HTMLDocument", "The eight inner property displayed should be 'HTMLDocument'");
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"), is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
"document", "The first global variable displayed should be 'document'"); "document", "The first global variable displayed should be 'document'");
@ -168,8 +158,8 @@ function testVariablesFiltering()
"The local scope 'this.window' should be expanded"); "The local scope 'this.window' should be expanded");
is(documentItem.expanded, true, is(documentItem.expanded, true,
"The local scope 'this.window.document' should be expanded"); "The local scope 'this.window.document' should be expanded");
is(locationItem.expanded, false, is(locationItem.expanded, true,
"The local scope 'this.window.document.location' should not be expanded"); "The local scope 'this.window.document.location' should be expanded");
documentItem.toggle(); documentItem.toggle();
documentItem.toggle(); documentItem.toggle();
@ -186,8 +176,8 @@ function testVariablesFiltering()
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 2, is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 2,
"There should be 2 variables displayed in the global scope"); "There should be 2 variables displayed in the global scope");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length, 8, ok(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length > 3,
"There should be 8 properties displayed in the inner scope"); "There should be more than 3 properties displayed in the inner scope");
is(mathScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, is(mathScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
"There should be 0 properties displayed in the math scope"); "There should be 0 properties displayed in the math scope");
is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
@ -205,16 +195,6 @@ function testVariablesFiltering()
"window", "The second inner property displayed should be 'window'"); "window", "The second inner property displayed should be 'window'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"), is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"),
"document", "The third inner property displayed should be 'document'"); "document", "The third inner property displayed should be 'document'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[3].getAttribute("value"),
"location", "The fourth inner property displayed should be 'location'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[4].getAttribute("value"),
"__proto__", "The fifth inner property displayed should be '__proto__'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[5].getAttribute("value"),
"__proto__", "The sixth inner property displayed should be '__proto__'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[6].getAttribute("value"),
"HTMLDocument", "The seventh inner property displayed should be 'HTMLDocument'");
is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[7].getAttribute("value"),
"HTMLDocument", "The eight inner property displayed should be 'HTMLDocument'");
is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"), is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
"document", "The first global variable displayed should be 'document'"); "document", "The first global variable displayed should be 'document'");

Просмотреть файл

@ -127,8 +127,8 @@ function testVariablesFiltering()
"There should be 0 properties displayed in the math scope"); "There should be 0 properties displayed in the math scope");
is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
"There should be 0 properties displayed in the test scope"); "There should be 0 properties displayed in the test scope");
is(loadScope.querySelectorAll(".variables-view-property:not([non-match])").length, 1, ok(loadScope.querySelectorAll(".variables-view-property:not([non-match])").length > 1,
"There should be 1 property displayed in the load scope"); "There should be more than one property displayed in the load scope");
isnot(globalScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, isnot(globalScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
"There should be some properties displayed in the global scope"); "There should be some properties displayed in the global scope");
} }
@ -157,8 +157,8 @@ function testVariablesFiltering()
"There should be 0 properties displayed in the math scope"); "There should be 0 properties displayed in the math scope");
is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
"There should be 0 properties displayed in the test scope"); "There should be 0 properties displayed in the test scope");
is(loadScope.querySelectorAll(".variables-view-property:not([non-match])").length, 1, ok(loadScope.querySelectorAll(".variables-view-property:not([non-match])").length > 1,
"There should be 1 property displayed in the load scope"); "There should be more than one properties displayed in the load scope");
isnot(globalScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0, isnot(globalScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
"There should be some properties displayed in the global scope"); "There should be some properties displayed in the global scope");
} }

Просмотреть файл

@ -183,6 +183,7 @@ function debug_tab_pane(aURL, aOnDebugging, aBeforeTabAdded) {
info("Debugger has started"); info("Debugger has started");
dbg._view.Variables.lazyEmpty = false; dbg._view.Variables.lazyEmpty = false;
dbg._view.Variables.lazyAppend = false; dbg._view.Variables.lazyAppend = false;
dbg._view.Variables.lazyExpand = false;
aOnDebugging(tab, debuggee, dbg); aOnDebugging(tab, debuggee, dbg);
}); });
}); });
@ -206,6 +207,7 @@ function debug_remote(aURL, aOnDebugging, aBeforeTabAdded) {
info("Remote Debugger has started"); info("Remote Debugger has started");
win._dbgwin.DebuggerView.Variables.lazyEmpty = false; win._dbgwin.DebuggerView.Variables.lazyEmpty = false;
win._dbgwin.DebuggerView.Variables.lazyAppend = false; win._dbgwin.DebuggerView.Variables.lazyAppend = false;
win._dbgwin.DebuggerView.Variables.lazyExpand = false;
aOnDebugging(tab, debuggee, win); aOnDebugging(tab, debuggee, win);
}); });
}); });

Просмотреть файл

@ -21,11 +21,10 @@ this.EXPORTED_SYMBOLS = [ 'helpers' ];
var helpers = {}; var helpers = {};
this.helpers = helpers; this.helpers = helpers;
let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require; let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require;
Components.utils.import("resource:///modules/devtools/gcli.jsm", {}); Components.utils.import("resource://gre/modules/devtools/gcli.jsm", {});
let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console; let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
let devtools = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools; let TargetFactory = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools.TargetFactory;
let TargetFactory = devtools.TargetFactory;
let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise; let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise;
let assert = { ok: ok, is: is, log: info }; let assert = { ok: ok, is: is, log: info };
@ -158,6 +157,7 @@ helpers.runTests = function(options, tests) {
}); });
var recover = function(error) { var recover = function(error) {
ok(false, error);
console.error(error); console.error(error);
}; };
@ -341,7 +341,7 @@ helpers._createDebugCheck = function(options) {
output += ' current: \'' + helpers._actual.current(options) + '\',\n'; output += ' current: \'' + helpers._actual.current(options) + '\',\n';
output += ' status: \'' + helpers._actual.status(options) + '\',\n'; output += ' status: \'' + helpers._actual.status(options) + '\',\n';
output += ' options: ' + outputArray(helpers._actual.options(options)) + ',\n'; output += ' options: ' + outputArray(helpers._actual.options(options)) + ',\n';
output += ' error: \'' + helpers._actual.message(options) + '\',\n'; output += ' message: \'' + helpers._actual.message(options) + '\',\n';
output += ' predictions: ' + outputArray(predictions) + ',\n'; output += ' predictions: ' + outputArray(predictions) + ',\n';
output += ' unassigned: ' + outputArray(requisition._unassigned) + ',\n'; output += ' unassigned: ' + outputArray(requisition._unassigned) + ',\n';
output += ' outputState: \'' + helpers._actual.outputState(options) + '\',\n'; output += ' outputState: \'' + helpers._actual.outputState(options) + '\',\n';
@ -378,6 +378,8 @@ helpers._createDebugCheck = function(options) {
output += ' exec: {\n'; output += ' exec: {\n';
output += ' output: \'\',\n'; output += ' output: \'\',\n';
output += ' completed: true,\n'; output += ' completed: true,\n';
output += ' type: \'string\',\n';
output += ' error: false\n';
output += ' }\n'; output += ' }\n';
output += ' }\n'; output += ' }\n';
output += ']);'; output += ']);';
@ -702,7 +704,7 @@ helpers._check = function(options, name, checks) {
*/ */
helpers._exec = function(options, name, expected) { helpers._exec = function(options, name, expected) {
if (expected == null) { if (expected == null) {
return Promise.resolve(); return Promise.resolve({});
} }
var output = options.display.requisition.exec({ hidden: true }); var output = options.display.requisition.exec({ hidden: true });
@ -715,20 +717,32 @@ helpers._exec = function(options, name, expected) {
if (!options.window.document.createElement) { if (!options.window.document.createElement) {
assert.log('skipping output tests (missing doc.createElement) for ' + name); assert.log('skipping output tests (missing doc.createElement) for ' + name);
return Promise.resolve(); return Promise.resolve({ output: output });
} }
if (!('output' in expected)) { if (!('output' in expected)) {
return Promise.resolve(); return Promise.resolve({ output: output });
} }
var deferred = Promise.defer();
var checkOutput = function() { var checkOutput = function() {
var div = options.window.document.createElement('div'); var div = options.window.document.createElement('div');
var nodePromise = converters.convert(output.data, output.type, 'dom', var conversionContext = options.display.requisition.conversionContext;
options.display.requisition.context);
nodePromise.then(function(node) { if ('type' in expected) {
assert.is(output.type,
expected.type,
'output.type for: ' + name);
}
if ('error' in expected) {
assert.is(output.error,
expected.error,
'output.error for: ' + name);
}
var convertPromise = converters.convert(output.data, output.type, 'dom',
conversionContext);
return convertPromise.then(function(node) {
div.appendChild(node); div.appendChild(node);
var actualOutput = div.textContent.trim(); var actualOutput = div.textContent.trim();
@ -757,24 +771,11 @@ helpers._exec = function(options, name, expected) {
doTest(expected.output, actualOutput); doTest(expected.output, actualOutput);
} }
deferred.resolve(actualOutput); return { output: output, text: actualOutput };
}); });
}; };
if (output.completed !== false) { return output.promise.then(checkOutput, checkOutput);
checkOutput();
}
else {
var changed = function() {
if (output.completed !== false) {
checkOutput();
output.onChange.remove(changed);
}
};
output.onChange.add(changed);
}
return deferred.promise;
}; };
/** /**
@ -789,15 +790,15 @@ helpers._setup = function(options, name, action) {
return Promise.resolve(action()); return Promise.resolve(action());
} }
return Promise.reject('setup must be a string or a function'); return Promise.reject('\'setup\' property must be a string or a function. Is ' + action);
}; };
/** /**
* Helper to shutdown the test * Helper to shutdown the test
*/ */
helpers._post = function(name, action, output) { helpers._post = function(name, action, data) {
if (typeof action === 'function') { if (typeof action === 'function') {
return Promise.resolve(action(output)); return Promise.resolve(action(data.output, data.text));
} }
return Promise.resolve(action); return Promise.resolve(action);
}; };
@ -943,19 +944,22 @@ helpers.audit = function(options, audits) {
if (typeof chunkLen !== 'number') { if (typeof chunkLen !== 'number') {
chunkLen = 1; chunkLen = 1;
} }
var responseTime = (new Date().getTime() - start) / chunkLen;
totalResponseTime += responseTime; if (assert.currentTest) {
if (responseTime > maxResponseTime) { var responseTime = (new Date().getTime() - start) / chunkLen;
maxResponseTime = responseTime; totalResponseTime += responseTime;
maxResponseCulprit = assert.currentTest + '/' + name; if (responseTime > maxResponseTime) {
maxResponseTime = responseTime;
maxResponseCulprit = assert.currentTest + '/' + name;
}
averageOver++;
} }
averageOver++;
var checkDone = helpers._check(options, name, audit.check); var checkDone = helpers._check(options, name, audit.check);
return checkDone.then(function() { return checkDone.then(function() {
var execDone = helpers._exec(options, name, audit.exec); var execDone = helpers._exec(options, name, audit.exec);
return execDone.then(function(output) { return execDone.then(function(data) {
return helpers._post(name, audit.post, output).then(function() { return helpers._post(name, audit.post, data).then(function() {
if (assert.testLogging) { if (assert.testLogging) {
log('- END \'' + name + '\' in ' + assert.currentTest); log('- END \'' + name + '\' in ' + assert.currentTest);
} }
@ -963,9 +967,8 @@ helpers.audit = function(options, audits) {
}); });
}); });
}); });
}).then(null, function(ex) { }).then(function() {
console.error(ex.stack); return options.display.inputter.setInput('');
throw(ex);
}); });
}; };

Просмотреть файл

@ -222,8 +222,8 @@ DevTools.prototype = {
* *
* Each toolDefinition has the following properties: * Each toolDefinition has the following properties:
* - id: Unique identifier for this tool (string|required) * - id: Unique identifier for this tool (string|required)
* - killswitch: Property name to allow us to turn this tool on/off globally * - visibilityswitch: Property name to allow us to hide this tool from the
* (string|required) (TODO: default to devtools.{id}.enabled?) * DevTools Toolbox.
* - icon: URL pointing to a graphic which will be used as the src for an * - icon: URL pointing to a graphic which will be used as the src for an
* 16x16 img tag (string|required) * 16x16 img tag (string|required)
* - url: URL pointing to a XUL/XHTML document containing the user interface * - url: URL pointing to a XUL/XHTML document containing the user interface
@ -241,7 +241,7 @@ DevTools.prototype = {
throw new Error("Invalid definition.id"); throw new Error("Invalid definition.id");
} }
toolDefinition.killswitch = toolDefinition.killswitch || toolDefinition.visibilityswitch = toolDefinition.visibilityswitch ||
"devtools." + toolId + ".enabled"; "devtools." + toolId + ".enabled";
this._tools.set(toolId, toolDefinition); this._tools.set(toolId, toolDefinition);
@ -307,21 +307,17 @@ DevTools.prototype = {
*/ */
getToolDefinitionMap: function DT_getToolDefinitionMap() { getToolDefinitionMap: function DT_getToolDefinitionMap() {
let tools = new Map(); let tools = new Map();
let disabledTools = [];
try {
disabledTools = JSON.parse(Services.prefs.getCharPref("devtools.toolbox.disabledTools"));
} catch(ex) {}
for (let [key, value] of this._tools) { for (let [key, value] of this._tools) {
let enabled; let enabled;
try { try {
enabled = Services.prefs.getBoolPref(value.killswitch); enabled = Services.prefs.getBoolPref(value.visibilityswitch);
} catch(e) { } catch(e) {
enabled = true; enabled = true;
} }
if (enabled && disabledTools.indexOf(key) == -1) { if (enabled || value.id == "options") {
tools.set(key, value); tools.set(key, value);
} }
} }
@ -454,7 +450,7 @@ DevTools.prototype = {
destroy: function() { destroy: function() {
Services.obs.removeObserver(this.destroy, "quit-application"); Services.obs.removeObserver(this.destroy, "quit-application");
for (let [key, tool] of this._tools) { for (let [key, tool] of this.getToolDefinitionMap()) {
this.unregisterTool(key, true); this.unregisterTool(key, true);
} }
@ -578,15 +574,23 @@ let gDevToolsBrowser = {
}, },
/** /**
* Add the menuitem for a tool to all open browser windows. Also toggles the * Add the menuitem for a tool to all open browser windows.
* kill switch preference of the tool.
* *
* @param {object} toolDefinition * @param {object} toolDefinition
* properties of the tool to add * properties of the tool to add
*/ */
_addToolToWindows: function DT_addToolToWindows(toolDefinition) { _addToolToWindows: function DT_addToolToWindows(toolDefinition) {
// Set the kill switch pref boolean to true // No menu item or global shortcut is required for options panel.
Services.prefs.setBoolPref(toolDefinition.killswitch, true); if (toolDefinition.id == "options") {
return;
}
// Skip if the tool is disabled.
try {
if (!Services.prefs.getBoolPref(toolDefinition.visibilityswitch)) {
return;
}
} catch(e) {}
// We need to insert the new tool in the right place, which means knowing // We need to insert the new tool in the right place, which means knowing
// the tool that comes before the tool that we're trying to add // the tool that comes before the tool that we're trying to add
@ -642,6 +646,17 @@ let gDevToolsBrowser = {
let fragMenuItems = doc.createDocumentFragment(); let fragMenuItems = doc.createDocumentFragment();
for (let toolDefinition of gDevTools.getToolDefinitionArray()) { for (let toolDefinition of gDevTools.getToolDefinitionArray()) {
if (toolDefinition.id == "options") {
continue;
}
// Skip if the tool is disabled.
try {
if (!Services.prefs.getBoolPref(toolDefinition.visibilityswitch)) {
continue;
}
} catch(e) {}
let elements = gDevToolsBrowser._createToolMenuElements(toolDefinition, doc); let elements = gDevToolsBrowser._createToolMenuElements(toolDefinition, doc);
if (!elements) { if (!elements) {
@ -714,7 +729,7 @@ let gDevToolsBrowser = {
let bc = doc.createElement("broadcaster"); let bc = doc.createElement("broadcaster");
bc.id = "devtoolsMenuBroadcaster_" + id; bc.id = "devtoolsMenuBroadcaster_" + id;
bc.setAttribute("label", toolDefinition.label); bc.setAttribute("label", toolDefinition.menuLabel || toolDefinition.label);
bc.setAttribute("command", cmd.id); bc.setAttribute("command", cmd.id);
if (key) { if (key) {
@ -767,16 +782,12 @@ let gDevToolsBrowser = {
}, },
/** /**
* Remove the menuitem for a tool to all open browser windows. Also sets the * Remove the menuitem for a tool to all open browser windows.
* kill switch boolean pref to false.
* *
* @param {object} toolId * @param {string} toolId
* id of the tool to remove * id of the tool to remove
* @param {string} killswitch
* The kill switch preference string of the tool
*/ */
_removeToolFromWindows: function DT_removeToolFromWindows(toolId, killswitch) { _removeToolFromWindows: function DT_removeToolFromWindows(toolId) {
Services.prefs.setBoolPref(killswitch, false);
for (let win of gDevToolsBrowser._trackedBrowserWindows) { for (let win of gDevToolsBrowser._trackedBrowserWindows) {
gDevToolsBrowser._removeToolFromMenu(toolId, win.document); gDevToolsBrowser._removeToolFromMenu(toolId, win.document);
} }
@ -854,15 +865,10 @@ gDevTools.on("tool-registered", function(ev, toolId) {
}); });
gDevTools.on("tool-unregistered", function(ev, toolId) { gDevTools.on("tool-unregistered", function(ev, toolId) {
let killswitch; if (typeof toolId != "string") {
if (typeof toolId == "string") {
killswitch = "devtools." + toolId + ".enabled";
}
else {
killswitch = toolId.killswitch;
toolId = toolId.id; toolId = toolId.id;
} }
gDevToolsBrowser._removeToolFromWindows(toolId, killswitch); gDevToolsBrowser._removeToolFromWindows(toolId);
}); });
gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox); gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox);

Просмотреть файл

@ -20,7 +20,7 @@ function runTests(aTab) {
let toolDefinition = { let toolDefinition = {
id: toolId, id: toolId,
isTargetSupported: function() true, isTargetSupported: function() true,
killswitch: "devtools.test-tool.enabled", visibilityswitch: "devtools.test-tool.enabled",
url: "about:blank", url: "about:blank",
label: "someLabel", label: "someLabel",
build: function(iframeWindow, toolbox) { build: function(iframeWindow, toolbox) {

Просмотреть файл

@ -26,24 +26,16 @@ function testSelectTool(aToolbox) {
function testOptionsShortcut() { function testOptionsShortcut() {
ok(true, "Toolbox selected via selectTool method"); ok(true, "Toolbox selected via selectTool method");
toolbox.once("options-selected", testOptionsButtonClick); toolbox.once("options-selected", testOptions);
toolbox.selectTool("webconsole") toolbox.selectTool("webconsole")
.then(() => synthesizeKeyFromKeyTag("toolbox-options-key", doc)); .then(() => synthesizeKeyFromKeyTag("toolbox-options-key", doc));
} }
function testOptionsButtonClick() { function testOptions(event, tool) {
ok(true, "Toolbox selected via shortcut");
toolbox.once("options-selected", testOptions);
toolbox.selectTool("webconsole")
.then(() => doc.getElementById("toolbox-tab-options").click());
}
function testOptions(event, iframe) {
ok(true, "Toolbox selected via button click"); ok(true, "Toolbox selected via button click");
panelWin = iframe.contentWindow; panelWin = tool.panelWin;
let panelDoc = iframe.contentDocument;
// Testing pref changes // Testing pref changes
let prefCheckboxes = panelDoc.querySelectorAll("checkbox[data-pref]"); let prefCheckboxes = tool.panelDoc.querySelectorAll("checkbox[data-pref]");
for (let checkbox of prefCheckboxes) { for (let checkbox of prefCheckboxes) {
prefNodes.push(checkbox); prefNodes.push(checkbox);
prefValues.push(Services.prefs.getBoolPref(checkbox.getAttribute("data-pref"))); prefValues.push(Services.prefs.getBoolPref(checkbox.getAttribute("data-pref")));

Просмотреть файл

@ -24,7 +24,7 @@ function test() {
let toolDefinition = { let toolDefinition = {
id: "fakeTool4242", id: "fakeTool4242",
killswitch: "devtools.fakeTool4242.enabled", visibilityswitch: "devtools.fakeTool4242.enabled",
url: toolURL, url: toolURL,
label: "FAKE TOOL!!!", label: "FAKE TOOL!!!",
isTargetSupported: function() true, isTargetSupported: function() true,

Просмотреть файл

@ -16,7 +16,7 @@ function test() {
const TOOL_ID_1 = "webconsole"; const TOOL_ID_1 = "webconsole";
const TOOL_ID_2 = "jsdebugger"; const TOOL_ID_2 = "jsdebugger";
const LABEL_1 = "Web Console"; const LABEL_1 = "Console";
const LABEL_2 = "Debugger"; const LABEL_2 = "Debugger";
let toolbox; let toolbox;

Просмотреть файл

@ -1,110 +1,142 @@
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict"; "use strict";
const { utils: Cu } = Components; const {Cu} = require("chrome");
const DISABLED_TOOLS = "devtools.toolbox.disabledTools";
Cu.import("resource://gre/modules/Services.jsm"); let Promise = require("sdk/core/promise");
Cu.import("resource:///modules/devtools/gDevTools.jsm"); let EventEmitter = require("devtools/shared/event-emitter");
window.addEventListener("load", function onLoad() { Cu.import("resource://gre/modules/Services.jsm");
window.removeEventListener("load", onLoad); Cu.import("resource:///modules/devtools/gDevTools.jsm");
setupToolsList();
populatePreferences(); exports.OptionsPanel = OptionsPanel;
});
/**
function setupToolsList() { * Represents the Options Panel in the Toolbox.
let disabledTools = []; */
try { function OptionsPanel(iframeWindow, toolbox) {
disabledTools = JSON.parse(Services.prefs.getCharPref(DISABLED_TOOLS)); this.panelDoc = iframeWindow.document;
} catch(ex) { this.panelWin = iframeWindow;
Cu.reportError("Error parsing pref " + DISABLED_TOOLS + " as JSON.");
} EventEmitter.decorate(this);
let defaultToolsBox = document.getElementById("default-tools-box"); };
let additionalToolsBox = document.getElementById("additional-tools-box");
OptionsPanel.prototype = {
defaultToolsBox.textContent = "";
additionalToolsBox.textContent = ""; open: function OP_open() {
let deferred = Promise.defer();
let onCheckboxClick = function(id) {
if (disabledTools.indexOf(id) > -1) { this.setupToolsList();
disabledTools.splice(disabledTools.indexOf(id), 1); this.populatePreferences();
Services.prefs.setCharPref(DISABLED_TOOLS, JSON.stringify(disabledTools));
gDevTools.emit("tool-registered", id); this.emit("ready");
} deferred.resolve(this);
else { return deferred.promise;
disabledTools.push(id); },
Services.prefs.setCharPref(DISABLED_TOOLS, JSON.stringify(disabledTools));
gDevTools.emit("tool-unregistered", gDevTools._tools.get(id)); setupToolsList: function OP_setupToolsList() {
} let defaultToolsBox = this.panelDoc.getElementById("default-tools-box");
}; let additionalToolsBox = this.panelDoc.getElementById("additional-tools-box");
// Populating the default tools lists defaultToolsBox.textContent = "";
for (let tool of gDevTools.getDefaultTools()) { additionalToolsBox.textContent = "";
let checkbox = document.createElement("checkbox");
checkbox.setAttribute("id", tool.id); let pref = function(key) {
checkbox.setAttribute("label", tool.label); try {
checkbox.setAttribute("tooltiptext", tool.tooltip || ""); return Services.prefs.getBoolPref(key);
checkbox.setAttribute("checked", disabledTools.indexOf(tool.id) == -1); }
checkbox.addEventListener("command", onCheckboxClick.bind(null, tool.id)); catch (ex) {
defaultToolsBox.appendChild(checkbox); return true;
} }
};
// Populating the additional tools list that came from add-ons.
let atleastOneAddon = false; let onCheckboxClick = function(id) {
for (let tool of gDevTools.getAdditionalTools()) { let toolDefinition = gDevTools._tools.get(id);
atleastOneAddon = true; // Set the kill switch pref boolean to true
let checkbox = document.createElement("checkbox"); Services.prefs.setBoolPref(toolDefinition.visibilityswitch, this.checked);
checkbox.setAttribute("id", tool.id); if (this.checked) {
checkbox.setAttribute("label", tool.label); gDevTools.emit("tool-registered", id);
checkbox.setAttribute("tooltiptext", tool.tooltip || ""); }
checkbox.setAttribute("checked", disabledTools.indexOf(tool.id) == -1); else {
checkbox.addEventListener("command", onCheckboxClick.bind(null, tool.id)); gDevTools.emit("tool-unregistered", toolDefinition);
additionalToolsBox.appendChild(checkbox); }
} };
if (!atleastOneAddon) { // Populating the default tools lists
additionalToolsBox.style.display = "none"; for (let tool of gDevTools.getDefaultTools()) {
additionalToolsBox.previousSibling.style.display = "none"; if (tool.id == "options") {
} continue;
}
window.focus(); let checkbox = this.panelDoc.createElement("checkbox");
} checkbox.setAttribute("id", tool.id);
checkbox.setAttribute("label", tool.label);
function populatePreferences() { checkbox.setAttribute("tooltiptext", tool.tooltip || "");
let prefCheckboxes = document.querySelectorAll("checkbox[data-pref]"); checkbox.setAttribute("checked", pref(tool.visibilityswitch));
for (let checkbox of prefCheckboxes) { checkbox.addEventListener("command", onCheckboxClick.bind(checkbox, tool.id));
checkbox.checked = Services.prefs.getBoolPref(checkbox.getAttribute("data-pref")); defaultToolsBox.appendChild(checkbox);
checkbox.addEventListener("command", function() { }
let data = {
pref: this.getAttribute("data-pref"), // Populating the additional tools list that came from add-ons.
newValue: this.checked let atleastOneAddon = false;
}; for (let tool of gDevTools.getAdditionalTools()) {
data.oldValue = Services.prefs.getBoolPref(data.pref); atleastOneAddon = true;
Services.prefs.setBoolPref(data.pref, data.newValue); let checkbox = this.panelDoc.createElement("checkbox");
gDevTools.emit("pref-changed", data); checkbox.setAttribute("id", tool.id);
}.bind(checkbox)); checkbox.setAttribute("label", tool.label);
} checkbox.setAttribute("tooltiptext", tool.tooltip || "");
let prefRadiogroups = document.querySelectorAll("radiogroup[data-pref]"); checkbox.setAttribute("checked", pref(tool.visibilityswitch));
for (let radiogroup of prefRadiogroups) { checkbox.addEventListener("command", onCheckboxClick.bind(checkbox, tool.id));
let selectedValue = Services.prefs.getCharPref(radiogroup.getAttribute("data-pref")); additionalToolsBox.appendChild(checkbox);
for (let radio of radiogroup.childNodes) { }
radiogroup.selectedIndex = -1;
if (radio.getAttribute("value") == selectedValue) { if (!atleastOneAddon) {
radiogroup.selectedItem = radio; additionalToolsBox.style.display = "none";
break; additionalToolsBox.previousSibling.style.display = "none";
} }
}
radiogroup.addEventListener("select", function() { this.panelWin.focus();
let data = { },
pref: this.getAttribute("data-pref"),
newValue: this.selectedItem.getAttribute("value") populatePreferences: function OP_populatePreferences() {
}; let prefCheckboxes = this.panelDoc.querySelectorAll("checkbox[data-pref]");
data.oldValue = Services.prefs.getCharPref(data.pref); for (let checkbox of prefCheckboxes) {
Services.prefs.setCharPref(data.pref, data.newValue); checkbox.checked = Services.prefs.getBoolPref(checkbox.getAttribute("data-pref"));
gDevTools.emit("pref-changed", data); checkbox.addEventListener("command", function() {
}.bind(radiogroup)); let data = {
} pref: this.getAttribute("data-pref"),
} newValue: this.checked
};
data.oldValue = Services.prefs.getBoolPref(data.pref);
Services.prefs.setBoolPref(data.pref, data.newValue);
gDevTools.emit("pref-changed", data);
}.bind(checkbox));
}
let prefRadiogroups = this.panelDoc.querySelectorAll("radiogroup[data-pref]");
for (let radiogroup of prefRadiogroups) {
let selectedValue = Services.prefs.getCharPref(radiogroup.getAttribute("data-pref"));
for (let radio of radiogroup.childNodes) {
radiogroup.selectedIndex = -1;
if (radio.getAttribute("value") == selectedValue) {
radiogroup.selectedItem = radio;
break;
}
}
radiogroup.addEventListener("select", function() {
let data = {
pref: this.getAttribute("data-pref"),
newValue: this.selectedItem.getAttribute("value")
};
data.oldValue = Services.prefs.getCharPref(data.pref);
Services.prefs.setCharPref(data.pref, data.newValue);
gDevTools.emit("pref-changed", data);
}.bind(radiogroup));
}
},
destroy: function OP_destroy() {
this.panelWin = this.panelDoc = null;
}
};

Просмотреть файл

@ -11,7 +11,6 @@
<?xml-stylesheet rel="stylesheet" href="chrome://browser/skin/devtools/toolbox.css" type="text/css"?> <?xml-stylesheet rel="stylesheet" href="chrome://browser/skin/devtools/toolbox.css" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript;version=1.8" src="toolbox-options.js"></script>
<hbox id="options-panel-container" flex="1"> <hbox id="options-panel-container" flex="1">
<hbox id="options-panel" flex="1"> <hbox id="options-panel" flex="1">
<vbox id="tools-box" class="options-vertical-pane" flex="1"> <vbox id="tools-box" class="options-vertical-pane" flex="1">

Просмотреть файл

@ -37,7 +37,7 @@ XPCOMUtils.defineLazyGetter(this, "toolboxStrings", function() {
XPCOMUtils.defineLazyGetter(this, "Requisition", function() { XPCOMUtils.defineLazyGetter(this, "Requisition", function() {
let scope = {}; let scope = {};
Cu.import("resource://gre/modules/devtools/Require.jsm", scope); Cu.import("resource://gre/modules/devtools/Require.jsm", scope);
Cu.import("resource:///modules/devtools/gcli.jsm", scope); Cu.import("resource://gre/modules/devtools/gcli.jsm", {});
let req = scope.require; let req = scope.require;
return req('gcli/cli').Requisition; return req('gcli/cli').Requisition;
@ -220,14 +220,6 @@ Toolbox.prototype = {
}, },
_buildOptions: function TBOX__buildOptions() { _buildOptions: function TBOX__buildOptions() {
this.optionsButton = this.doc.getElementById("toolbox-tab-options");
this.optionsButton.addEventListener("command", function() {
this.selectTool("options");
}.bind(this), false);
let iframe = this.doc.getElementById("toolbox-panel-iframe-options");
this._toolPanels.set("options", iframe);
let key = this.doc.getElementById("toolbox-options-key"); let key = this.doc.getElementById("toolbox-options-key");
key.addEventListener("command", function(toolId) { key.addEventListener("command", function(toolId) {
this.selectTool(toolId); this.selectTool(toolId);
@ -358,7 +350,6 @@ Toolbox.prototype = {
let radio = this.doc.createElement("radio"); let radio = this.doc.createElement("radio");
radio.className = "toolbox-tab devtools-tab"; radio.className = "toolbox-tab devtools-tab";
radio.id = "toolbox-tab-" + id; radio.id = "toolbox-tab-" + id;
radio.setAttribute("flex", "1");
radio.setAttribute("toolid", id); radio.setAttribute("toolid", id);
if (toolDefinition.ordinal == undefined || toolDefinition.ordinal < 0) { if (toolDefinition.ordinal == undefined || toolDefinition.ordinal < 0) {
toolDefinition.ordinal = MAX_ORDINAL; toolDefinition.ordinal = MAX_ORDINAL;
@ -370,22 +361,30 @@ Toolbox.prototype = {
this.selectTool(id); this.selectTool(id);
}.bind(this, id)); }.bind(this, id));
// spacer lets us center the image and label, while allowing cropping
let spacer = this.doc.createElement("spacer");
spacer.setAttribute("flex", "1");
radio.appendChild(spacer);
if (toolDefinition.icon) { if (toolDefinition.icon) {
let image = this.doc.createElement("image"); let image = this.doc.createElement("image");
image.setAttribute("src", toolDefinition.icon); image.setAttribute("src", toolDefinition.icon);
radio.appendChild(image); radio.appendChild(image);
} }
let label = this.doc.createElement("label"); if (toolDefinition.label) {
label.setAttribute("value", toolDefinition.label) let label = this.doc.createElement("label");
label.setAttribute("crop", "end"); label.setAttribute("value", toolDefinition.label)
label.setAttribute("flex", "1"); label.setAttribute("crop", "end");
label.setAttribute("flex", "1");
radio.appendChild(label);
radio.setAttribute("flex", "1");
}
let vbox = this.doc.createElement("vbox"); let vbox = this.doc.createElement("vbox");
vbox.className = "toolbox-panel"; vbox.className = "toolbox-panel";
vbox.id = "toolbox-panel-" + id; vbox.id = "toolbox-panel-" + id;
radio.appendChild(label);
// If there is no tab yet, or the ordinal to be added is the largest one. // If there is no tab yet, or the ordinal to be added is the largest one.
if (tabs.childNodes.length == 0 || if (tabs.childNodes.length == 0 ||
@ -398,8 +397,7 @@ Toolbox.prototype = {
Array.some(tabs.childNodes, (node, i) => { Array.some(tabs.childNodes, (node, i) => {
if (+node.getAttribute("ordinal") > toolDefinition.ordinal) { if (+node.getAttribute("ordinal") > toolDefinition.ordinal) {
tabs.insertBefore(radio, node); tabs.insertBefore(radio, node);
deck.insertBefore(vbox, deck.childNodes[i + 1]); deck.insertBefore(vbox, deck.childNodes[i]);
// + 1 because of options panel.
return true; return true;
} }
}); });
@ -440,8 +438,9 @@ Toolbox.prototype = {
let tabstrip = this.doc.getElementById("toolbox-tabs"); let tabstrip = this.doc.getElementById("toolbox-tabs");
// select the right tab // select the right tab, making 0th index the default tab if right tab not
let index = -1; // found
let index = 0;
let tabs = tabstrip.childNodes; let tabs = tabstrip.childNodes;
for (let i = 0; i < tabs.length; i++) { for (let i = 0; i < tabs.length; i++) {
if (tabs[i] === tab) { if (tabs[i] === tab) {
@ -453,15 +452,7 @@ Toolbox.prototype = {
// and select the right iframe // and select the right iframe
let deck = this.doc.getElementById("toolbox-deck"); let deck = this.doc.getElementById("toolbox-deck");
// offset by 1 due to options panel deck.selectedIndex = index;
if (id == "options") {
deck.selectedIndex = 0;
this.optionsButton.setAttribute("checked", true);
}
else {
deck.selectedIndex = index != -1 ? index + 1: -1;
this.optionsButton.removeAttribute("checked");
}
let definition = gDevTools.getToolDefinitionMap().get(id); let definition = gDevTools.getToolDefinitionMap().get(id);
@ -668,7 +659,7 @@ Toolbox.prototype = {
if (this.hostType == Toolbox.HostType.WINDOW) { if (this.hostType == Toolbox.HostType.WINDOW) {
let doc = this.doc.defaultView.parent.document; let doc = this.doc.defaultView.parent.document;
let key = doc.getElementById("key_" + id); let key = doc.getElementById("key_" + toolId);
if (key) { if (key) {
key.parentNode.removeChild(key); key.parentNode.removeChild(key);
} }
@ -709,7 +700,6 @@ Toolbox.prototype = {
let outstanding = []; let outstanding = [];
this._toolPanels.delete("options");
for (let [id, panel] of this._toolPanels) { for (let [id, panel] of this._toolPanels) {
outstanding.push(panel.destroy()); outstanding.push(panel.destroy());
} }

Просмотреть файл

@ -34,10 +34,6 @@
<hbox id="toolbox-dock-buttons"/> <hbox id="toolbox-dock-buttons"/>
</hbox> </hbox>
#endif #endif
<toolbarbutton id="toolbox-tab-options"
autocheck="false"
class="command-button toolbox-tab devtools-tab"
tooltiptext="&toolboxOptionsButton.tooltip;"/>
<hbox id="toolbox-tabs" flex="1"> <hbox id="toolbox-tabs" flex="1">
</hbox> </hbox>
<hbox id="toolbox-buttons" pack="end"/> <hbox id="toolbox-buttons" pack="end"/>
@ -51,14 +47,6 @@
#endif #endif
</toolbar> </toolbar>
<deck id="toolbox-deck" flex="1"> <deck id="toolbox-deck" flex="1">
<vbox id="toolbox-panel-options"
class="toolbox-panel">
<iframe id="toolbox-panel-iframe-options"
class="toolbox-panel-iframe"
flex="1" forceOwnRefreshDriver=""
src="chrome://browser/content/devtools/framework/toolbox-options.xul">
</iframe>
</vbox>
</deck> </deck>
</notificationbox> </notificationbox>
</window> </window>

Просмотреть файл

@ -5,7 +5,7 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components; const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
this.EXPORTED_SYMBOLS = [ ]; this.EXPORTED_SYMBOLS = [ ];
Cu.import("resource:///modules/devtools/gcli.jsm"); Cu.import("resource://gre/modules/devtools/gcli.jsm");
Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",

Просмотреть файл

@ -21,11 +21,10 @@ this.EXPORTED_SYMBOLS = [ 'helpers' ];
var helpers = {}; var helpers = {};
this.helpers = helpers; this.helpers = helpers;
let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require; let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require;
Components.utils.import("resource:///modules/devtools/gcli.jsm", {}); Components.utils.import("resource://gre/modules/devtools/gcli.jsm", {});
let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console; let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
let devtools = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools; let TargetFactory = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools.TargetFactory;
let TargetFactory = devtools.TargetFactory;
let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise; let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise;
let assert = { ok: ok, is: is, log: info }; let assert = { ok: ok, is: is, log: info };
@ -158,6 +157,7 @@ helpers.runTests = function(options, tests) {
}); });
var recover = function(error) { var recover = function(error) {
ok(false, error);
console.error(error); console.error(error);
}; };
@ -341,7 +341,7 @@ helpers._createDebugCheck = function(options) {
output += ' current: \'' + helpers._actual.current(options) + '\',\n'; output += ' current: \'' + helpers._actual.current(options) + '\',\n';
output += ' status: \'' + helpers._actual.status(options) + '\',\n'; output += ' status: \'' + helpers._actual.status(options) + '\',\n';
output += ' options: ' + outputArray(helpers._actual.options(options)) + ',\n'; output += ' options: ' + outputArray(helpers._actual.options(options)) + ',\n';
output += ' error: \'' + helpers._actual.message(options) + '\',\n'; output += ' message: \'' + helpers._actual.message(options) + '\',\n';
output += ' predictions: ' + outputArray(predictions) + ',\n'; output += ' predictions: ' + outputArray(predictions) + ',\n';
output += ' unassigned: ' + outputArray(requisition._unassigned) + ',\n'; output += ' unassigned: ' + outputArray(requisition._unassigned) + ',\n';
output += ' outputState: \'' + helpers._actual.outputState(options) + '\',\n'; output += ' outputState: \'' + helpers._actual.outputState(options) + '\',\n';
@ -378,6 +378,8 @@ helpers._createDebugCheck = function(options) {
output += ' exec: {\n'; output += ' exec: {\n';
output += ' output: \'\',\n'; output += ' output: \'\',\n';
output += ' completed: true,\n'; output += ' completed: true,\n';
output += ' type: \'string\',\n';
output += ' error: false\n';
output += ' }\n'; output += ' }\n';
output += ' }\n'; output += ' }\n';
output += ']);'; output += ']);';
@ -702,7 +704,7 @@ helpers._check = function(options, name, checks) {
*/ */
helpers._exec = function(options, name, expected) { helpers._exec = function(options, name, expected) {
if (expected == null) { if (expected == null) {
return Promise.resolve(); return Promise.resolve({});
} }
var output = options.display.requisition.exec({ hidden: true }); var output = options.display.requisition.exec({ hidden: true });
@ -715,20 +717,32 @@ helpers._exec = function(options, name, expected) {
if (!options.window.document.createElement) { if (!options.window.document.createElement) {
assert.log('skipping output tests (missing doc.createElement) for ' + name); assert.log('skipping output tests (missing doc.createElement) for ' + name);
return Promise.resolve(); return Promise.resolve({ output: output });
} }
if (!('output' in expected)) { if (!('output' in expected)) {
return Promise.resolve(); return Promise.resolve({ output: output });
} }
var deferred = Promise.defer();
var checkOutput = function() { var checkOutput = function() {
var div = options.window.document.createElement('div'); var div = options.window.document.createElement('div');
var nodePromise = converters.convert(output.data, output.type, 'dom', var conversionContext = options.display.requisition.conversionContext;
options.display.requisition.context);
nodePromise.then(function(node) { if ('type' in expected) {
assert.is(output.type,
expected.type,
'output.type for: ' + name);
}
if ('error' in expected) {
assert.is(output.error,
expected.error,
'output.error for: ' + name);
}
var convertPromise = converters.convert(output.data, output.type, 'dom',
conversionContext);
return convertPromise.then(function(node) {
div.appendChild(node); div.appendChild(node);
var actualOutput = div.textContent.trim(); var actualOutput = div.textContent.trim();
@ -757,24 +771,11 @@ helpers._exec = function(options, name, expected) {
doTest(expected.output, actualOutput); doTest(expected.output, actualOutput);
} }
deferred.resolve(actualOutput); return { output: output, text: actualOutput };
}); });
}; };
if (output.completed !== false) { return output.promise.then(checkOutput, checkOutput);
checkOutput();
}
else {
var changed = function() {
if (output.completed !== false) {
checkOutput();
output.onChange.remove(changed);
}
};
output.onChange.add(changed);
}
return deferred.promise;
}; };
/** /**
@ -789,15 +790,15 @@ helpers._setup = function(options, name, action) {
return Promise.resolve(action()); return Promise.resolve(action());
} }
return Promise.reject('setup must be a string or a function'); return Promise.reject('\'setup\' property must be a string or a function. Is ' + action);
}; };
/** /**
* Helper to shutdown the test * Helper to shutdown the test
*/ */
helpers._post = function(name, action, output) { helpers._post = function(name, action, data) {
if (typeof action === 'function') { if (typeof action === 'function') {
return Promise.resolve(action(output)); return Promise.resolve(action(data.output, data.text));
} }
return Promise.resolve(action); return Promise.resolve(action);
}; };
@ -943,19 +944,22 @@ helpers.audit = function(options, audits) {
if (typeof chunkLen !== 'number') { if (typeof chunkLen !== 'number') {
chunkLen = 1; chunkLen = 1;
} }
var responseTime = (new Date().getTime() - start) / chunkLen;
totalResponseTime += responseTime; if (assert.currentTest) {
if (responseTime > maxResponseTime) { var responseTime = (new Date().getTime() - start) / chunkLen;
maxResponseTime = responseTime; totalResponseTime += responseTime;
maxResponseCulprit = assert.currentTest + '/' + name; if (responseTime > maxResponseTime) {
maxResponseTime = responseTime;
maxResponseCulprit = assert.currentTest + '/' + name;
}
averageOver++;
} }
averageOver++;
var checkDone = helpers._check(options, name, audit.check); var checkDone = helpers._check(options, name, audit.check);
return checkDone.then(function() { return checkDone.then(function() {
var execDone = helpers._exec(options, name, audit.exec); var execDone = helpers._exec(options, name, audit.exec);
return execDone.then(function(output) { return execDone.then(function(data) {
return helpers._post(name, audit.post, output).then(function() { return helpers._post(name, audit.post, data).then(function() {
if (assert.testLogging) { if (assert.testLogging) {
log('- END \'' + name + '\' in ' + assert.currentTest); log('- END \'' + name + '\' in ' + assert.currentTest);
} }
@ -963,9 +967,8 @@ helpers.audit = function(options, audits) {
}); });
}); });
}); });
}).then(null, function(ex) { }).then(function() {
console.error(ex.stack); return options.display.inputter.setInput('');
throw(ex);
}); });
}; };

Просмотреть файл

@ -29,6 +29,7 @@ Object.defineProperty(exports, "TargetFactory", {
loader.lazyGetter(this, "osString", () => Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS); loader.lazyGetter(this, "osString", () => Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS);
// Panels // Panels
loader.lazyGetter(this, "OptionsPanel", function() require("devtools/framework/toolbox-options").OptionsPanel);
loader.lazyGetter(this, "InspectorPanel", function() require("devtools/inspector/inspector-panel").InspectorPanel); loader.lazyGetter(this, "InspectorPanel", function() require("devtools/inspector/inspector-panel").InspectorPanel);
loader.lazyImporter(this, "WebConsolePanel", "resource:///modules/WebConsolePanel.jsm"); loader.lazyImporter(this, "WebConsolePanel", "resource:///modules/WebConsolePanel.jsm");
loader.lazyImporter(this, "DebuggerPanel", "resource:///modules/devtools/DebuggerPanel.jsm"); loader.lazyImporter(this, "DebuggerPanel", "resource:///modules/devtools/DebuggerPanel.jsm");
@ -37,12 +38,14 @@ loader.lazyImporter(this, "ProfilerPanel", "resource:///modules/devtools/Profile
loader.lazyImporter(this, "NetMonitorPanel", "resource:///modules/devtools/NetMonitorPanel.jsm"); loader.lazyImporter(this, "NetMonitorPanel", "resource:///modules/devtools/NetMonitorPanel.jsm");
// Strings // Strings
const toolboxProps = "chrome://browser/locale/devtools/toolbox.properties";
const inspectorProps = "chrome://browser/locale/devtools/inspector.properties"; const inspectorProps = "chrome://browser/locale/devtools/inspector.properties";
const debuggerProps = "chrome://browser/locale/devtools/debugger.properties"; const debuggerProps = "chrome://browser/locale/devtools/debugger.properties";
const styleEditorProps = "chrome://browser/locale/devtools/styleeditor.properties"; const styleEditorProps = "chrome://browser/locale/devtools/styleeditor.properties";
const webConsoleProps = "chrome://browser/locale/devtools/webconsole.properties"; const webConsoleProps = "chrome://browser/locale/devtools/webconsole.properties";
const profilerProps = "chrome://browser/locale/devtools/profiler.properties"; const profilerProps = "chrome://browser/locale/devtools/profiler.properties";
const netMonitorProps = "chrome://browser/locale/devtools/netmonitor.properties"; const netMonitorProps = "chrome://browser/locale/devtools/netmonitor.properties";
loader.lazyGetter(this, "toolboxStrings", () => Services.strings.createBundle(toolboxProps));
loader.lazyGetter(this, "webConsoleStrings", () => Services.strings.createBundle(webConsoleProps)); loader.lazyGetter(this, "webConsoleStrings", () => Services.strings.createBundle(webConsoleProps));
loader.lazyGetter(this, "debuggerStrings", () => Services.strings.createBundle(debuggerProps)); loader.lazyGetter(this, "debuggerStrings", () => Services.strings.createBundle(debuggerProps));
loader.lazyGetter(this, "styleEditorStrings", () => Services.strings.createBundle(styleEditorProps)); loader.lazyGetter(this, "styleEditorStrings", () => Services.strings.createBundle(styleEditorProps));
@ -54,15 +57,31 @@ let Tools = {};
exports.Tools = Tools; exports.Tools = Tools;
// Definitions // Definitions
Tools.options = {
id: "options",
ordinal: 0,
url: "chrome://browser/content/devtools/framework/toolbox-options.xul",
icon: "chrome://browser/skin/devtools/tool-options.png",
tooltip: l10n("optionsButton.tooltip", toolboxStrings),
isTargetSupported: function(target) {
return true;
},
build: function(iframeWindow, toolbox) {
let panel = new OptionsPanel(iframeWindow, toolbox);
return panel.open();
}
}
Tools.webConsole = { Tools.webConsole = {
id: "webconsole", id: "webconsole",
key: l10n("cmd.commandkey", webConsoleStrings), key: l10n("cmd.commandkey", webConsoleStrings),
accesskey: l10n("webConsoleCmd.accesskey", webConsoleStrings), accesskey: l10n("webConsoleCmd.accesskey", webConsoleStrings),
modifiers: Services.appinfo.OS == "Darwin" ? "accel,alt" : "accel,shift", modifiers: Services.appinfo.OS == "Darwin" ? "accel,alt" : "accel,shift",
ordinal: 0, ordinal: 1,
icon: "chrome://browser/skin/devtools/tool-webconsole.png", icon: "chrome://browser/skin/devtools/tool-webconsole.png",
url: "chrome://browser/content/devtools/webconsole.xul", url: "chrome://browser/content/devtools/webconsole.xul",
label: l10n("ToolboxWebconsole.label", webConsoleStrings), label: l10n("ToolboxTabWebconsole.label", webConsoleStrings),
menuLabel: l10n("MenuWebconsole.label", webConsoleStrings),
tooltip: l10n("ToolboxWebconsole.tooltip", webConsoleStrings), tooltip: l10n("ToolboxWebconsole.tooltip", webConsoleStrings),
isTargetSupported: function(target) { isTargetSupported: function(target) {
@ -74,33 +93,11 @@ Tools.webConsole = {
} }
}; };
Tools.jsdebugger = {
id: "jsdebugger",
key: l10n("open.commandkey", debuggerStrings),
accesskey: l10n("debuggerMenu.accesskey", debuggerStrings),
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
ordinal: 2,
killswitch: "devtools.debugger.enabled",
icon: "chrome://browser/skin/devtools/tool-debugger.png",
url: "chrome://browser/content/devtools/debugger.xul",
label: l10n("ToolboxDebugger.label", debuggerStrings),
tooltip: l10n("ToolboxDebugger.tooltip", debuggerStrings),
isTargetSupported: function(target) {
return true;
},
build: function(iframeWindow, toolbox) {
let panel = new DebuggerPanel(iframeWindow, toolbox);
return panel.open();
}
};
Tools.inspector = { Tools.inspector = {
id: "inspector", id: "inspector",
accesskey: l10n("inspector.accesskey", inspectorStrings), accesskey: l10n("inspector.accesskey", inspectorStrings),
key: l10n("inspector.commandkey", inspectorStrings), key: l10n("inspector.commandkey", inspectorStrings),
ordinal: 1, ordinal: 2,
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift", modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
icon: "chrome://browser/skin/devtools/tool-inspector.png", icon: "chrome://browser/skin/devtools/tool-inspector.png",
url: "chrome://browser/content/devtools/inspector/inspector.xul", url: "chrome://browser/content/devtools/inspector/inspector.xul",
@ -117,10 +114,32 @@ Tools.inspector = {
} }
}; };
Tools.jsdebugger = {
id: "jsdebugger",
key: l10n("open.commandkey", debuggerStrings),
accesskey: l10n("debuggerMenu.accesskey", debuggerStrings),
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
ordinal: 3,
visibilityswitch: "devtools.debugger.enabled",
icon: "chrome://browser/skin/devtools/tool-debugger.png",
url: "chrome://browser/content/devtools/debugger.xul",
label: l10n("ToolboxDebugger.label", debuggerStrings),
tooltip: l10n("ToolboxDebugger.tooltip", debuggerStrings),
isTargetSupported: function(target) {
return true;
},
build: function(iframeWindow, toolbox) {
let panel = new DebuggerPanel(iframeWindow, toolbox);
return panel.open();
}
};
Tools.styleEditor = { Tools.styleEditor = {
id: "styleeditor", id: "styleeditor",
key: l10n("open.commandkey", styleEditorStrings), key: l10n("open.commandkey", styleEditorStrings),
ordinal: 3, ordinal: 4,
accesskey: l10n("open.accesskey", styleEditorStrings), accesskey: l10n("open.accesskey", styleEditorStrings),
modifiers: "shift", modifiers: "shift",
icon: "chrome://browser/skin/devtools/tool-styleeditor.png", icon: "chrome://browser/skin/devtools/tool-styleeditor.png",
@ -142,9 +161,9 @@ Tools.jsprofiler = {
id: "jsprofiler", id: "jsprofiler",
accesskey: l10n("profiler.accesskey", profilerStrings), accesskey: l10n("profiler.accesskey", profilerStrings),
key: l10n("profiler2.commandkey", profilerStrings), key: l10n("profiler2.commandkey", profilerStrings),
ordinal: 4, ordinal: 5,
modifiers: "shift", modifiers: "shift",
killswitch: "devtools.profiler.enabled", visibilityswitch: "devtools.profiler.enabled",
icon: "chrome://browser/skin/devtools/tool-profiler.png", icon: "chrome://browser/skin/devtools/tool-profiler.png",
url: "chrome://browser/content/devtools/profiler.xul", url: "chrome://browser/content/devtools/profiler.xul",
label: l10n("profiler.label", profilerStrings), label: l10n("profiler.label", profilerStrings),
@ -164,10 +183,10 @@ Tools.netMonitor = {
id: "netmonitor", id: "netmonitor",
accesskey: l10n("netmonitor.accesskey", netMonitorStrings), accesskey: l10n("netmonitor.accesskey", netMonitorStrings),
key: l10n("netmonitor.commandkey", netMonitorStrings), key: l10n("netmonitor.commandkey", netMonitorStrings),
ordinal: 5, ordinal: 6,
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift", modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
killswitch: "devtools.netmonitor.enabled", visibilityswitch: "devtools.netmonitor.enabled",
icon: "chrome://browser/skin/devtools/tool-profiler.png", icon: "chrome://browser/skin/devtools/tool-network.png",
url: "chrome://browser/content/devtools/netmonitor.xul", url: "chrome://browser/content/devtools/netmonitor.xul",
label: l10n("netmonitor.label", netMonitorStrings), label: l10n("netmonitor.label", netMonitorStrings),
tooltip: l10n("netmonitor.tooltip", netMonitorStrings), tooltip: l10n("netmonitor.tooltip", netMonitorStrings),
@ -183,6 +202,7 @@ Tools.netMonitor = {
}; };
let defaultTools = [ let defaultTools = [
Tools.options,
Tools.styleEditor, Tools.styleEditor,
Tools.webConsole, Tools.webConsole,
Tools.jsdebugger, Tools.jsdebugger,
@ -201,7 +221,7 @@ var unloadObserver = {
observe: function(subject, topic, data) { observe: function(subject, topic, data) {
if (subject.wrappedJSObject === require("@loader/unload")) { if (subject.wrappedJSObject === require("@loader/unload")) {
Services.obs.removeObserver(unloadObserver, "sdk:loader:destroy"); Services.obs.removeObserver(unloadObserver, "sdk:loader:destroy");
for (let definition of defaultTools) { for (let definition of gDevTools.getToolDefinitionArray()) {
gDevTools.unregisterTool(definition.id); gDevTools.unregisterTool(definition.id);
} }
} }

Просмотреть файл

@ -17,7 +17,7 @@ let EventEmitter = require("devtools/shared/event-emitter");
let {editableField, InplaceEditor} = require("devtools/shared/inplace-editor"); let {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm"); Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource:///modules/devtools/Templater.jsm"); Cu.import("resource://gre/modules/devtools/Templater.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
/** /**

Просмотреть файл

@ -5,11 +5,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict"; "use strict";
const HTML_NS = "http://www.w3.org/1999/xhtml";
const EPSILON = 0.001; const EPSILON = 0.001;
const RESIZE_REFRESH_RATE = 50; // ms
const REQUESTS_REFRESH_RATE = 50; // ms const REQUESTS_REFRESH_RATE = 50; // ms
const REQUESTS_HEADERS_SAFE_BOUNDS = 30; // px const REQUESTS_HEADERS_SAFE_BOUNDS = 30; // px
const REQUESTS_WATERFALL_SAFE_BOUNDS = 100; // px const REQUESTS_WATERFALL_SAFE_BOUNDS = 90; // px
const REQUESTS_WATERFALL_BACKGROUND_PATTERN = [5, 250, 1000, 2000]; // ms const REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE = 5; // ms
const REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN = 60; // px
const REQUESTS_WATERFALL_BACKGROUND_TICKS_MULTIPLE = 5; // ms
const REQUESTS_WATERFALL_BACKGROUND_TICKS_SCALES = 3;
const REQUESTS_WATERFALL_BACKGROUND_TICKS_SPACING_MIN = 10; // px
const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_MIN = 10; // byte
const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD = 16; // byte
const DEFAULT_HTTP_VERSION = "HTTP/1.1"; const DEFAULT_HTTP_VERSION = "HTTP/1.1";
const HEADERS_SIZE_DECIMALS = 3; const HEADERS_SIZE_DECIMALS = 3;
const CONTENT_SIZE_DECIMALS = 2; const CONTENT_SIZE_DECIMALS = 2;
@ -48,8 +56,6 @@ const GENERIC_VARIABLES_VIEW_SETTINGS = {
switch: () => {} switch: () => {}
}; };
function $(aSelector, aTarget = document) aTarget.querySelector(aSelector);
/** /**
* Object defining the network monitor view components. * Object defining the network monitor view components.
*/ */
@ -157,6 +163,7 @@ let NetMonitorView = {
$("#details-pane").selectedIndex = aTabIndex; $("#details-pane").selectedIndex = aTabIndex;
} }
}, },
/** /**
* Lazily initializes and returns a promise for a SourceEditor instance. * Lazily initializes and returns a promise for a SourceEditor instance.
* *
@ -264,6 +271,7 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
this.node = new SideMenuWidget($("#requests-menu-contents"), false); this.node = new SideMenuWidget($("#requests-menu-contents"), false);
this.node.maintainSelectionVisible = false; this.node.maintainSelectionVisible = false;
this.node.autoscrollWithAppendedItems = true;
this.node.addEventListener("mousedown", this._onMouseDown, false); this.node.addEventListener("mousedown", this._onMouseDown, false);
this.node.addEventListener("select", this._onSelect, false); this.node.addEventListener("select", this._onSelect, false);
@ -321,7 +329,6 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
// Append a network request item to this container. // Append a network request item to this container.
let requestItem = this.push(menuView, { let requestItem = this.push(menuView, {
relaxed: true, /* this container should allow dupes & degenerates */
attachment: { attachment: {
id: aId, id: aId,
startedDeltaMillis: unixTime - this._firstRequestStartedMillis, startedDeltaMillis: unixTime - this._firstRequestStartedMillis,
@ -332,10 +339,133 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
finalize: this._onRequestItemRemoved finalize: this._onRequestItemRemoved
}); });
$("#details-pane-toggle").disabled = false;
$(".requests-menu-empty-notice").hidden = true; $(".requests-menu-empty-notice").hidden = true;
this._cache.set(aId, requestItem); this._cache.set(aId, requestItem);
}, },
/**
* Sorts all network requests in this container by a specified detail.
*
* @param string aType
* Either "status", "method", "file", "domain", "type" or "size".
*/
sortBy: function NVRM_sortBy(aType) {
let target = $("#requests-menu-" + aType + "-button");
let headers = document.querySelectorAll(".requests-menu-header-button");
for (let header of headers) {
if (header != target) {
header.removeAttribute("sorted");
header.removeAttribute("tooltiptext");
}
}
let direction = "";
if (target) {
if (!target.hasAttribute("sorted")) {
target.setAttribute("sorted", direction = "ascending");
target.setAttribute("tooltiptext", L10N.getStr("networkMenu.sortedAsc"));
} else if (target.getAttribute("sorted") == "ascending") {
target.setAttribute("sorted", direction = "descending");
target.setAttribute("tooltiptext", L10N.getStr("networkMenu.sortedDesc"));
} else {
target.removeAttribute("sorted");
target.removeAttribute("tooltiptext");
}
}
// Sort by timing.
if (!target || !direction) {
this.sortContents(this._byTiming);
}
// Sort by whatever was requested.
else switch (aType) {
case "status":
if (direction == "ascending") {
this.sortContents(this._byStatus);
} else {
this.sortContents((a, b) => !this._byStatus(a, b));
}
break;
case "method":
if (direction == "ascending") {
this.sortContents(this._byMethod);
} else {
this.sortContents((a, b) => !this._byMethod(a, b));
}
break;
case "file":
if (direction == "ascending") {
this.sortContents(this._byFile);
} else {
this.sortContents((a, b) => !this._byFile(a, b));
}
break;
case "domain":
if (direction == "ascending") {
this.sortContents(this._byDomain);
} else {
this.sortContents((a, b) => !this._byDomain(a, b));
}
break;
case "type":
if (direction == "ascending") {
this.sortContents(this._byType);
} else {
this.sortContents((a, b) => !this._byType(a, b));
}
break;
case "size":
if (direction == "ascending") {
this.sortContents(this._bySize);
} else {
this.sortContents((a, b) => !this._bySize(a, b));
}
break;
}
},
/**
* Predicates used when sorting items.
*
* @param MenuItem aFirst
* The first menu item used in the comparison.
* @param MenuItem aSecond
* The second menu item used in the comparison.
* @return number
* -1 to sort aFirst to a lower index than aSecond
* 0 to leave aFirst and aSecond unchanged with respect to each other
* 1 to sort aSecond to a lower index than aFirst
*/
_byTiming: (aFirst, aSecond) =>
aFirst.attachment.startedMillis > aSecond.attachment.startedMillis,
_byStatus: (aFirst, aSecond) =>
aFirst.attachment.status > aSecond.attachment.status,
_byMethod: (aFirst, aSecond) =>
aFirst.attachment.method > aSecond.attachment.method,
_byFile: (aFirst, aSecond) =>
!aFirst.target || !aSecond.target ? -1 :
$(".requests-menu-file", aFirst.target).getAttribute("value").toLowerCase() >
$(".requests-menu-file", aSecond.target).getAttribute("value").toLowerCase(),
_byDomain: (aFirst, aSecond) =>
!aFirst.target || !aSecond.target ? -1 :
$(".requests-menu-domain", aFirst.target).getAttribute("value").toLowerCase() >
$(".requests-menu-domain", aSecond.target).getAttribute("value").toLowerCase(),
_byType: (aFirst, aSecond) =>
!aFirst.target || !aSecond.target ? -1 :
$(".requests-menu-type", aFirst.target).getAttribute("value").toLowerCase() >
$(".requests-menu-type", aSecond.target).getAttribute("value").toLowerCase(),
_bySize: (aFirst, aSecond) =>
aFirst.attachment.contentSize > aSecond.attachment.contentSize,
/** /**
* Schedules adding additional information to a network request. * Schedules adding additional information to a network request.
* *
@ -356,8 +486,8 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
if (!this.lazyUpdate) { if (!this.lazyUpdate) {
return void this._flushRequests(); return void this._flushRequests();
} }
window.clearTimeout(this._updateTimeout); // Allow requests to settle down first.
this._updateTimeout = window.setTimeout(this._flushRequests, REQUESTS_REFRESH_RATE); drain("update-requests", REQUESTS_REFRESH_RATE, () => this._flushRequests());
}, },
/** /**
@ -444,8 +574,12 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
NetMonitorView.NetworkDetails.populate(selectedItem.attachment); NetMonitorView.NetworkDetails.populate(selectedItem.attachment);
} }
} }
// We're done flushing all the requests, clear the update queue. // We're done flushing all the requests, clear the update queue.
this._updateQueue = []; this._updateQueue = [];
// Make sure all the requests are sorted.
this.sortContents();
}, },
/** /**
@ -584,38 +718,19 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
}, },
/** /**
* Rescales and redraws all the waterfalls in this container. * Rescales and redraws all the waterfall views in this container.
* *
* @param boolean aReset * @param boolean aReset
* True if this container's width was changed. * True if this container's width was changed.
*/ */
_flushWaterfallViews: function NVRM__flushWaterfallViews(aReset) { _flushWaterfallViews: function NVRM__flushWaterfallViews(aReset) {
// To avoid expensive operations like getBoundingClientRect(), the // To avoid expensive operations like getBoundingClientRect() and
// waterfalls width is cached. However, in certain scenarios like when // rebuilding the waterfall background each time a new request comes in,
// the window is resized, this needs to be invalidated. // stuff is cached. However, in certain scenarios like when the window
// is resized, this needs to be invalidated.
if (aReset) { if (aReset) {
this._cachedWaterfallWidth = 0; this._cachedWaterfallWidth = 0;
this._hideOverflowingColumns();
let table = $("#network-table");
let toolbar = $("#requests-menu-toolbar");
let columns = [
[".requests-menu-waterfall", "waterfall-overflows"],
[".requests-menu-size", "size-overflows"],
[".requests-menu-type", "type-overflows"],
[".requests-menu-domain", "domain-overflows"]
];
// Flush headers.
columns.forEach(([, attribute]) => table.removeAttribute(attribute));
let availableWidth = toolbar.getBoundingClientRect().width;
// Hide overflowing columns.
columns.forEach(([className, attribute]) => {
let bounds = $(".requests-menu-header" + className).getBoundingClientRect();
if (bounds.right > availableWidth - REQUESTS_HEADERS_SAFE_BOUNDS) {
table.setAttribute(attribute, "");
}
});
} }
// Determine the scaling to be applied to all the waterfalls so that // Determine the scaling to be applied to all the waterfalls so that
@ -624,6 +739,11 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
let longestWidth = this._lastRequestEndedMillis - this._firstRequestStartedMillis; let longestWidth = this._lastRequestEndedMillis - this._firstRequestStartedMillis;
let scale = Math.min(Math.max(availableWidth / longestWidth, EPSILON), 1); let scale = Math.min(Math.max(availableWidth / longestWidth, EPSILON), 1);
// Redraw and set the canvas background for each waterfall view.
this._showWaterfallDivisionLabels(scale);
this._drawWaterfallBackground(scale);
this._flushWaterfallBackgrounds();
// Apply CSS transforms to each waterfall in this container totalTime // Apply CSS transforms to each waterfall in this container totalTime
// accurately translate and resize as needed. // accurately translate and resize as needed.
for (let [, { target, attachment }] of this._cache) { for (let [, { target, attachment }] of this._cache) {
@ -651,6 +771,145 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
} }
}, },
/**
* Creates the labels displayed on the waterfall header in this container.
*
* @param number aScale
* The current waterfall scale.
*/
_showWaterfallDivisionLabels: function NVRM__showWaterfallDivisionLabels(aScale) {
let container = $("#requests-menu-waterfall-header-box");
let availableWidth = this._waterfallWidth - REQUESTS_WATERFALL_SAFE_BOUNDS;
// Nuke all existing labels.
while (container.hasChildNodes()) {
container.firstChild.remove();
}
// Build new millisecond tick labels...
let timingStep = REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE;
let optimalTickIntervalFound = false;
while (!optimalTickIntervalFound) {
// Ignore any divisions that would end up being too close to each other.
let scaledStep = aScale * timingStep;
if (scaledStep < REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN) {
timingStep <<= 1;
continue;
}
optimalTickIntervalFound = true;
// Insert one label for each division on the current scale.
let fragment = document.createDocumentFragment();
for (let x = 0; x < availableWidth; x += scaledStep) {
let divisionMS = (x / aScale).toFixed(0);
let translateX = "translateX(" + (x | 0) + "px)";
let node = document.createElement("label");
let text = L10N.getFormatStr("networkMenu.divisionMS", divisionMS);
node.className = "plain requests-menu-timings-division";
node.style.transform = translateX;
node.setAttribute("value", text);
fragment.appendChild(node);
}
container.appendChild(fragment);
}
},
/**
* Creates the background displayed on each waterfall view in this container.
*
* @param number aScale
* The current waterfall scale.
*/
_drawWaterfallBackground: function NVRM__drawWaterfallBackground(aScale) {
if (!this._canvas || !this._ctx) {
this._canvas = document.createElementNS(HTML_NS, "canvas");
this._ctx = this._canvas.getContext("2d");
}
let canvas = this._canvas;
let ctx = this._ctx;
// Nuke the context.
let canvasWidth = canvas.width = this._waterfallWidth;
let canvasHeight = canvas.height = 1; // Awww yeah, 1px, repeats on Y axis.
// Start over.
let imageData = ctx.createImageData(canvasWidth, canvasHeight);
let pixelArray = imageData.data;
let buf = new ArrayBuffer(pixelArray.length);
let buf8 = new Uint8ClampedArray(buf);
let data32 = new Uint32Array(buf);
// Build new millisecond tick lines...
let timingStep = REQUESTS_WATERFALL_BACKGROUND_TICKS_MULTIPLE;
let alphaComponent = REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_MIN;
let optimalTickIntervalFound = false;
while (!optimalTickIntervalFound) {
// Ignore any divisions that would end up being too close to each other.
let scaledStep = aScale * timingStep;
if (scaledStep < REQUESTS_WATERFALL_BACKGROUND_TICKS_SPACING_MIN) {
timingStep <<= 1;
continue;
}
optimalTickIntervalFound = true;
// Insert one pixel for each division on each scale.
for (let i = 1; i <= REQUESTS_WATERFALL_BACKGROUND_TICKS_SCALES; i++) {
let increment = scaledStep * Math.pow(2, i);
for (let x = 0; x < canvasWidth; x += increment) {
data32[x | 0] = (alphaComponent << 24) | (255 << 16) | (255 << 8) | 255;
}
alphaComponent += REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD;
}
}
// Flush the image data and cache the waterfall background.
pixelArray.set(buf8);
ctx.putImageData(imageData, 0, 0);
this._cachedWaterfallBackground = "url(" + canvas.toDataURL() + ")";
},
/**
* Reapplies the current waterfall background on all request items.
*/
_flushWaterfallBackgrounds: function NVRM__flushWaterfallBackgrounds() {
for (let [, { target }] of this._cache) {
let waterfallNode = $(".requests-menu-waterfall", target);
waterfallNode.style.backgroundImage = this._cachedWaterfallBackground;
}
},
/**
* Hides the overflowing columns in the requests table.
*/
_hideOverflowingColumns: function NVRM__hideOverflowingColumns() {
let table = $("#network-table");
let toolbar = $("#requests-menu-toolbar");
let columns = [
["#requests-menu-waterfall-header-box", "waterfall-overflows"],
["#requests-menu-size-header-box", "size-overflows"],
["#requests-menu-type-header-box", "type-overflows"],
["#requests-menu-domain-header-box", "domain-overflows"]
];
// Flush headers.
columns.forEach(([, attribute]) => table.removeAttribute(attribute));
let availableWidth = toolbar.getBoundingClientRect().width;
// Hide the columns.
columns.forEach(([id, attribute]) => {
let bounds = $(id).getBoundingClientRect();
if (bounds.right > availableWidth - REQUESTS_HEADERS_SAFE_BOUNDS) {
table.setAttribute(attribute, "");
}
});
},
/** /**
* Function called each time a network request item is removed. * Function called each time a network request item is removed.
* *
@ -685,7 +944,8 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
* The resize listener for this container's window. * The resize listener for this container's window.
*/ */
_onResize: function NVRM__onResize(e) { _onResize: function NVRM__onResize(e) {
this._flushWaterfallViews(true); // Allow requests to settle down first.
drain("resize-events", RESIZE_REFRESH_RATE, () => this._flushWaterfallViews(true));
}, },
/** /**
@ -721,7 +981,7 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
get _waterfallWidth() { get _waterfallWidth() {
if (this._cachedWaterfallWidth == 0) { if (this._cachedWaterfallWidth == 0) {
let container = $("#requests-menu-toolbar"); let container = $("#requests-menu-toolbar");
let waterfall = $("#requests-menu-waterfall-label"); let waterfall = $("#requests-menu-waterfall-header-box");
let containerBounds = container.getBoundingClientRect(); let containerBounds = container.getBoundingClientRect();
let waterfallBounds = waterfall.getBoundingClientRect(); let waterfallBounds = waterfall.getBoundingClientRect();
this._cachedWaterfallWidth = containerBounds.width - waterfallBounds.left; this._cachedWaterfallWidth = containerBounds.width - waterfallBounds.left;
@ -730,11 +990,15 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
}, },
_cache: null, _cache: null,
_canvas: null,
_ctx: null,
_cachedWaterfallWidth: 0, _cachedWaterfallWidth: 0,
_cachedWaterfallBackground: null,
_firstRequestStartedMillis: -1, _firstRequestStartedMillis: -1,
_lastRequestEndedMillis: -1, _lastRequestEndedMillis: -1,
_updateQueue: [], _updateQueue: [],
_updateTimeout: null _updateTimeout: null,
_resizeTimeout: null
}); });
/** /**
@ -1116,7 +1380,7 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
$("#response-content-textarea-box").hidden = false; $("#response-content-textarea-box").hidden = false;
NetMonitorView.editor("#response-content-textarea").then((aEditor) => { NetMonitorView.editor("#response-content-textarea").then((aEditor) => {
aEditor.setMode(SourceEditor.MODES.TEXT); aEditor.setMode(SourceEditor.MODES.TEXT);
aEditor.setText(aString); aEditor.setText(NetworkHelper.convertToUnicode(aString, "UTF-8"));
// Maybe set a more appropriate mode in the Source Editor if possible. // Maybe set a more appropriate mode in the Source Editor if possible.
for (let key in CONTENT_MIME_TYPE_MAPPINGS) { for (let key in CONTENT_MIME_TYPE_MAPPINGS) {
@ -1213,6 +1477,22 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
_responseCookies: "" _responseCookies: ""
}); });
/**
* DOM query helper.
*/
function $(aSelector, aTarget = document) aTarget.querySelector(aSelector);
/**
* Helper for draining a rapid succession of events and invoking a callback
* once everything settles down.
*/
function drain(aId, aWait, aCallback) {
window.clearTimeout(drain.store.get(aId));
drain.store.set(aId, window.setTimeout(aCallback, aWait));
}
drain.store = new Map();
/** /**
* Preliminary setup for the NetMonitorView object. * Preliminary setup for the NetMonitorView object.
*/ */

Просмотреть файл

@ -3,6 +3,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#details-pane-toggle[disabled] {
visibility: hidden;
}
#response-content-image-box { #response-content-image-box {
overflow: auto; overflow: auto;
} }

Просмотреть файл

@ -22,34 +22,75 @@
<toolbar id="requests-menu-toolbar" <toolbar id="requests-menu-toolbar"
class="devtools-toolbar" class="devtools-toolbar"
align="center"> align="center">
<label id="requests-menu-status-and-method-label" <hbox id="toolbar-labels" flex="1">
class="plain requests-menu-header requests-menu-status-and-method" <hbox id="requests-menu-status-and-method-header-box"
value="&netmonitorUI.toolbar.method;" class="requests-menu-header requests-menu-status-and-method"
crop="end"/> align="center">
<label id="requests-menu-file-label" <button id="requests-menu-status-button"
class="plain requests-menu-header requests-menu-file" class="requests-menu-header-button requests-menu-status"
value="&netmonitorUI.toolbar.file;" onclick="NetMonitorView.RequestsMenu.sortBy('status')">
crop="end"/> &netmonitorUI.toolbar.status;
<label id="requests-menu-domain-label" </button>
class="plain requests-menu-header requests-menu-domain" <button id="requests-menu-method-button"
value="&netmonitorUI.toolbar.domain;" class="requests-menu-header-button requests-menu-method"
crop="end"/> onclick="NetMonitorView.RequestsMenu.sortBy('method')"
<label id="requests-menu-type-label" flex="1">
class="plain requests-menu-header requests-menu-type" &netmonitorUI.toolbar.method;
value="&netmonitorUI.toolbar.type;" </button>
crop="end"/> </hbox>
<label id="requests-menu-size-label" <hbox id="requests-menu-file-header-box"
class="plain requests-menu-header requests-menu-size" class="requests-menu-header requests-menu-file"
value="&netmonitorUI.toolbar.size;" align="center">
crop="end"/> <button id="requests-menu-file-button"
<label id="requests-menu-waterfall-label" class="requests-menu-header-button requests-menu-file"
class="plain requests-menu-header requests-menu-waterfall" onclick="NetMonitorView.RequestsMenu.sortBy('file')"
value="&netmonitorUI.toolbar.waterfall;" flex="1">
crop="end"/> &netmonitorUI.toolbar.file;
</button>
</hbox>
<hbox id="requests-menu-domain-header-box"
class="requests-menu-header requests-menu-domain"
align="center">
<button id="requests-menu-domain-button"
class="requests-menu-header-button requests-menu-domain"
onclick="NetMonitorView.RequestsMenu.sortBy('domain')"
flex="1">
&netmonitorUI.toolbar.domain;
</button>
</hbox>
<hbox id="requests-menu-type-header-box"
class="requests-menu-header requests-menu-type"
align="center">
<button id="requests-menu-type-button"
class="requests-menu-header-button requests-menu-type"
onclick="NetMonitorView.RequestsMenu.sortBy('type')"
flex="1">
&netmonitorUI.toolbar.type;
</button>
</hbox>
<hbox id="requests-menu-size-header-box"
class="requests-menu-header requests-menu-size"
align="center">
<button id="requests-menu-size-button"
class="requests-menu-header-button requests-menu-size"
onclick="NetMonitorView.RequestsMenu.sortBy('size')"
flex="1">
&netmonitorUI.toolbar.size;
</button>
</hbox>
<hbox id="requests-menu-waterfall-header-box"
class="requests-menu-header requests-menu-waterfall"
align="center">
<label id="requests-menu-waterfall-label"
class="plain requests-menu-waterfall"
value="&netmonitorUI.toolbar.waterfall;"/>
</hbox>
</hbox>
<spacer id="toolbar-spacer" flex="1"/> <spacer id="toolbar-spacer" flex="1"/>
<toolbarbutton id="details-pane-toggle" <toolbarbutton id="details-pane-toggle"
class="devtools-toolbarbutton" class="devtools-toolbarbutton"
tooltiptext="&netmonitorUI.panesButton.tooltip;" tooltiptext="&netmonitorUI.panesButton.tooltip;"
disabled="true"
tabindex="0"/> tabindex="0"/>
</toolbar> </toolbar>
<label class="plain requests-menu-empty-notice" <label class="plain requests-menu-empty-notice"
@ -58,7 +99,7 @@
<hbox id="requests-menu-item-template" hidden="true"> <hbox id="requests-menu-item-template" hidden="true">
<hbox class="requests-menu-subitem requests-menu-status-and-method" <hbox class="requests-menu-subitem requests-menu-status-and-method"
align="center"> align="center">
<hbox class="requests-menu-status"/> <box class="requests-menu-status"/>
<label class="plain requests-menu-method" <label class="plain requests-menu-method"
crop="end" crop="end"
flex="1"/> flex="1"/>
@ -124,7 +165,7 @@
align="center"> align="center">
<label class="plain tabpanel-summary-label" <label class="plain tabpanel-summary-label"
value="&netmonitorUI.summary.status;"/> value="&netmonitorUI.summary.status;"/>
<hbox id="headers-summary-status-circle" <box id="headers-summary-status-circle"
class="requests-menu-status"/> class="requests-menu-status"/>
<label id="headers-summary-status-value" <label id="headers-summary-status-value"
class="plain tabpanel-summary-value" class="plain tabpanel-summary-value"

Просмотреть файл

@ -12,6 +12,7 @@ include $(DEPTH)/config/autoconf.mk
MOCHITEST_BROWSER_TESTS = \ MOCHITEST_BROWSER_TESTS = \
browser_net_aaa_leaktest.js \ browser_net_aaa_leaktest.js \
browser_net_autoscroll.js \
browser_net_simple-init.js \ browser_net_simple-init.js \
browser_net_page-nav.js \ browser_net_page-nav.js \
browser_net_prefs-and-l10n.js \ browser_net_prefs-and-l10n.js \
@ -21,10 +22,15 @@ MOCHITEST_BROWSER_TESTS = \
browser_net_simple-request-data.js \ browser_net_simple-request-data.js \
browser_net_simple-request-details.js \ browser_net_simple-request-details.js \
browser_net_content-type.js \ browser_net_content-type.js \
browser_net_cyrillic.js \
browser_net_status-codes.js \ browser_net_status-codes.js \
browser_net_post-data.js \ browser_net_post-data.js \
browser_net_jsonp.js \ browser_net_jsonp.js \
browser_net_json-long.js \ browser_net_json-long.js \
browser_net_timeline_ticks.js \
browser_net_sort-01.js \
browser_net_sort-02.js \
browser_net_sort-03.js \
head.js \ head.js \
$(NULL) $(NULL)
@ -33,13 +39,17 @@ MOCHITEST_BROWSER_PAGES = \
html_simple-test-page.html \ html_simple-test-page.html \
html_navigate-test-page.html \ html_navigate-test-page.html \
html_content-type-test-page.html \ html_content-type-test-page.html \
html_cyrillic-test-page.html \
html_status-codes-test-page.html \ html_status-codes-test-page.html \
html_post-data-test-page.html \ html_post-data-test-page.html \
html_jsonp-test-page.html \ html_jsonp-test-page.html \
html_json-long-test-page.html \ html_json-long-test-page.html \
html_sorting-test-page.html \
html_infinite-get-page.html \
sjs_simple-test-server.sjs \ sjs_simple-test-server.sjs \
sjs_content-type-test-server.sjs \ sjs_content-type-test-server.sjs \
sjs_status-codes-test-server.sjs \ sjs_status-codes-test-server.sjs \
sjs_sorting-test-server.sjs \
$(NULL) $(NULL)
MOCHITEST_BROWSER_FILES_PARTS = MOCHITEST_BROWSER_TESTS MOCHITEST_BROWSER_PAGES MOCHITEST_BROWSER_FILES_PARTS = MOCHITEST_BROWSER_TESTS MOCHITEST_BROWSER_PAGES

Просмотреть файл

@ -0,0 +1,77 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Bug 863102 - Automatically scroll down upon new network requests.
*/
function test() {
let monitor, debuggee, requestsContainer, scrollTop;
initNetMonitor(INFINITE_GET_URL).then(([aTab, aDebuggee, aMonitor]) => {
monitor = aMonitor;
debuggee = aDebuggee;
let win = monitor.panelWin;
let topNode = win.document.getElementById("requests-menu-contents");
requestsContainer = topNode.getElementsByTagName("scrollbox")[0];
ok(!!requestsContainer, "Container element exists as expected.");
})
// (1) Check that the scroll position is maintained at the bottom
// when the requests overflow the vertical size of the container.
.then(() => {
debuggee.performRequests();
return waitForRequestsToOverflowContainer(monitor, requestsContainer);
}).then(() => {
ok(scrolledToBottom(requestsContainer), "Scrolled to bottom on overflow.");
})
// (2) Now set the scroll position somewhere in the middle and check
// that additional requests do not change the scroll position.
.then(() => {
let children = requestsContainer.childNodes;
let middleNode = children.item(children.length / 2);
middleNode.scrollIntoView();
ok(!scrolledToBottom(requestsContainer), "Not scrolled to bottom.");
scrollTop = requestsContainer.scrollTop; // save for comparison later
return waitForNetworkEvents(monitor, 8);
}).then(() => {
is(requestsContainer.scrollTop, scrollTop, "Did not scroll.");
})
// (3) Now set the scroll position back at the bottom and check that
// additional requests *do* cause the container to scroll down.
.then(() => {
requestsContainer.scrollTop = requestsContainer.scrollHeight;
ok(scrolledToBottom(requestsContainer), "Set scroll position to bottom.");
return waitForNetworkEvents(monitor, 8);
}).then(() => {
ok(scrolledToBottom(requestsContainer), "Still scrolled to bottom.");
})
// Done; clean up.
.then(() => {
return teardown(monitor).then(finish);
})
// Handle exceptions in the chain of promises.
.then(null, (err) => {
ok(false, err);
finish();
});
function waitForRequestsToOverflowContainer (aMonitor, aContainer) {
return waitForNetworkEvents(aMonitor, 1).then(() => {
if (aContainer.scrollHeight > aContainer.clientHeight) {
// Wait for some more just for good measure.
return waitForNetworkEvents(aMonitor, 8);
} else {
return waitForRequestsToOverflowContainer(aMonitor, aContainer);
}
});
}
function scrolledToBottom(aElement) {
return aElement.scrollTop + aElement.clientHeight >= aElement.scrollHeight;
}
}

Просмотреть файл

@ -0,0 +1,41 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if cyrillic text is rendered correctly in the source editor.
*/
function test() {
initNetMonitor(CYRILLIC_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
let { document, L10N, SourceEditor, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
waitForNetworkEvents(aMonitor, 1).then(() => {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
"GET", CONTENT_TYPE_SJS + "?fmt=txt", {
status: 200,
statusText: "DA DA DA"
});
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll("#details-pane tab")[3]);
NetMonitorView.editor("#response-content-textarea").then((aEditor) => {
is(aEditor.getText().indexOf("\u044F"), 26, // я
"The text shown in the source editor is incorrect.");
is(aEditor.getMode(), SourceEditor.MODES.TEXT,
"The mode active in the source editor is incorrect.");
teardown(aMonitor).then(finish);
});
});
aDebuggee.performRequests();
});
}

Просмотреть файл

@ -14,6 +14,9 @@ function test() {
RequestsMenu.lazyUpdate = false; RequestsMenu.lazyUpdate = false;
is(document.querySelector("#details-pane-toggle")
.hasAttribute("disabled"), true,
"The pane toggle button should be disabled when the frontend is opened.");
is(document.querySelector(".requests-menu-empty-notice") is(document.querySelector(".requests-menu-empty-notice")
.hasAttribute("hidden"), false, .hasAttribute("hidden"), false,
"An empty notice should be displayed when the frontend is opened."); "An empty notice should be displayed when the frontend is opened.");
@ -23,6 +26,9 @@ function test() {
"The details pane should be hidden when the frontend is opened."); "The details pane should be hidden when the frontend is opened.");
aMonitor.panelWin.once("NetMonitor:NetworkEvent", () => { aMonitor.panelWin.once("NetMonitor:NetworkEvent", () => {
is(document.querySelector("#details-pane-toggle")
.hasAttribute("disabled"), false,
"The pane toggle button should be enabled after the first request.");
is(document.querySelector(".requests-menu-empty-notice") is(document.querySelector(".requests-menu-empty-notice")
.hasAttribute("hidden"), true, .hasAttribute("hidden"), true,
"The empty notice should be hidden after the first request."); "The empty notice should be hidden after the first request.");
@ -32,6 +38,9 @@ function test() {
"The details pane should still be hidden after the first request."); "The details pane should still be hidden after the first request.");
aMonitor.panelWin.once("NetMonitor:NetworkEvent", () => { aMonitor.panelWin.once("NetMonitor:NetworkEvent", () => {
is(document.querySelector("#details-pane-toggle")
.hasAttribute("disabled"), false,
"The pane toggle button should be still be enabled after a reload.");
is(document.querySelector(".requests-menu-empty-notice") is(document.querySelector(".requests-menu-empty-notice")
.hasAttribute("hidden"), true, .hasAttribute("hidden"), true,
"The empty notice should be still hidden after a reload."); "The empty notice should be still hidden after a reload.");

Просмотреть файл

@ -0,0 +1,225 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test if the sorting mechanism works correctly.
*/
function test() {
initNetMonitor(STATUS_CODES_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
let { L10N, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
waitForNetworkEvents(aMonitor, 5).then(() => {
testContents([0, 1, 2, 3, 4])
.then(() => {
info("Testing swap(0, 0)");
RequestsMenu.swapItemsAtIndices(0, 0);
return testContents([0, 1, 2, 3, 4]);
})
.then(() => {
info("Testing swap(0, 1)");
RequestsMenu.swapItemsAtIndices(0, 1);
return testContents([1, 0, 2, 3, 4]);
})
.then(() => {
info("Testing swap(0, 2)");
RequestsMenu.swapItemsAtIndices(0, 2);
return testContents([1, 2, 0, 3, 4]);
})
.then(() => {
info("Testing swap(0, 3)");
RequestsMenu.swapItemsAtIndices(0, 3);
return testContents([1, 2, 3, 0, 4]);
})
.then(() => {
info("Testing swap(0, 4)");
RequestsMenu.swapItemsAtIndices(0, 4);
return testContents([1, 2, 3, 4, 0]);
})
.then(() => {
info("Testing swap(1, 0)");
RequestsMenu.swapItemsAtIndices(1, 0);
return testContents([0, 2, 3, 4, 1]);
})
.then(() => {
info("Testing swap(1, 1)");
RequestsMenu.swapItemsAtIndices(1, 1);
return testContents([0, 2, 3, 4, 1]);
})
.then(() => {
info("Testing swap(1, 2)");
RequestsMenu.swapItemsAtIndices(1, 2);
return testContents([0, 1, 3, 4, 2]);
})
.then(() => {
info("Testing swap(1, 3)");
RequestsMenu.swapItemsAtIndices(1, 3);
return testContents([0, 3, 1, 4, 2]);
})
.then(() => {
info("Testing swap(1, 4)");
RequestsMenu.swapItemsAtIndices(1, 4);
return testContents([0, 3, 4, 1, 2]);
})
.then(() => {
info("Testing swap(2, 0)");
RequestsMenu.swapItemsAtIndices(2, 0);
return testContents([2, 3, 4, 1, 0]);
})
.then(() => {
info("Testing swap(2, 1)");
RequestsMenu.swapItemsAtIndices(2, 1);
return testContents([1, 3, 4, 2, 0]);
})
.then(() => {
info("Testing swap(2, 2)");
RequestsMenu.swapItemsAtIndices(2, 2);
return testContents([1, 3, 4, 2, 0]);
})
.then(() => {
info("Testing swap(2, 3)");
RequestsMenu.swapItemsAtIndices(2, 3);
return testContents([1, 2, 4, 3, 0]);
})
.then(() => {
info("Testing swap(2, 4)");
RequestsMenu.swapItemsAtIndices(2, 4);
return testContents([1, 4, 2, 3, 0]);
})
.then(() => {
info("Testing swap(3, 0)");
RequestsMenu.swapItemsAtIndices(3, 0);
return testContents([1, 4, 2, 0, 3]);
})
.then(() => {
info("Testing swap(3, 1)");
RequestsMenu.swapItemsAtIndices(3, 1);
return testContents([3, 4, 2, 0, 1]);
})
.then(() => {
info("Testing swap(3, 2)");
RequestsMenu.swapItemsAtIndices(3, 2);
return testContents([2, 4, 3, 0, 1]);
})
.then(() => {
info("Testing swap(3, 3)");
RequestsMenu.swapItemsAtIndices(3, 3);
return testContents([2, 4, 3, 0, 1]);
})
.then(() => {
info("Testing swap(3, 4)");
RequestsMenu.swapItemsAtIndices(3, 4);
return testContents([2, 3, 4, 0, 1]);
})
.then(() => {
info("Testing swap(4, 0)");
RequestsMenu.swapItemsAtIndices(4, 0);
return testContents([2, 3, 0, 4, 1]);
})
.then(() => {
info("Testing swap(4, 1)");
RequestsMenu.swapItemsAtIndices(4, 1);
return testContents([2, 3, 0, 1, 4]);
})
.then(() => {
info("Testing swap(4, 2)");
RequestsMenu.swapItemsAtIndices(4, 2);
return testContents([4, 3, 0, 1, 2]);
})
.then(() => {
info("Testing swap(4, 3)");
RequestsMenu.swapItemsAtIndices(4, 3);
return testContents([3, 4, 0, 1, 2]);
})
.then(() => {
info("Testing swap(4, 4)");
RequestsMenu.swapItemsAtIndices(4, 4);
return testContents([3, 4, 0, 1, 2]);
})
.then(() => {
RequestsMenu.sortBy(null);
return testContents([0, 1, 2, 3, 4]);
})
.then(() => {
return teardown(aMonitor);
})
.then(finish);
});
function testContents([a, b, c, d, e]) {
let deferred = Promise.defer();
is(RequestsMenu.allItems.length, 5,
"There should be a total of 5 items in the requests menu.");
is(RequestsMenu.visibleItems.length, 5,
"There should be a total of 5 visbile items in the requests menu.");
is(RequestsMenu.getItemAtIndex(0), RequestsMenu.allItems[0],
"The requests menu items aren't ordered correctly. First item is misplaced.");
is(RequestsMenu.getItemAtIndex(1), RequestsMenu.allItems[1],
"The requests menu items aren't ordered correctly. Second item is misplaced.");
is(RequestsMenu.getItemAtIndex(2), RequestsMenu.allItems[2],
"The requests menu items aren't ordered correctly. Third item is misplaced.");
is(RequestsMenu.getItemAtIndex(3), RequestsMenu.allItems[3],
"The requests menu items aren't ordered correctly. Fourth item is misplaced.");
is(RequestsMenu.getItemAtIndex(4), RequestsMenu.allItems[4],
"The requests menu items aren't ordered correctly. Fifth item is misplaced.");
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(a),
"GET", STATUS_CODES_SJS + "?sts=100", {
status: 101,
statusText: "Switching Protocols",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(b),
"GET", STATUS_CODES_SJS + "?sts=200", {
status: 202,
statusText: "Created",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.02),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c),
"GET", STATUS_CODES_SJS + "?sts=300", {
status: 303,
statusText: "See Other",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d),
"GET", STATUS_CODES_SJS + "?sts=400", {
status: 404,
statusText: "Not Found",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.02),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e),
"GET", STATUS_CODES_SJS + "?sts=500", {
status: 501,
statusText: "Not Implemented",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.02),
time: true
});
executeSoon(deferred.resolve);
return deferred.promise;
}
aDebuggee.performRequests();
});
}

Просмотреть файл

@ -0,0 +1,225 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test if sorting columns in the network table works correctly.
*/
function test() {
initNetMonitor(SORTING_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
let { $, L10N, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
waitForNetworkEvents(aMonitor, 5).then(() => {
EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle"));
isnot(RequestsMenu.selectedItem, null,
"There should be a selected item in the requests menu.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be selected in the requests menu.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should not be hidden after toggle button was pressed.");
testHeaders();
testContents([0, 2, 4, 3, 1])
.then(() => {
info("Testing status sort, ascending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button"));
testHeaders("status", "ascending");
return testContents([0, 1, 2, 3, 4]);
})
.then(() => {
info("Testing status sort, descending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button"));
testHeaders("status", "descending");
return testContents([4, 3, 2, 1, 0]);
})
.then(() => {
info("Clearing status sort.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button"));
testHeaders();
return testContents([0, 2, 4, 3, 1]);
})
.then(() => {
info("Testing method sort, ascending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-method-button"));
testHeaders("method", "ascending");
return testContents([0, 1, 2, 3, 4]);
})
.then(() => {
info("Testing method sort, descending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-method-button"));
testHeaders("method", "descending");
return testContents([4, 3, 2, 1, 0]);
})
.then(() => {
info("Clearing method sort.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-method-button"));
testHeaders();
return testContents([0, 2, 4, 3, 1]);
})
.then(() => {
info("Testing file sort, ascending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-file-button"));
testHeaders("file", "ascending");
return testContents([0, 1, 2, 3, 4]);
})
.then(() => {
info("Testing file sort, descending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-file-button"));
testHeaders("file", "descending");
return testContents([4, 3, 2, 1, 0]);
})
.then(() => {
info("Clearing file sort.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-file-button"));
testHeaders();
return testContents([0, 2, 4, 3, 1]);
})
.then(() => {
info("Testing type sort, ascending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-type-button"));
testHeaders("type", "ascending");
return testContents([0, 1, 2, 3, 4]);
})
.then(() => {
info("Testing type sort, descending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-type-button"));
testHeaders("type", "descending");
return testContents([4, 3, 2, 1, 0]);
})
.then(() => {
info("Clearing type sort.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-type-button"));
testHeaders();
return testContents([0, 2, 4, 3, 1]);
})
.then(() => {
info("Testing size sort, ascending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button"));
testHeaders("size", "ascending");
return testContents([0, 1, 2, 3, 4]);
})
.then(() => {
info("Testing size sort, descending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button"));
testHeaders("size", "descending");
return testContents([4, 3, 2, 1, 0]);
})
.then(() => {
info("Clearing size sort.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button"));
testHeaders();
return testContents([0, 2, 4, 3, 1]);
})
.then(() => {
return teardown(aMonitor);
})
.then(finish);
});
function testHeaders(aSortType, aDirection) {
let doc = aMonitor.panelWin.document;
let target = doc.querySelector("#requests-menu-" + aSortType + "-button");
let headers = doc.querySelectorAll(".requests-menu-header-button");
for (let header of headers) {
if (header != target) {
is(header.hasAttribute("sorted"), false,
"The " + header.id + " header should not have a 'sorted' attribute.");
is(header.hasAttribute("tooltiptext"), false,
"The " + header.id + " header should not have a 'tooltiptext' attribute.");
} else {
is(header.getAttribute("sorted"), aDirection,
"The " + header.id + " header has an incorrect 'sorted' attribute.");
is(header.getAttribute("tooltiptext"), aDirection == "ascending"
? L10N.getStr("networkMenu.sortedAsc")
: L10N.getStr("networkMenu.sortedDesc"),
"The " + header.id + " has an incorrect 'tooltiptext' attribute.");
}
}
}
function testContents([a, b, c, d, e]) {
let deferred = Promise.defer();
isnot(RequestsMenu.selectedItem, null,
"There should still be a selected item after sorting.");
is(RequestsMenu.selectedIndex, a,
"The first item should be still selected after sorting.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should still be visible after sorting.");
is(RequestsMenu.allItems.length, 5,
"There should be a total of 5 items in the requests menu.");
is(RequestsMenu.visibleItems.length, 5,
"There should be a total of 5 visbile items in the requests menu.");
is(RequestsMenu.getItemAtIndex(0), RequestsMenu.allItems[0],
"The requests menu items aren't ordered correctly. First item is misplaced.");
is(RequestsMenu.getItemAtIndex(1), RequestsMenu.allItems[1],
"The requests menu items aren't ordered correctly. Second item is misplaced.");
is(RequestsMenu.getItemAtIndex(2), RequestsMenu.allItems[2],
"The requests menu items aren't ordered correctly. Third item is misplaced.");
is(RequestsMenu.getItemAtIndex(3), RequestsMenu.allItems[3],
"The requests menu items aren't ordered correctly. Fourth item is misplaced.");
is(RequestsMenu.getItemAtIndex(4), RequestsMenu.allItems[4],
"The requests menu items aren't ordered correctly. Fifth item is misplaced.");
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(a),
"GET1", SORTING_SJS + "?index=1", {
status: 101,
statusText: "Meh",
type: "1",
fullMimeType: "text/1",
size: L10N.getFormatStr("networkMenu.sizeKB", 0),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(b),
"GET2", SORTING_SJS + "?index=2", {
status: 200,
statusText: "Meh",
type: "2",
fullMimeType: "text/2",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.01),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c),
"GET3", SORTING_SJS + "?index=3", {
status: 300,
statusText: "Meh",
type: "3",
fullMimeType: "text/3",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.02),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d),
"GET4", SORTING_SJS + "?index=4", {
status: 400,
statusText: "Meh",
type: "4",
fullMimeType: "text/4",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.03),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e),
"GET5", SORTING_SJS + "?index=5", {
status: 500,
statusText: "Meh",
type: "5",
fullMimeType: "text/5",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.04),
time: true
});
executeSoon(deferred.resolve);
return deferred.promise;
}
aDebuggee.performRequests();
});
}

Просмотреть файл

@ -0,0 +1,171 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test if sorting columns in the network table works correctly with new requests.
*/
function test() {
initNetMonitor(SORTING_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
let { $, L10N, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
waitForNetworkEvents(aMonitor, 5).then(() => {
EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle"));
isnot(RequestsMenu.selectedItem, null,
"There should be a selected item in the requests menu.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be selected in the requests menu.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should not be hidden after toggle button was pressed.");
testHeaders();
testContents([0, 2, 4, 3, 1], 0)
.then(() => {
info("Testing status sort, ascending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button"));
testHeaders("status", "ascending");
return testContents([0, 1, 2, 3, 4], 0);
})
.then(() => {
info("Performing more requests.");
aDebuggee.performRequests();
return waitForNetworkEvents(aMonitor, 5);
})
.then(() => {
info("Testing status sort again, ascending.");
testHeaders("status", "ascending");
return testContents([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0);
})
.then(() => {
info("Testing status sort, descending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-status-button"));
testHeaders("status", "descending");
return testContents([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 9);
})
.then(() => {
info("Performing more requests.");
aDebuggee.performRequests();
return waitForNetworkEvents(aMonitor, 5);
})
.then(() => {
info("Testing status sort again, descending.");
testHeaders("status", "descending");
return testContents([14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], 12);
})
.then(() => {
return teardown(aMonitor);
})
.then(finish);
});
function testHeaders(aSortType, aDirection) {
let doc = aMonitor.panelWin.document;
let target = doc.querySelector("#requests-menu-" + aSortType + "-button");
let headers = doc.querySelectorAll(".requests-menu-header-button");
for (let header of headers) {
if (header != target) {
is(header.hasAttribute("sorted"), false,
"The " + header.id + " header should not have a 'sorted' attribute.");
is(header.hasAttribute("tooltiptext"), false,
"The " + header.id + " header should not have a 'tooltiptext' attribute.");
} else {
is(header.getAttribute("sorted"), aDirection,
"The " + header.id + " header has an incorrect 'sorted' attribute.");
is(header.getAttribute("tooltiptext"), aDirection == "ascending"
? L10N.getStr("networkMenu.sortedAsc")
: L10N.getStr("networkMenu.sortedDesc"),
"The " + header.id + " has an incorrect 'tooltiptext' attribute.");
}
}
}
function testContents(aOrder, aSelection) {
let deferred = Promise.defer();
isnot(RequestsMenu.selectedItem, null,
"There should still be a selected item after sorting.");
is(RequestsMenu.selectedIndex, aSelection,
"The first item should be still selected after sorting.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should still be visible after sorting.");
is(RequestsMenu.allItems.length, aOrder.length,
"There should be a specific number of items in the requests menu.");
is(RequestsMenu.visibleItems.length, aOrder.length,
"There should be a specific number of visbile items in the requests menu.");
for (let i = 0; i < aOrder.length; i++) {
is(RequestsMenu.getItemAtIndex(i), RequestsMenu.allItems[i],
"The requests menu items aren't ordered correctly. Misplaced item " + i + ".");
}
for (let i = 0, len = aOrder.length / 5; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i]),
"GET1", SORTING_SJS + "?index=1", {
status: 101,
statusText: "Meh",
type: "1",
fullMimeType: "text/1",
size: L10N.getFormatStr("networkMenu.sizeKB", 0),
time: true
});
}
for (let i = 0, len = aOrder.length / 5; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i + len]),
"GET2", SORTING_SJS + "?index=2", {
status: 200,
statusText: "Meh",
type: "2",
fullMimeType: "text/2",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.01),
time: true
});
}
for (let i = 0, len = aOrder.length / 5; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i + len * 2]),
"GET3", SORTING_SJS + "?index=3", {
status: 300,
statusText: "Meh",
type: "3",
fullMimeType: "text/3",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.02),
time: true
});
}
for (let i = 0, len = aOrder.length / 5; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i + len * 3]),
"GET4", SORTING_SJS + "?index=4", {
status: 400,
statusText: "Meh",
type: "4",
fullMimeType: "text/4",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.03),
time: true
});
}
for (let i = 0, len = aOrder.length / 5; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i + len * 4]),
"GET5", SORTING_SJS + "?index=5", {
status: 500,
statusText: "Meh",
type: "5",
fullMimeType: "text/5",
size: L10N.getFormatStr("networkMenu.sizeKB", 0.04),
time: true
});
}
executeSoon(deferred.resolve);
return deferred.promise;
}
aDebuggee.performRequests();
});
}

Просмотреть файл

@ -0,0 +1,137 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if timeline correctly displays interval divisions.
*/
function test() {
initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
let { document, L10N, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
is(document.querySelector(".requests-menu-empty-notice")
.hasAttribute("hidden"), false,
"An timeline label should be displayed when the frontend is opened.");
ok(document.querySelectorAll(".requests-menu-timings-division").length == 0,
"No tick labels should be displayed when the frontend is opened.");
ok(!RequestsMenu._canvas,
"No canvas should be created when the frontend is opened.");
ok(!RequestsMenu._ctx,
"No 2d context should be created when the frontend is opened.");
waitForNetworkEvents(aMonitor, 1).then(() => {
is(document.querySelector(".requests-menu-empty-notice")
.hasAttribute("hidden"), true,
"The timeline label should be hidden after the first request.");
ok(document.querySelectorAll(".requests-menu-timings-division").length >= 3,
"There should be at least 3 tick labels in the network requests header.");
is(document.querySelectorAll(".requests-menu-timings-division")[0]
.getAttribute("value"), L10N.getFormatStr("networkMenu.divisionMS", 0),
"The first tick label has an incorrect value");
is(document.querySelectorAll(".requests-menu-timings-division")[1]
.getAttribute("value"), L10N.getFormatStr("networkMenu.divisionMS", 80),
"The second tick label has an incorrect value");
is(document.querySelectorAll(".requests-menu-timings-division")[2]
.getAttribute("value"), L10N.getFormatStr("networkMenu.divisionMS", 160),
"The third tick label has an incorrect value");
is(document.querySelectorAll(".requests-menu-timings-division")[0]
.style.transform, "translateX(0px)",
"The first tick label has an incorrect translation");
is(document.querySelectorAll(".requests-menu-timings-division")[1]
.style.transform, "translateX(80px)",
"The second tick label has an incorrect translation");
is(document.querySelectorAll(".requests-menu-timings-division")[2]
.style.transform, "translateX(160px)",
"The third tick label has an incorrect translation");
ok(RequestsMenu._canvas,
"A canvas should be created after the first request.");
ok(RequestsMenu._ctx,
"A 2d context should be created after the first request.");
let imageData = RequestsMenu._ctx.getImageData(0, 0, 161, 1);
ok(imageData, "The image data should have been created.");
let data = imageData.data;
ok(data, "The image data should contain a pixel array.");
ok( hasPixelAt(0), "The tick at 0 is should not be empty.");
ok(!hasPixelAt(1), "The tick at 1 is should be empty.");
ok(!hasPixelAt(19), "The tick at 19 is should be empty.");
ok( hasPixelAt(20), "The tick at 20 is should not be empty.");
ok(!hasPixelAt(21), "The tick at 21 is should be empty.");
ok(!hasPixelAt(39), "The tick at 39 is should be empty.");
ok( hasPixelAt(40), "The tick at 40 is should not be empty.");
ok(!hasPixelAt(41), "The tick at 41 is should be empty.");
ok(!hasPixelAt(59), "The tick at 59 is should be empty.");
ok( hasPixelAt(60), "The tick at 60 is should not be empty.");
ok(!hasPixelAt(61), "The tick at 61 is should be empty.");
ok(!hasPixelAt(79), "The tick at 79 is should be empty.");
ok( hasPixelAt(80), "The tick at 80 is should not be empty.");
ok(!hasPixelAt(81), "The tick at 81 is should be empty.");
ok(!hasPixelAt(159), "The tick at 159 is should be empty.");
ok( hasPixelAt(160), "The tick at 160 is should not be empty.");
ok(!hasPixelAt(161), "The tick at 161 is should be empty.");
ok(isPixelBrighterAtThan(0, 20),
"The tick at 0 should be brighter than the one at 20");
ok(isPixelBrighterAtThan(40, 20),
"The tick at 40 should be brighter than the one at 20");
ok(isPixelBrighterAtThan(40, 60),
"The tick at 40 should be brighter than the one at 60");
ok(isPixelBrighterAtThan(80, 60),
"The tick at 80 should be brighter than the one at 60");
ok(isPixelBrighterAtThan(80, 100),
"The tick at 80 should be brighter than the one at 100");
ok(isPixelBrighterAtThan(120, 100),
"The tick at 120 should be brighter than the one at 100");
ok(isPixelBrighterAtThan(120, 140),
"The tick at 120 should be brighter than the one at 140");
ok(isPixelBrighterAtThan(160, 140),
"The tick at 160 should be brighter than the one at 140");
ok(isPixelEquallyBright(20, 60),
"The tick at 20 should be equally bright to the one at 60");
ok(isPixelEquallyBright(100, 140),
"The tick at 100 should be equally bright to the one at 140");
ok(isPixelEquallyBright(40, 120),
"The tick at 40 should be equally bright to the one at 120");
ok(isPixelEquallyBright(0, 80),
"The tick at 80 should be equally bright to the one at 160");
ok(isPixelEquallyBright(80, 160),
"The tick at 80 should be equally bright to the one at 160");
function hasPixelAt(x) {
let i = (x | 0) * 4;
return data[i] && data[i + 1] && data[i + 2] && data[i + 3];
}
function isPixelBrighterAtThan(x1, x2) {
let i = (x1 | 0) * 4;
let j = (x2 | 0) * 4;
return data[i + 3] > data [j + 3];
}
function isPixelEquallyBright(x1, x2) {
let i = (x1 | 0) * 4;
let j = (x2 | 0) * 4;
return data[i + 3] == data [j + 3];
}
teardown(aMonitor).then(finish);
});
aDebuggee.location.reload();
});
}

Просмотреть файл

@ -15,14 +15,18 @@ const EXAMPLE_URL = "http://example.com/browser/browser/devtools/netmonitor/test
const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html"; const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html"; const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html"; const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html";
const CYRILLIC_URL = EXAMPLE_URL + "html_cyrillic-test-page.html";
const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html"; const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html";
const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html"; const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html";
const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html"; const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html"; const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html";
const SORTING_URL = EXAMPLE_URL + "html_sorting-test-page.html";
const INFINITE_GET_URL = EXAMPLE_URL + "html_infinite-get-page.html";
const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs"; const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs";
const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs"; const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs";
const STATUS_CODES_SJS = EXAMPLE_URL + "sjs_status-codes-test-server.sjs"; const STATUS_CODES_SJS = EXAMPLE_URL + "sjs_status-codes-test-server.sjs";
const SORTING_SJS = EXAMPLE_URL + "sjs_sorting-test-server.sjs";
const TEST_IMAGE = EXAMPLE_URL + "test-image.png"; const TEST_IMAGE = EXAMPLE_URL + "test-image.png";

Просмотреть файл

@ -0,0 +1,33 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Network Monitor test page</title>
</head>
<body>
<p>Cyrillic type test</p>
<script type="text/javascript">
function get(aAddress, aCallback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", aAddress, true);
xhr.onreadystatechange = function() {
if (this.readyState == this.DONE) {
aCallback();
}
};
xhr.send(null);
}
function performRequests() {
get("sjs_content-type-test-server.sjs?fmt=txt", function() {
// Done.
});
}
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,36 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Network Monitor test page</title>
</head>
<body>
<p>Infinite GETs</p>
<script type="text/javascript">
function get(aAddress, aCallback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", aAddress, true);
xhr.onreadystatechange = function() {
if (this.readyState == this.DONE) {
aCallback();
}
};
xhr.send(null);
}
// Use a count parameter to defeat caching.
var count = 0;
function performRequests() {
get("request_" + (count++), function() {
setTimeout(performRequests, 0);
});
}
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,41 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Network Monitor test page</title>
</head>
<body>
<p>Sorting test</p>
<script type="text/javascript">
function get(aAddress, aIndex, aCallback) {
var xhr = new XMLHttpRequest();
xhr.open("GET" + aIndex, aAddress + "?index=" + aIndex, true);
xhr.onreadystatechange = function() {
if (this.readyState == this.DONE) {
aCallback();
}
};
xhr.send(null);
}
function performRequests() {
get("sjs_sorting-test-server.sjs", 1, function() {
get("sjs_sorting-test-server.sjs", 5, function() {
get("sjs_sorting-test-server.sjs", 2, function() {
get("sjs_sorting-test-server.sjs", 4, function() {
get("sjs_sorting-test-server.sjs", 3, function() {
// Done.
});
});
});
});
});
}
</script>
</body>
</html>

Просмотреть файл

@ -11,6 +11,13 @@ function handleRequest(request, response) {
Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer).initWithCallback(() => { Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer).initWithCallback(() => {
switch (format) { switch (format) {
case "txt": {
response.setStatusLine(request.httpVersion, 200, "DA DA DA");
response.setHeader("Content-Type", "text/plain", false);
response.write("Братан, ты вообще качаешься?");
response.finish();
break;
}
case "xml": { case "xml": {
response.setStatusLine(request.httpVersion, 200, "OK"); response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "text/xml; charset=utf-8", false); response.setHeader("Content-Type", "text/xml; charset=utf-8", false);

Просмотреть файл

@ -0,0 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { classes: Cc, interfaces: Ci } = Components;
function handleRequest(request, response) {
response.processAsync();
let params = request.queryString.split("&");
let index = params.filter((s) => s.contains("index="))[0].split("=")[1];
Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer).initWithCallback(() => {
response.setStatusLine(request.httpVersion, index == 1 ? 101 : index * 100, "Meh");
response.setHeader("Content-Type", "text/" + index, false);
response.write(new Array(index * 10).join(index)); // + 0.01 KB
response.finish();
}, 50, Ci.nsITimer.TYPE_ONE_SHOT); // Make sure this request takes a few ms.
}

Просмотреть файл

@ -5,7 +5,7 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components; const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
this.EXPORTED_SYMBOLS = []; this.EXPORTED_SYMBOLS = [];
Cu.import("resource:///modules/devtools/gcli.jsm"); Cu.import("resource://gre/modules/devtools/gcli.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/devtools/Require.jsm"); Cu.import("resource://gre/modules/devtools/Require.jsm");
@ -80,7 +80,7 @@ gcli.addCommand({
} }
if (profile.isFinished) { if (profile.isFinished) {
throw gcli.lookup("profilerAlradyFinished"); throw gcli.lookup("profilerAlreadyFinished");
} }
panel.switchToProfile(profile, function () profile.start()); panel.switchToProfile(profile, function () profile.start());

Просмотреть файл

@ -3,7 +3,7 @@
const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>"; const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>";
let gcli = Cu.import("resource:///modules/devtools/gcli.jsm", {}).gcli; let gcli = Cu.import("resource://gre/modules/devtools/gcli.jsm", {}).gcli;
let gTarget, gPanel, gOptions; let gTarget, gPanel, gOptions;
function cmd(typed, expected="") { function cmd(typed, expected="") {

Просмотреть файл

@ -11,7 +11,7 @@ const BRAND_SHORT_NAME = Cc["@mozilla.org/intl/stringbundle;1"].
this.EXPORTED_SYMBOLS = [ ]; this.EXPORTED_SYMBOLS = [ ];
Cu.import("resource:///modules/devtools/gcli.jsm"); Cu.import("resource://gre/modules/devtools/gcli.jsm");
/* Responsive Mode commands */ /* Responsive Mode commands */
gcli.addCommand({ gcli.addCommand({

Просмотреть файл

@ -21,11 +21,10 @@ this.EXPORTED_SYMBOLS = [ 'helpers' ];
var helpers = {}; var helpers = {};
this.helpers = helpers; this.helpers = helpers;
let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require; let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require;
Components.utils.import("resource:///modules/devtools/gcli.jsm", {}); Components.utils.import("resource://gre/modules/devtools/gcli.jsm", {});
let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console; let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
let devtools = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools; let TargetFactory = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools.TargetFactory;
let TargetFactory = devtools.TargetFactory;
let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise; let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise;
let assert = { ok: ok, is: is, log: info }; let assert = { ok: ok, is: is, log: info };
@ -158,6 +157,7 @@ helpers.runTests = function(options, tests) {
}); });
var recover = function(error) { var recover = function(error) {
ok(false, error);
console.error(error); console.error(error);
}; };
@ -341,7 +341,7 @@ helpers._createDebugCheck = function(options) {
output += ' current: \'' + helpers._actual.current(options) + '\',\n'; output += ' current: \'' + helpers._actual.current(options) + '\',\n';
output += ' status: \'' + helpers._actual.status(options) + '\',\n'; output += ' status: \'' + helpers._actual.status(options) + '\',\n';
output += ' options: ' + outputArray(helpers._actual.options(options)) + ',\n'; output += ' options: ' + outputArray(helpers._actual.options(options)) + ',\n';
output += ' error: \'' + helpers._actual.message(options) + '\',\n'; output += ' message: \'' + helpers._actual.message(options) + '\',\n';
output += ' predictions: ' + outputArray(predictions) + ',\n'; output += ' predictions: ' + outputArray(predictions) + ',\n';
output += ' unassigned: ' + outputArray(requisition._unassigned) + ',\n'; output += ' unassigned: ' + outputArray(requisition._unassigned) + ',\n';
output += ' outputState: \'' + helpers._actual.outputState(options) + '\',\n'; output += ' outputState: \'' + helpers._actual.outputState(options) + '\',\n';
@ -378,6 +378,8 @@ helpers._createDebugCheck = function(options) {
output += ' exec: {\n'; output += ' exec: {\n';
output += ' output: \'\',\n'; output += ' output: \'\',\n';
output += ' completed: true,\n'; output += ' completed: true,\n';
output += ' type: \'string\',\n';
output += ' error: false\n';
output += ' }\n'; output += ' }\n';
output += ' }\n'; output += ' }\n';
output += ']);'; output += ']);';
@ -702,7 +704,7 @@ helpers._check = function(options, name, checks) {
*/ */
helpers._exec = function(options, name, expected) { helpers._exec = function(options, name, expected) {
if (expected == null) { if (expected == null) {
return Promise.resolve(); return Promise.resolve({});
} }
var output = options.display.requisition.exec({ hidden: true }); var output = options.display.requisition.exec({ hidden: true });
@ -715,20 +717,32 @@ helpers._exec = function(options, name, expected) {
if (!options.window.document.createElement) { if (!options.window.document.createElement) {
assert.log('skipping output tests (missing doc.createElement) for ' + name); assert.log('skipping output tests (missing doc.createElement) for ' + name);
return Promise.resolve(); return Promise.resolve({ output: output });
} }
if (!('output' in expected)) { if (!('output' in expected)) {
return Promise.resolve(); return Promise.resolve({ output: output });
} }
var deferred = Promise.defer();
var checkOutput = function() { var checkOutput = function() {
var div = options.window.document.createElement('div'); var div = options.window.document.createElement('div');
var nodePromise = converters.convert(output.data, output.type, 'dom', var conversionContext = options.display.requisition.conversionContext;
options.display.requisition.context);
nodePromise.then(function(node) { if ('type' in expected) {
assert.is(output.type,
expected.type,
'output.type for: ' + name);
}
if ('error' in expected) {
assert.is(output.error,
expected.error,
'output.error for: ' + name);
}
var convertPromise = converters.convert(output.data, output.type, 'dom',
conversionContext);
return convertPromise.then(function(node) {
div.appendChild(node); div.appendChild(node);
var actualOutput = div.textContent.trim(); var actualOutput = div.textContent.trim();
@ -757,24 +771,11 @@ helpers._exec = function(options, name, expected) {
doTest(expected.output, actualOutput); doTest(expected.output, actualOutput);
} }
deferred.resolve(actualOutput); return { output: output, text: actualOutput };
}); });
}; };
if (output.completed !== false) { return output.promise.then(checkOutput, checkOutput);
checkOutput();
}
else {
var changed = function() {
if (output.completed !== false) {
checkOutput();
output.onChange.remove(changed);
}
};
output.onChange.add(changed);
}
return deferred.promise;
}; };
/** /**
@ -789,15 +790,15 @@ helpers._setup = function(options, name, action) {
return Promise.resolve(action()); return Promise.resolve(action());
} }
return Promise.reject('setup must be a string or a function'); return Promise.reject('\'setup\' property must be a string or a function. Is ' + action);
}; };
/** /**
* Helper to shutdown the test * Helper to shutdown the test
*/ */
helpers._post = function(name, action, output) { helpers._post = function(name, action, data) {
if (typeof action === 'function') { if (typeof action === 'function') {
return Promise.resolve(action(output)); return Promise.resolve(action(data.output, data.text));
} }
return Promise.resolve(action); return Promise.resolve(action);
}; };
@ -943,19 +944,22 @@ helpers.audit = function(options, audits) {
if (typeof chunkLen !== 'number') { if (typeof chunkLen !== 'number') {
chunkLen = 1; chunkLen = 1;
} }
var responseTime = (new Date().getTime() - start) / chunkLen;
totalResponseTime += responseTime; if (assert.currentTest) {
if (responseTime > maxResponseTime) { var responseTime = (new Date().getTime() - start) / chunkLen;
maxResponseTime = responseTime; totalResponseTime += responseTime;
maxResponseCulprit = assert.currentTest + '/' + name; if (responseTime > maxResponseTime) {
maxResponseTime = responseTime;
maxResponseCulprit = assert.currentTest + '/' + name;
}
averageOver++;
} }
averageOver++;
var checkDone = helpers._check(options, name, audit.check); var checkDone = helpers._check(options, name, audit.check);
return checkDone.then(function() { return checkDone.then(function() {
var execDone = helpers._exec(options, name, audit.exec); var execDone = helpers._exec(options, name, audit.exec);
return execDone.then(function(output) { return execDone.then(function(data) {
return helpers._post(name, audit.post, output).then(function() { return helpers._post(name, audit.post, data).then(function() {
if (assert.testLogging) { if (assert.testLogging) {
log('- END \'' + name + '\' in ' + assert.currentTest); log('- END \'' + name + '\' in ' + assert.currentTest);
} }
@ -963,9 +967,8 @@ helpers.audit = function(options, audits) {
}); });
}); });
}); });
}).then(null, function(ex) { }).then(function() {
console.error(ex.stack); return options.display.inputter.setInput('');
throw(ex);
}); });
}; };

Просмотреть файл

@ -4,7 +4,7 @@
this.EXPORTED_SYMBOLS = [ ]; this.EXPORTED_SYMBOLS = [ ];
Components.utils.import("resource:///modules/devtools/gcli.jsm"); Components.utils.import("resource://gre/modules/devtools/gcli.jsm");
/** /**
* 'scratchpad' command * 'scratchpad' command

Просмотреть файл

@ -21,7 +21,6 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource:///modules/PropertyPanel.jsm");
Cu.import("resource:///modules/source-editor.jsm"); Cu.import("resource:///modules/source-editor.jsm");
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm"); Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
Cu.import("resource:///modules/devtools/scratchpad-manager.jsm"); Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
@ -79,7 +78,7 @@ var Scratchpad = {
.replace(/^\/\*/, "") .replace(/^\/\*/, "")
.replace(/\*\/$/, ""); .replace(/\*\/$/, "");
aLine.split(",").forEach(function (pair) { aLine.split(",").forEach(pair => {
let [key, val] = pair.split(":"); let [key, val] = pair.split(":");
if (key && val) { if (key && val) {
@ -621,18 +620,18 @@ var Scratchpad = {
let encoder = new TextEncoder(); let encoder = new TextEncoder();
let buffer = encoder.encode(this.getText()); let buffer = encoder.encode(this.getText());
let promise = OS.File.writeAtomic(aFile.path, buffer,{tmpPath: aFile.path + ".tmp"}); let promise = OS.File.writeAtomic(aFile.path, buffer,{tmpPath: aFile.path + ".tmp"});
promise.then(function success(value) { promise.then(value => {
if (aCallback) { if (aCallback) {
aCallback.call(this, Components.results.NS_OK); aCallback.call(this, Components.results.NS_OK);
} }
}.bind(this), function failure(reason) { }, reason => {
if (!aSilentError) { if (!aSilentError) {
window.alert(this.strings.GetStringFromName("saveFile.failed")); window.alert(this.strings.GetStringFromName("saveFile.failed"));
} }
if (aCallback) { if (aCallback) {
aCallback.call(this, Components.results.NS_ERROR_UNEXPECTED); aCallback.call(this, Components.results.NS_ERROR_UNEXPECTED);
} }
}.bind(this)); });
}, },
@ -656,8 +655,7 @@ var Scratchpad = {
let channel = NetUtil.newChannel(aFile); let channel = NetUtil.newChannel(aFile);
channel.contentType = "application/javascript"; channel.contentType = "application/javascript";
let self = this; NetUtil.asyncFetch(channel, (aInputStream, aStatus) => {
NetUtil.asyncFetch(channel, function(aInputStream, aStatus) {
let content = null; let content = null;
if (Components.isSuccessCode(aStatus)) { if (Components.isSuccessCode(aStatus)) {
@ -670,22 +668,22 @@ var Scratchpad = {
// Check to see if the first line is a mode-line comment. // Check to see if the first line is a mode-line comment.
let line = content.split("\n")[0]; let line = content.split("\n")[0];
let modeline = self._scanModeLine(line); let modeline = this._scanModeLine(line);
let chrome = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED); let chrome = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED);
if (chrome && modeline["-sp-context"] === "browser") { if (chrome && modeline["-sp-context"] === "browser") {
self.setBrowserContext(); this.setBrowserContext();
} }
self.setText(content); this.setText(content);
self.editor.resetUndo(); this.editor.resetUndo();
} }
else if (!aSilentError) { else if (!aSilentError) {
window.alert(self.strings.GetStringFromName("openFile.failed")); window.alert(this.strings.GetStringFromName("openFile.failed"));
} }
if (aCallback) { if (aCallback) {
aCallback.call(self, aStatus, content); aCallback.call(this, aStatus, content);
} }
}); });
}, },
@ -698,8 +696,8 @@ var Scratchpad = {
*/ */
openFile: function SP_openFile(aIndex) openFile: function SP_openFile(aIndex)
{ {
let promptCallback = function(aFile) { let promptCallback = aFile => {
this.promptSave(function(aCloseFile, aSaved, aStatus) { this.promptSave((aCloseFile, aSaved, aStatus) => {
let shouldOpen = aCloseFile; let shouldOpen = aCloseFile;
if (aSaved && !Components.isSuccessCode(aStatus)) { if (aSaved && !Components.isSuccessCode(aStatus)) {
shouldOpen = false; shouldOpen = false;
@ -732,23 +730,21 @@ var Scratchpad = {
this.importFromFile(file, false); this.importFromFile(file, false);
this.setRecentFile(file); this.setRecentFile(file);
} }
}.bind(this)); });
}.bind(this); };
if (aIndex > -1) { if (aIndex > -1) {
promptCallback(); promptCallback();
} else { } else {
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
let fpCallback = function fpCallback_done(aResult) {
if (aResult != Ci.nsIFilePicker.returnCancel) {
promptCallback(fp.file);
}
};
fp.init(window, this.strings.GetStringFromName("openFile.title"), fp.init(window, this.strings.GetStringFromName("openFile.title"),
Ci.nsIFilePicker.modeOpen); Ci.nsIFilePicker.modeOpen);
fp.defaultString = ""; fp.defaultString = "";
fp.open(fpCallback); fp.open(aResult => {
if (aResult != Ci.nsIFilePicker.returnCancel) {
promptCallback(fp.file);
}
});
} }
}, },
@ -953,7 +949,7 @@ var Scratchpad = {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(this.filename); file.initWithPath(this.filename);
this.exportToFile(file, true, false, function(aStatus) { this.exportToFile(file, true, false, aStatus => {
if (Components.isSuccessCode(aStatus)) { if (Components.isSuccessCode(aStatus)) {
this.editor.dirty = false; this.editor.dirty = false;
this.setRecentFile(file); this.setRecentFile(file);
@ -973,10 +969,10 @@ var Scratchpad = {
saveFileAs: function SP_saveFileAs(aCallback) saveFileAs: function SP_saveFileAs(aCallback)
{ {
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
let fpCallback = function fpCallback_done(aResult) { let fpCallback = aResult => {
if (aResult != Ci.nsIFilePicker.returnCancel) { if (aResult != Ci.nsIFilePicker.returnCancel) {
this.setFilename(fp.file.path); this.setFilename(fp.file.path);
this.exportToFile(fp.file, true, false, function(aStatus) { this.exportToFile(fp.file, true, false, aStatus => {
if (Components.isSuccessCode(aStatus)) { if (Components.isSuccessCode(aStatus)) {
this.editor.dirty = false; this.editor.dirty = false;
this.setRecentFile(fp.file); this.setRecentFile(fp.file);
@ -986,7 +982,7 @@ var Scratchpad = {
} }
}); });
} }
}.bind(this); };
fp.init(window, this.strings.GetStringFromName("saveFileAs"), fp.init(window, this.strings.GetStringFromName("saveFileAs"),
Ci.nsIFilePicker.modeSave); Ci.nsIFilePicker.modeSave);
@ -1009,7 +1005,7 @@ var Scratchpad = {
return; return;
} }
this.importFromFile(file, false, function(aStatus, aContent) { this.importFromFile(file, false, (aStatus, aContent) => {
if (aCallback) { if (aCallback) {
aCallback(aStatus); aCallback(aStatus);
} }
@ -1045,8 +1041,8 @@ var Scratchpad = {
return; return;
} }
if (button == BUTTON_POSITION_REVERT) { if (button == BUTTON_POSITION_REVERT) {
this.revertFile(function(aStatus) { this.revertFile(aStatus => {
if(aCallback){ if (aCallback) {
aCallback(true, aStatus); aCallback(true, aStatus);
} }
}); });
@ -1348,7 +1344,7 @@ var Scratchpad = {
} }
if (button == BUTTON_POSITION_SAVE) { if (button == BUTTON_POSITION_SAVE) {
this.saveFile(function(aStatus) { this.saveFile(aStatus => {
if (aCallback) { if (aCallback) {
aCallback(true, true, aStatus); aCallback(true, true, aStatus);
} }
@ -1387,7 +1383,7 @@ var Scratchpad = {
*/ */
close: function SP_close(aCallback) close: function SP_close(aCallback)
{ {
this.promptSave(function(aShouldClose, aSaved, aStatus) { this.promptSave((aShouldClose, aSaved, aStatus) => {
let shouldClose = aShouldClose; let shouldClose = aShouldClose;
if (aSaved && !Components.isSuccessCode(aStatus)) { if (aSaved && !Components.isSuccessCode(aStatus)) {
shouldClose = false; shouldClose = false;
@ -1399,7 +1395,7 @@ var Scratchpad = {
if (aCallback) { if (aCallback) {
aCallback(); aCallback();
} }
}.bind(this)); });
}, },
_observers: [], _observers: [],

Просмотреть файл

@ -53,6 +53,7 @@
<command id="sp-cmd-errorConsole" oncommand="Scratchpad.openErrorConsole();" disabled="true"/> <command id="sp-cmd-errorConsole" oncommand="Scratchpad.openErrorConsole();" disabled="true"/>
<command id="sp-cmd-webConsole" oncommand="Scratchpad.openWebConsole();"/> <command id="sp-cmd-webConsole" oncommand="Scratchpad.openWebConsole();"/>
<command id="sp-cmd-documentationLink" oncommand="Scratchpad.openDocumentationPage();"/> <command id="sp-cmd-documentationLink" oncommand="Scratchpad.openDocumentationPage();"/>
<command id="sp-cmd-hideSidebar" oncommand="Scratchpad.sidebar.hide();"/>
</commandset> </commandset>
<keyset id="sourceEditorKeys"/> <keyset id="sourceEditorKeys"/>
@ -102,6 +103,9 @@
key="&errorConsoleCmd.commandkey;" key="&errorConsoleCmd.commandkey;"
command="sp-cmd-errorConsole" command="sp-cmd-errorConsole"
modifiers="accel,shift"/> modifiers="accel,shift"/>
<key id="sp-key-hideSidebar"
keycode="VK_ESCAPE"
command="sp-cmd-hideSidebar"/>
<key id="key_openHelp" <key id="key_openHelp"
keycode="VK_F1" keycode="VK_F1"
command="sp-cmd-documentationLink"/> command="sp-cmd-documentationLink"/>

Просмотреть файл

@ -241,6 +241,10 @@ AppCacheUtils.prototype = {
}, },
listEntries: function ACU_show(searchTerm) { listEntries: function ACU_show(searchTerm) {
if (!Services.prefs.getBoolPref("browser.cache.disk.enable")) {
throw new Error(l10n.GetStringFromName("cacheDisabled"));
}
let entries = []; let entries = [];
Services.cache.visitEntries({ Services.cache.visitEntries({
@ -275,6 +279,9 @@ AppCacheUtils.prototype = {
} }
}); });
if (entries.length == 0) {
throw new Error(l10n.GetStringFromName("noResults"));
}
return entries; return entries;
}, },
@ -336,7 +343,7 @@ AppCacheUtils.prototype = {
} else { } else {
this.errors.push({ this.errors.push({
line: 0, line: 0,
msg: "The URI passed to AppCacheUtils is invalid." msg: l10n.GetStringFromName("invalidURI")
}); });
} }
}); });

Просмотреть файл

@ -1,28 +0,0 @@
/* 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/. */
"use strict";
/**
* Define various constants to match the globals provided by the browser.
* This module helps cases where code is shared between the web and Firefox.
* See also Console.jsm for an implementation of the Firefox console that
* forwards to dump();
*/
this.EXPORTED_SYMBOLS = [ "Node", "HTMLElement", "setTimeout", "clearTimeout" ];
/**
* Expose Node/HTMLElement objects. This allows us to use the Node constants
* without resorting to hardcoded numbers
*/
this.Node = Components.interfaces.nsIDOMNode;
this.HTMLElement = Components.interfaces.nsIDOMHTMLElement;
/*
* Import and re-export the timeout functions from Timer.jsm.
*/
let Timer = Components.utils.import("resource://gre/modules/Timer.jsm", {});
this.setTimeout = Timer.setTimeout;
this.clearTimeout = Timer.clearTimeout;

Просмотреть файл

@ -19,7 +19,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm"); "resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "gcli", XPCOMUtils.defineLazyModuleGetter(this, "gcli",
"resource:///modules/devtools/gcli.jsm"); "resource://gre/modules/devtools/gcli.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CmdCommands", XPCOMUtils.defineLazyModuleGetter(this, "CmdCommands",
"resource:///modules/devtools/BuiltinCommands.jsm"); "resource:///modules/devtools/BuiltinCommands.jsm");
@ -153,7 +153,7 @@ let CommandUtils = {
createEnvironment: function(chromeDocument, contentDocument) { createEnvironment: function(chromeDocument, contentDocument) {
let environment = { let environment = {
chromeDocument: chromeDocument, chromeDocument: chromeDocument,
contentDocument: contentDocument, // Use of contentDocument is deprecated chromeWindow: chromeDocument.defaultView,
document: contentDocument, document: contentDocument,
window: contentDocument.defaultView window: contentDocument.defaultView
@ -181,9 +181,13 @@ this.CommandUtils = CommandUtils;
* to using panels. * to using panels.
*/ */
XPCOMUtils.defineLazyGetter(this, "isLinux", function () { XPCOMUtils.defineLazyGetter(this, "isLinux", function () {
return OS == "Linux";
});
XPCOMUtils.defineLazyGetter(this, "OS", function () {
let os = Components.classes["@mozilla.org/xre/app-info;1"] let os = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULRuntime).OS; .getService(Components.interfaces.nsIXULRuntime).OS;
return os == "Linux"; return os;
}); });
/** /**
@ -761,6 +765,7 @@ function OutputPanel(aDevToolbar, aLoadCallback)
this.displayedOutput = undefined; this.displayedOutput = undefined;
this._onload = this._onload.bind(this); this._onload = this._onload.bind(this);
this._update = this._update.bind(this);
this._frame.addEventListener("load", this._onload, true); this._frame.addEventListener("load", this._onload, true);
this.loaded = false; this.loaded = false;
@ -791,33 +796,6 @@ OutputPanel.prototype._onload = function OP_onload()
} }
}; };
/**
* Determine the scrollbar width in the current document.
*
* @private
*/
Object.defineProperty(OutputPanel.prototype, 'scrollbarWidth', {
get: function() {
if (this.__scrollbarWidth) {
return this.__scrollbarWidth;
}
let hbox = this.document.createElementNS(XUL_NS, "hbox");
hbox.setAttribute("style", "height: 0%; overflow: hidden");
let scrollbar = this.document.createElementNS(XUL_NS, "scrollbar");
scrollbar.setAttribute("orient", "vertical");
hbox.appendChild(scrollbar);
this.document.documentElement.appendChild(hbox);
this.__scrollbarWidth = scrollbar.clientWidth;
this.document.documentElement.removeChild(hbox);
return this.__scrollbarWidth;
},
enumerable: true
});
/** /**
* Prevent the popup from hiding if it is not permitted via this.canHide. * Prevent the popup from hiding if it is not permitted via this.canHide.
*/ */
@ -863,13 +841,32 @@ OutputPanel.prototype._resize = function CLP_resize()
// Set max panel width to match any content with a max of the width of the // Set max panel width to match any content with a max of the width of the
// browser window. // browser window.
let maxWidth = this._panel.ownerDocument.documentElement.clientWidth; let maxWidth = this._panel.ownerDocument.documentElement.clientWidth;
let width = Math.min(maxWidth, this.document.documentElement.scrollWidth);
// Add scrollbar width to content size in case a scrollbar is needed. // Adjust max width according to OS.
width += this.scrollbarWidth; // We'd like to put this in CSS but we can't:
// body { width: calc(min(-5px, max-content)); }
// #_panel { max-width: -5px; }
switch(OS) {
case "Linux":
maxWidth -= 5;
break;
case "Darwin":
maxWidth -= 25;
break;
case "WINNT":
maxWidth -= 5;
break;
}
this.document.body.style.width = "-moz-max-content";
let style = this._frame.contentWindow.getComputedStyle(this.document.body);
let frameWidth = parseInt(style.width, 10);
let width = Math.min(maxWidth, frameWidth);
this.document.body.style.width = width + "px";
// Set the width of the iframe. // Set the width of the iframe.
this._frame.style.minWidth = width + "px"; this._frame.style.minWidth = width + "px";
this._panel.style.maxWidth = maxWidth + "px";
// browserAdjustment is used to correct the panel height according to the // browserAdjustment is used to correct the panel height according to the
// browsers borders etc. // browsers borders etc.
@ -906,18 +903,28 @@ OutputPanel.prototype._outputChanged = function OP_outputChanged(aEvent)
this.remove(); this.remove();
this.displayedOutput = aEvent.output; this.displayedOutput = aEvent.output;
this.update();
this.displayedOutput.onChange.add(this.update, this);
this.displayedOutput.onClose.add(this.remove, this); this.displayedOutput.onClose.add(this.remove, this);
if (this.displayedOutput.completed) {
this._update();
}
else {
this.displayedOutput.promise.then(this._update, this._update)
.then(null, console.error);
}
}; };
/** /**
* Called when displayed Output says it's changed or from outputChanged, which * Called when displayed Output says it's changed or from outputChanged, which
* happens when there is a new displayed Output. * happens when there is a new displayed Output.
*/ */
OutputPanel.prototype.update = function OP_update() OutputPanel.prototype._update = function OP_update()
{ {
// destroy has been called, bail out
if (this._div == null) {
return;
}
// Empty this._div // Empty this._div
while (this._div.hasChildNodes()) { while (this._div.hasChildNodes()) {
this._div.removeChild(this._div.firstChild); this._div.removeChild(this._div.firstChild);
@ -927,7 +934,7 @@ OutputPanel.prototype.update = function OP_update()
let requisition = this._devtoolbar.display.requisition; let requisition = this._devtoolbar.display.requisition;
let nodePromise = converters.convert(this.displayedOutput.data, let nodePromise = converters.convert(this.displayedOutput.data,
this.displayedOutput.type, 'dom', this.displayedOutput.type, 'dom',
requisition.context); requisition.conversionContext);
nodePromise.then(function(node) { nodePromise.then(function(node) {
while (this._div.hasChildNodes()) { while (this._div.hasChildNodes()) {
this._div.removeChild(this._div.firstChild); this._div.removeChild(this._div.firstChild);
@ -958,7 +965,6 @@ OutputPanel.prototype.remove = function OP_remove()
} }
if (this.displayedOutput) { if (this.displayedOutput) {
this.displayedOutput.onChange.remove(this.update, this);
this.displayedOutput.onClose.remove(this.remove, this); this.displayedOutput.onClose.remove(this.remove, this);
delete this.displayedOutput; delete this.displayedOutput;
} }

Просмотреть файл

@ -14,7 +14,6 @@ include $(DEPTH)/config/autoconf.mk
DISABLED_XPCSHELL_TESTS = unit DISABLED_XPCSHELL_TESTS = unit
MOCHITEST_BROWSER_FILES = \ MOCHITEST_BROWSER_FILES = \
browser_browser_basic.js \
browser_require_basic.js \ browser_require_basic.js \
browser_templater_basic.js \ browser_templater_basic.js \
browser_toolbar_basic.js \ browser_toolbar_basic.js \

Просмотреть файл

@ -1,28 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests exports from Browser.jsm
const TEST_URI = "data:text/html;charset=utf-8,<p id=id>Text</p>";
let imported = {};
Components.utils.import("resource:///modules/devtools/Browser.jsm", imported);
registerCleanupFunction(function tearDown() {
imported = undefined;
});
function test() {
addTab(TEST_URI, function(browser, tab, document) {
runTest(browser, tab, document);
});
}
function runTest(browser, tab, document) {
var p = document.getElementById("id");
ok(p instanceof imported.Node, "Node correctly defined");
ok(p instanceof imported.HTMLElement, "HTMLElement correctly defined");
finish();
}

Просмотреть файл

@ -10,7 +10,7 @@
*/ */
var Promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise; var Promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
var template = Cu.import("resource:///modules/devtools/Templater.jsm", {}).template; var template = Cu.import("resource://gre/modules/devtools/Templater.jsm", {}).template;
const TEST_URI = "http://example.com/browser/browser/devtools/shared/test/browser_templater_basic.html"; const TEST_URI = "http://example.com/browser/browser/devtools/shared/test/browser_templater_basic.html";

Просмотреть файл

@ -35,6 +35,8 @@ this.EXPORTED_SYMBOLS = ["BreadcrumbsWidget"];
* The element associated with the widget. * The element associated with the widget.
*/ */
this.BreadcrumbsWidget = function BreadcrumbsWidget(aNode) { this.BreadcrumbsWidget = function BreadcrumbsWidget(aNode) {
this.document = aNode.ownerDocument;
this.window = this.document.defaultView;
this._parent = aNode; this._parent = aNode;
// Create an internal arrowscrollbox container. // Create an internal arrowscrollbox container.
@ -59,9 +61,6 @@ this.BreadcrumbsWidget = function BreadcrumbsWidget(aNode) {
}; };
BreadcrumbsWidget.prototype = { BreadcrumbsWidget.prototype = {
get document() this._parent.ownerDocument,
get window() this.document.defaultView,
/** /**
* Inserts an item in this container at the specified index. * Inserts an item in this container at the specified index.
* *
@ -108,13 +107,12 @@ BreadcrumbsWidget.prototype = {
* Removes all of the child nodes from this container. * Removes all of the child nodes from this container.
*/ */
removeAllItems: function BCW_removeAllItems() { removeAllItems: function BCW_removeAllItems() {
let parent = this._parent;
let list = this._list; let list = this._list;
let firstChild;
while (firstChild = list.firstChild) { while (list.hasChildNodes()) {
list.removeChild(firstChild); list.firstChild.remove();
} }
this._selectedItem = null; this._selectedItem = null;
}, },
@ -146,11 +144,11 @@ BreadcrumbsWidget.prototype = {
// Repeated calls to ensureElementIsVisible would interfere with each other // Repeated calls to ensureElementIsVisible would interfere with each other
// and may sometimes result in incorrect scroll positions. // and may sometimes result in incorrect scroll positions.
this.window.clearTimeout(this._ensureVisibleTimeout); this.window.clearTimeout(this._ensureVisibleTimeout);
this._ensureVisibleTimeout = this.window.setTimeout(function() { this._ensureVisibleTimeout = this.window.setTimeout(() => {
if (this._selectedItem) { if (this._selectedItem) {
this._list.ensureElementIsVisible(this._selectedItem); this._list.ensureElementIsVisible(this._selectedItem);
} }
}.bind(this), ENSURE_SELECTION_VISIBLE_DELAY); }, ENSURE_SELECTION_VISIBLE_DELAY);
}, },
/** /**
@ -177,6 +175,8 @@ BreadcrumbsWidget.prototype = {
target.setAttribute("overflows", ""); target.setAttribute("overflows", "");
}, },
window: null,
document: null,
_parent: null, _parent: null,
_list: null, _list: null,
_selectedItem: null, _selectedItem: null,
@ -192,6 +192,8 @@ BreadcrumbsWidget.prototype = {
* The string or node displayed in the container. * The string or node displayed in the container.
*/ */
function Breadcrumb(aWidget, aContents) { function Breadcrumb(aWidget, aContents) {
this.document = aWidget.document;
this.window = aWidget.window;
this.ownerView = aWidget; this.ownerView = aWidget;
this._target = this.document.createElement("hbox"); this._target = this.document.createElement("hbox");
@ -201,9 +203,6 @@ function Breadcrumb(aWidget, aContents) {
} }
Breadcrumb.prototype = { Breadcrumb.prototype = {
get document() this.ownerView.document,
get window() this.document.defaultView,
/** /**
* Sets the contents displayed in this item's view. * Sets the contents displayed in this item's view.
* *
@ -228,6 +227,8 @@ Breadcrumb.prototype = {
this._target.appendChild(aContents); this._target.appendChild(aContents);
}, },
window: null,
document: null,
ownerView: null, ownerView: null,
_target: null _target: null
}; };

Просмотреть файл

@ -37,6 +37,8 @@ this.EXPORTED_SYMBOLS = ["SideMenuWidget"];
* Specifies if items in this container should display horizontal arrows. * Specifies if items in this container should display horizontal arrows.
*/ */
this.SideMenuWidget = function SideMenuWidget(aNode, aShowArrows = true) { this.SideMenuWidget = function SideMenuWidget(aNode, aShowArrows = true) {
this.document = aNode.ownerDocument;
this.window = this.document.defaultView;
this._parent = aNode; this._parent = aNode;
this._showArrows = aShowArrows; this._showArrows = aShowArrows;
@ -60,8 +62,10 @@ this.SideMenuWidget = function SideMenuWidget(aNode, aShowArrows = true) {
}; };
SideMenuWidget.prototype = { SideMenuWidget.prototype = {
get document() this._parent.ownerDocument, /**
get window() this.document.defaultView, * Specifies if groups in this container should be sorted alphabetically.
*/
sortedGroups: true,
/** /**
* Specifies if this container should try to keep the selected item visible. * Specifies if this container should try to keep the selected item visible.
@ -70,9 +74,12 @@ SideMenuWidget.prototype = {
maintainSelectionVisible: true, maintainSelectionVisible: true,
/** /**
* Specifies if groups in this container should be sorted alphabetically. * Specifies that the container viewport should be "stuck" to the
* bottom. That is, the container is automatically scrolled down to
* keep appended items visible, but only when the scroll position is
* already at the bottom.
*/ */
sortedGroups: true, autoscrollWithAppendedItems: false,
/** /**
* Inserts an item in this container at the specified index, optionally * Inserts an item in this container at the specified index, optionally
@ -90,12 +97,26 @@ SideMenuWidget.prototype = {
* The element associated with the displayed item. * The element associated with the displayed item.
*/ */
insertItemAt: function SMW_insertItemAt(aIndex, aContents, aTooltip = "", aGroup = "") { insertItemAt: function SMW_insertItemAt(aIndex, aContents, aTooltip = "", aGroup = "") {
if (this.maintainSelectionVisible) { // Invalidate any notices set on this widget.
this.ensureSelectionIsVisible(true, true); // Don't worry, it's delayed. this.removeAttribute("notice");
}
let maintainScrollAtBottom =
this.autoscrollWithAppendedItems &&
(aIndex < 0 || aIndex >= this._orderedMenuElementsArray.length) &&
(this._list.scrollTop + this._list.clientHeight >= this._list.scrollHeight);
let group = this._getGroupForName(aGroup); let group = this._getGroupForName(aGroup);
return group.insertItemAt(aIndex, aContents, aTooltip, this._showArrows); let item = this._getItemForGroup(group, aContents, aTooltip);
let element = item.insertSelfAt(aIndex);
if (this.maintainSelectionVisible) {
this.ensureSelectionIsVisible({ withGroup: true, delayed: true });
}
if (maintainScrollAtBottom) {
this._list.scrollTop = this._list.scrollHeight;
}
return element;
}, },
/** /**
@ -117,9 +138,14 @@ SideMenuWidget.prototype = {
* The element associated with the displayed item. * The element associated with the displayed item.
*/ */
removeChild: function SMW_removeChild(aChild) { removeChild: function SMW_removeChild(aChild) {
// Remove the item itself, not the contents. if (aChild.className == "side-menu-widget-item-contents") {
let item = aChild.parentNode; // Remove the item itself, not the contents.
item.parentNode.removeChild(item); aChild.parentNode.remove();
} else {
// Groups with no title don't have any special internal structure.
aChild.remove();
}
this._orderedMenuElementsArray.splice( this._orderedMenuElementsArray.splice(
this._orderedMenuElementsArray.indexOf(aChild), 1); this._orderedMenuElementsArray.indexOf(aChild), 1);
@ -134,11 +160,11 @@ SideMenuWidget.prototype = {
removeAllItems: function SMW_removeAllItems() { removeAllItems: function SMW_removeAllItems() {
let parent = this._parent; let parent = this._parent;
let list = this._list; let list = this._list;
let firstChild;
while (firstChild = list.firstChild) { while (list.hasChildNodes()) {
list.removeChild(firstChild); list.firstChild.remove();
} }
this._selectedItem = null; this._selectedItem = null;
this._groupsByName.clear(); this._groupsByName.clear();
@ -157,12 +183,12 @@ SideMenuWidget.prototype = {
* @param nsIDOMNode aChild * @param nsIDOMNode aChild
*/ */
set selectedItem(aChild) { set selectedItem(aChild) {
let menuElementsArray = this._orderedMenuElementsArray; let menuArray = this._orderedMenuElementsArray;
if (!aChild) { if (!aChild) {
this._selectedItem = null; this._selectedItem = null;
} }
for (let node of menuElementsArray) { for (let node of menuArray) {
if (node == aChild) { if (node == aChild) {
node.classList.add("selected"); node.classList.add("selected");
node.parentNode.classList.add("selected"); node.parentNode.classList.add("selected");
@ -172,18 +198,18 @@ SideMenuWidget.prototype = {
node.parentNode.classList.remove("selected"); node.parentNode.classList.remove("selected");
} }
} }
// Repeated calls to ensureElementIsVisible would interfere with each other // Repeated calls to ensureElementIsVisible would interfere with each other
// and may sometimes result in incorrect scroll positions. // and may sometimes result in incorrect scroll positions.
this.ensureSelectionIsVisible(false, true); this.ensureSelectionIsVisible({ delayed: true });
}, },
/** /**
* Ensures the selected element is visible. * Ensures the selected element is visible.
* @see SideMenuWidget.prototype.ensureElementIsVisible. * @see SideMenuWidget.prototype.ensureElementIsVisible.
*/ */
ensureSelectionIsVisible: ensureSelectionIsVisible: function SMW_ensureSelectionIsVisible(aFlags) {
function SMW_ensureSelectionIsVisible(aGroupFlag, aDelayedFlag) { this.ensureElementIsVisible(this.selectedItem, aFlags);
this.ensureElementIsVisible(this.selectedItem, aGroupFlag, aDelayedFlag);
}, },
/** /**
@ -191,28 +217,31 @@ SideMenuWidget.prototype = {
* *
* @param nsIDOMNode aElement * @param nsIDOMNode aElement
* The element to make visible. * The element to make visible.
* @param boolean aGroupFlag * @param object aFlags [optional]
* True if the group header should also be made visible, if possible. * An object containing some of the following flags:
* @param boolean aDelayedFlag * - withGroup: true if the group header should also be made visible, if possible
* True to wait a few cycles before ensuring the selection is visible. * - delayed: wait a few cycles before ensuring the selection is visible
*/ */
ensureElementIsVisible: ensureElementIsVisible: function SMW_ensureElementIsVisible(aElement, aFlags = {}) {
function SMW_ensureElementIsVisible(aElement, aGroupFlag, aDelayedFlag) {
if (!aElement) { if (!aElement) {
return; return;
} }
if (aDelayedFlag) {
if (aFlags.delayed) {
delete aFlags.delayed;
this.window.clearTimeout(this._ensureVisibleTimeout); this.window.clearTimeout(this._ensureVisibleTimeout);
this._ensureVisibleTimeout = this.window.setTimeout(function() { this._ensureVisibleTimeout = this.window.setTimeout(() => {
this.ensureElementIsVisible(aElement, aGroupFlag, false); this.ensureElementIsVisible(aElement, aFlags);
}.bind(this), ENSURE_SELECTION_VISIBLE_DELAY); }, ENSURE_SELECTION_VISIBLE_DELAY);
return; return;
} }
if (aGroupFlag) {
if (aFlags.withGroup) {
let groupList = aElement.parentNode; let groupList = aElement.parentNode;
let groupContainer = groupList.parentNode; let groupContainer = groupList.parentNode;
groupContainer.scrollIntoView(true); // Align with the top. groupContainer.scrollIntoView(true); // Align with the top.
} }
this._boxObject.ensureElementIsVisible(aElement); this._boxObject.ensureElementIsVisible(aElement);
}, },
@ -349,6 +378,24 @@ SideMenuWidget.prototype = {
return group; return group;
}, },
/**
* Gets a menu item to be displayed inside a group.
* @see SideMenuWidget.prototype._getGroupForName
*
* @param SideMenuGroup aGroup
* The group to contain the menu item.
* @param string | nsIDOMNode aContents
* The string or node displayed in the container.
* @param string aTooltip [optional]
* A tooltip attribute for the displayed item.
*/
_getItemForGroup: function SMW__getItemForGroup(aGroup, aContents, aTooltip) {
return new SideMenuItem(aGroup, aContents, aTooltip, this._showArrows);
},
window: null,
document: null,
_showArrows: false,
_parent: null, _parent: null,
_list: null, _list: null,
_boxObject: null, _boxObject: null,
@ -372,70 +419,44 @@ SideMenuWidget.prototype = {
* The string displayed in the container. * The string displayed in the container.
*/ */
function SideMenuGroup(aWidget, aName) { function SideMenuGroup(aWidget, aName) {
this.document = aWidget.document;
this.window = aWidget.window;
this.ownerView = aWidget; this.ownerView = aWidget;
this.identifier = aName; this.identifier = aName;
let document = this.document; // Create an internal title and list container.
let title = this._title = document.createElement("hbox"); if (aName) {
title.className = "side-menu-widget-group-title"; let target = this._target = this.document.createElement("vbox");
target.className = "side-menu-widget-group";
target.setAttribute("name", aName);
target.setAttribute("tooltiptext", aName);
let name = this._name = document.createElement("label"); let list = this._list = this.document.createElement("vbox");
name.className = "plain name"; list.className = "side-menu-widget-group-list";
name.setAttribute("value", aName);
name.setAttribute("crop", "end");
name.setAttribute("flex", "1");
let list = this._list = document.createElement("vbox"); let title = this._title = this.document.createElement("hbox");
list.className = "side-menu-widget-group-list"; title.className = "side-menu-widget-group-title";
let target = this._target = document.createElement("vbox"); let name = this._name = this.document.createElement("label");
target.className = "side-menu-widget-group side-menu-widget-item-or-group"; name.className = "plain name";
target.setAttribute("name", aName); name.setAttribute("value", aName);
target.setAttribute("tooltiptext", aName); name.setAttribute("crop", "end");
name.setAttribute("flex", "1");
title.appendChild(name); title.appendChild(name);
target.appendChild(title); target.appendChild(title);
target.appendChild(list); target.appendChild(list);
}
// Skip a few redundant nodes when no title is shown.
else {
let target = this._target = this._list = this.document.createElement("vbox");
target.className = "side-menu-widget-group side-menu-widget-group-list";
}
} }
SideMenuGroup.prototype = { SideMenuGroup.prototype = {
get document() this.ownerView.document, get _orderedGroupElementsArray() this.ownerView._orderedGroupElementsArray,
get window() this.document.defaultView, get _orderedMenuElementsArray() this.ownerView._orderedMenuElementsArray,
get _groupElementsArray() this.ownerView._orderedGroupElementsArray,
get _menuElementsArray() this.ownerView._orderedMenuElementsArray,
/**
* Inserts an item in this group at the specified index.
*
* @param number aIndex
* The position in the container intended for this item.
* @param string | nsIDOMNode aContents
* The string or node displayed in the container.
* @param string aTooltip [optional]
* A tooltip attribute for the displayed item.
* @param boolean aArrowFlag
* True if a horizontal arrow should be shown.
* @return nsIDOMNode
* The element associated with the displayed item.
*/
insertItemAt: function SMG_insertItemAt(aIndex, aContents, aTooltip, aArrowFlag) {
let list = this._list;
let menuArray = this._menuElementsArray;
let item = new SideMenuItem(this, aContents, aTooltip, aArrowFlag);
// Invalidate any notices set on the owner widget.
this.ownerView.removeAttribute("notice");
if (aIndex >= 0) {
list.insertBefore(item._container, list.childNodes[aIndex]);
menuArray.splice(aIndex, 0, item._target);
} else {
list.appendChild(item._container);
menuArray.push(item._target);
}
return item._target;
},
/** /**
* Inserts this group in the parent container at the specified index. * Inserts this group in the parent container at the specified index.
@ -445,7 +466,7 @@ SideMenuGroup.prototype = {
*/ */
insertSelfAt: function SMG_insertSelfAt(aIndex) { insertSelfAt: function SMG_insertSelfAt(aIndex) {
let ownerList = this.ownerView._list; let ownerList = this.ownerView._list;
let groupsArray = this._groupElementsArray; let groupsArray = this._orderedGroupElementsArray;
if (aIndex >= 0) { if (aIndex >= 0) {
ownerList.insertBefore(this._target, groupsArray[aIndex]); ownerList.insertBefore(this._target, groupsArray[aIndex]);
@ -464,7 +485,7 @@ SideMenuGroup.prototype = {
*/ */
findExpectedIndexForSelf: function SMG_findExpectedIndexForSelf() { findExpectedIndexForSelf: function SMG_findExpectedIndexForSelf() {
let identifier = this.identifier; let identifier = this.identifier;
let groupsArray = this._groupElementsArray; let groupsArray = this._orderedGroupElementsArray;
for (let group of groupsArray) { for (let group of groupsArray) {
let name = group.getAttribute("name"); let name = group.getAttribute("name");
@ -476,6 +497,8 @@ SideMenuGroup.prototype = {
return -1; return -1;
}, },
window: null,
document: null,
ownerView: null, ownerView: null,
identifier: "", identifier: "",
_target: null, _target: null,
@ -497,31 +520,29 @@ SideMenuGroup.prototype = {
* True if a horizontal arrow should be shown. * True if a horizontal arrow should be shown.
*/ */
function SideMenuItem(aGroup, aContents, aTooltip, aArrowFlag) { function SideMenuItem(aGroup, aContents, aTooltip, aArrowFlag) {
this.document = aGroup.document;
this.window = aGroup.window;
this.ownerView = aGroup; this.ownerView = aGroup;
let document = this.document;
// Show a horizontal arrow towards the content. // Show a horizontal arrow towards the content.
if (aArrowFlag) { if (aArrowFlag) {
let target = this._target = document.createElement("vbox"); let container = this._container = this.document.createElement("hbox");
container.className = "side-menu-widget-item";
container.setAttribute("tooltiptext", aTooltip);
let target = this._target = this.document.createElement("vbox");
target.className = "side-menu-widget-item-contents"; target.className = "side-menu-widget-item-contents";
let arrow = this._arrow = document.createElement("hbox"); let arrow = this._arrow = this.document.createElement("hbox");
arrow.className = "side-menu-widget-item-arrow"; arrow.className = "side-menu-widget-item-arrow";
let container = this._container = document.createElement("hbox");
container.className = "side-menu-widget-item side-menu-widget-item-or-group";
container.setAttribute("tooltiptext", aTooltip);
container.appendChild(target); container.appendChild(target);
container.appendChild(arrow); container.appendChild(arrow);
} }
// Skip a few redundant nodes when no horizontal arrow is shown. // Skip a few redundant nodes when no horizontal arrow is shown.
else { else {
let target = this._target = this._container = document.createElement("hbox"); let target = this._target = this._container = this.document.createElement("hbox");
target.className = target.className = "side-menu-widget-item side-menu-widget-item-contents";
"side-menu-widget-item " +
"side-menu-widget-item-or-group " +
"side-menu-widget-item-contents";
} }
this._target.setAttribute("flex", "1"); this._target.setAttribute("flex", "1");
@ -529,8 +550,31 @@ function SideMenuItem(aGroup, aContents, aTooltip, aArrowFlag) {
} }
SideMenuItem.prototype = { SideMenuItem.prototype = {
get document() this.ownerView.document, get _orderedGroupElementsArray() this.ownerView._orderedGroupElementsArray,
get window() this.document.defaultView, get _orderedMenuElementsArray() this.ownerView._orderedMenuElementsArray,
/**
* Inserts this item in the parent group at the specified index.
*
* @param number aIndex
* The position in the container intended for this item.
* @return nsIDOMNode
* The element associated with the displayed item.
*/
insertSelfAt: function SMI_insertSelfAt(aIndex) {
let ownerList = this.ownerView._list;
let menuArray = this._orderedMenuElementsArray;
if (aIndex >= 0) {
ownerList.insertBefore(this._container, ownerList.childNodes[aIndex]);
menuArray.splice(aIndex, 0, this._target);
} else {
ownerList.appendChild(this._container);
menuArray.push(this._target);
}
return this._target;
},
/** /**
* Sets the contents displayed in this item's view. * Sets the contents displayed in this item's view.
@ -559,6 +603,8 @@ SideMenuItem.prototype = {
this._target.appendChild(aContents); this._target.appendChild(aContents);
}, },
window: null,
document: null,
ownerView: null, ownerView: null,
_target: null, _target: null,
_container: null, _container: null,

Просмотреть файл

@ -128,10 +128,9 @@ VariablesView.prototype = {
} }
let list = this._list; let list = this._list;
let firstChild;
while (firstChild = list.firstChild) { while (list.hasChildNodes()) {
list.removeChild(firstChild); list.firstChild.remove();
} }
this._store.length = 0; this._store.length = 0;
@ -163,7 +162,7 @@ VariablesView.prototype = {
this._store.length = 0; this._store.length = 0;
this._itemsByElement.clear(); this._itemsByElement.clear();
this._emptyTimeout = this.window.setTimeout(function() { this._emptyTimeout = this.window.setTimeout(() => {
this._emptyTimeout = null; this._emptyTimeout = null;
prevList.removeEventListener("keypress", this._onViewKeyPress, false); prevList.removeEventListener("keypress", this._onViewKeyPress, false);
@ -178,7 +177,7 @@ VariablesView.prototype = {
this._appendEmptyNotice(); this._appendEmptyNotice();
this._toggleSearchVisibility(false); this._toggleSearchVisibility(false);
} }
}.bind(this), aTimeout); }, aTimeout);
}, },
/** /**
@ -198,6 +197,12 @@ VariablesView.prototype = {
*/ */
lazyAppend: true, lazyAppend: true,
/**
* Specifies if nodes in this view may be expanded lazily.
* @see Scope.prototype.expand
*/
lazyExpand: true,
/** /**
* Function called each time a variable or property's value is changed via * Function called each time a variable or property's value is changed via
* user interaction. If null, then value changes are disabled. * user interaction. If null, then value changes are disabled.
@ -403,7 +408,7 @@ VariablesView.prototype = {
if (!this._searchboxContainer) { if (!this._searchboxContainer) {
return; return;
} }
this._searchboxContainer.parentNode.removeChild(this._searchboxContainer); this._searchboxContainer.remove();
this._searchboxNode.removeEventListener("input", this._onSearchboxInput, false); this._searchboxNode.removeEventListener("input", this._onSearchboxInput, false);
this._searchboxNode.removeEventListener("keypress", this._onSearchboxKeyPress, false); this._searchboxNode.removeEventListener("keypress", this._onSearchboxKeyPress, false);
@ -642,15 +647,17 @@ VariablesView.prototype = {
* Focuses the next scope, variable or property in this view. * Focuses the next scope, variable or property in this view.
* @see VariablesView.prototype._focusChange * @see VariablesView.prototype._focusChange
*/ */
focusNextItem: function VV_focusNextItem(aMaintainViewFocusedFlag) focusNextItem: function VV_focusNextItem(aMaintainViewFocusedFlag) {
this._focusChange("advanceFocus", aMaintainViewFocusedFlag), this._focusChange("advanceFocus", aMaintainViewFocusedFlag)
},
/** /**
* Focuses the previous scope, variable or property in this view. * Focuses the previous scope, variable or property in this view.
* @see VariablesView.prototype._focusChange * @see VariablesView.prototype._focusChange
*/ */
focusPrevItem: function VV_focusPrevItem(aMaintainViewFocusedFlag) focusPrevItem: function VV_focusPrevItem(aMaintainViewFocusedFlag) {
this._focusChange("rewindFocus", aMaintainViewFocusedFlag), this._focusChange("rewindFocus", aMaintainViewFocusedFlag)
},
/** /**
* Focuses the next or previous scope, variable or property in this view. * Focuses the next or previous scope, variable or property in this view.
@ -761,7 +768,6 @@ VariablesView.prototype = {
if (!item._isArrowVisible) { if (!item._isArrowVisible) {
return; return;
} }
// Expand scopes, variables and properties before advancing focus. // Expand scopes, variables and properties before advancing focus.
if (!item._isExpanded) { if (!item._isExpanded) {
item.expand(); item.expand();
@ -1051,7 +1057,8 @@ VariablesView.getterOrSetterEvalMacro = function(aItem, aCurrentString) {
VariablesView.getterOrSetterDeleteCallback = function(aItem) { VariablesView.getterOrSetterDeleteCallback = function(aItem) {
aItem._disable(); aItem._disable();
// Make sure the right getter/setter to value override macro is applied to the target object. // Make sure the right getter/setter to value override macro is applied
// to the target object.
aItem.ownerView.eval(aItem.evaluationMacro(aItem, "")); aItem.ownerView.eval(aItem.evaluationMacro(aItem, ""));
return true; // Don't hide the element. return true; // Don't hide the element.
@ -1237,7 +1244,7 @@ Scope.prototype = {
// even if they were already displayed before. In this case, show a throbber // even if they were already displayed before. In this case, show a throbber
// to suggest that this scope is expanding. // to suggest that this scope is expanding.
if (!this._isExpanding && if (!this._isExpanding &&
this._variablesView.lazyAppend && this._variablesView.lazyExpand &&
this._store.size > LAZY_APPEND_BATCH) { this._store.size > LAZY_APPEND_BATCH) {
this._isExpanding = true; this._isExpanding = true;
@ -1971,6 +1978,13 @@ function Variable(aScope, aName, aDescriptor) {
this._activateNameInput = this._activateNameInput.bind(this); this._activateNameInput = this._activateNameInput.bind(this);
this._activateValueInput = this._activateValueInput.bind(this); this._activateValueInput = this._activateValueInput.bind(this);
// Treat safe getter descriptors as descriptors with a value.
if ("getterValue" in aDescriptor) {
aDescriptor.value = aDescriptor.getterValue;
delete aDescriptor.get;
delete aDescriptor.set;
}
Scope.call(this, aScope, aName, this._initialDescriptor = aDescriptor); Scope.call(this, aScope, aName, this._initialDescriptor = aDescriptor);
this.setGrip(aDescriptor.value); this.setGrip(aDescriptor.value);
this._symbolicName = aName; this._symbolicName = aName;
@ -1995,6 +2009,8 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
* - { value: { type: "object", class: "Object" } } * - { value: { type: "object", class: "Object" } }
* - { get: { type: "object", class: "Function" }, * - { get: { type: "object", class: "Function" },
* set: { type: "undefined" } } * set: { type: "undefined" } }
* - { get: { type "object", class: "Function" },
* getterValue: "foo", getterPrototypeLevel: 2 }
* @param boolean aRelaxed * @param boolean aRelaxed
* True if name duplicates should be allowed. * True if name duplicates should be allowed.
* @return Property * @return Property
@ -2374,14 +2390,17 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
let configurableLabel = document.createElement("label"); let configurableLabel = document.createElement("label");
let enumerableLabel = document.createElement("label"); let enumerableLabel = document.createElement("label");
let writableLabel = document.createElement("label"); let writableLabel = document.createElement("label");
let safeGetterLabel = document.createElement("label");
configurableLabel.setAttribute("value", "configurable"); configurableLabel.setAttribute("value", "configurable");
enumerableLabel.setAttribute("value", "enumerable"); enumerableLabel.setAttribute("value", "enumerable");
writableLabel.setAttribute("value", "writable"); writableLabel.setAttribute("value", "writable");
safeGetterLabel.setAttribute("value", "native-getter");
tooltip.setAttribute("orient", "horizontal"); tooltip.setAttribute("orient", "horizontal");
tooltip.appendChild(configurableLabel); tooltip.appendChild(configurableLabel);
tooltip.appendChild(enumerableLabel); tooltip.appendChild(enumerableLabel);
tooltip.appendChild(writableLabel); tooltip.appendChild(writableLabel);
tooltip.appendChild(safeGetterLabel);
this._target.appendChild(tooltip); this._target.appendChild(tooltip);
this._target.setAttribute("tooltip", tooltip.id); this._target.setAttribute("tooltip", tooltip.id);
@ -2420,6 +2439,9 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, {
if (!descriptor.null && !descriptor.writable && !this.ownerView.getter && !this.ownerView.setter) { if (!descriptor.null && !descriptor.writable && !this.ownerView.getter && !this.ownerView.setter) {
this._target.setAttribute("non-writable", ""); this._target.setAttribute("non-writable", "");
} }
if (descriptor && "getterValue" in descriptor) {
this._target.setAttribute("safe-getter", "");
}
if (name == "this") { if (name == "this") {
this._target.setAttribute("self", ""); this._target.setAttribute("self", "");
} }

Просмотреть файл

@ -305,43 +305,39 @@ ViewHelpers.Prefs.prototype = {
* *
* @param any aAttachment * @param any aAttachment
* Some attached primitive/object. * Some attached primitive/object.
* @param string aLabel * @param nsIDOMNode | nsIDOMDocumentFragment | array aContents [optional]
* The label displayed in the container. * A prebuilt node, or an array containing the following properties:
* @param string aValue * - aLabel: the label displayed in the container
* The actual internal value of the item. * - aValue: the actual internal value of the item
* @param string aDescription [optional] * - aDescription: an optional description of the item
* An optional description of the item.
*/ */
this.MenuItem = function MenuItem(aAttachment, aLabel, aValue, aDescription) { this.MenuItem = function MenuItem(aAttachment, aContents = []) {
this.attachment = aAttachment; this.attachment = aAttachment;
this._label = aLabel + "";
this._value = aValue + ""; // Allow the insertion of prebuilt nodes.
this._description = (aDescription || "") + ""; if (aContents instanceof Ci.nsIDOMNode ||
aContents instanceof Ci.nsIDOMDocumentFragment) {
this._prebuiltTarget = aContents;
}
// Delegate the item view creation to a container widget.
else {
let [aLabel, aValue, aDescription] = aContents;
this._label = aLabel + "";
this._value = aValue + "";
this._description = (aDescription || "") + "";
}
}; };
MenuItem.prototype = { MenuItem.prototype = {
/**
* Gets the label set for this item.
* @return string
*/
get label() this._label, get label() this._label,
/**
* Gets the value set for this item.
* @return string
*/
get value() this._value, get value() this._value,
/**
* Gets the description set for this item.
* @return string
*/
get description() this._description, get description() this._description,
get target() this._target,
/** /**
* Immediately appends a child item to this menu item. * Immediately appends a child item to this menu item.
* *
* @param nsIDOMNode * @param nsIDOMNode aElement
* An nsIDOMNode representing the child element to append. * An nsIDOMNode representing the child element to append.
* @param object aOptions [optional] * @param object aOptions [optional]
* Additional options or flags supported by this operation: * Additional options or flags supported by this operation:
@ -363,7 +359,7 @@ MenuItem.prototype = {
} }
// Entangle the item with the newly inserted child node. // Entangle the item with the newly inserted child node.
this._entangleItem(item, this.target.appendChild(aElement)); this._entangleItem(item, this._target.appendChild(aElement));
// Return the item associated with the displayed element. // Return the item associated with the displayed element.
return item; return item;
@ -379,7 +375,7 @@ MenuItem.prototype = {
if (!aItem) { if (!aItem) {
return; return;
} }
this.target.removeChild(aItem.target); this._target.removeChild(aItem._target);
this._untangleItem(aItem); this._untangleItem(aItem);
}, },
@ -387,20 +383,20 @@ MenuItem.prototype = {
* Visually marks this menu item as selected. * Visually marks this menu item as selected.
*/ */
markSelected: function MI_markSelected() { markSelected: function MI_markSelected() {
if (!this.target) { if (!this._target) {
return; return;
} }
this.target.classList.add("selected"); this._target.classList.add("selected");
}, },
/** /**
* Visually marks this menu item as deselected. * Visually marks this menu item as deselected.
*/ */
markDeselected: function MI_markDeselected() { markDeselected: function MI_markDeselected() {
if (!this.target) { if (!this._target) {
return; return;
} }
this.target.classList.remove("selected"); this._target.classList.remove("selected");
}, },
/** /**
@ -411,7 +407,7 @@ MenuItem.prototype = {
* @param nsIDOMNode aElement [optional] * @param nsIDOMNode aElement [optional]
* A custom element to set the attributes to. * A custom element to set the attributes to.
*/ */
setAttributes: function MI_setAttributes(aAttributes, aElement = this.target) { setAttributes: function MI_setAttributes(aAttributes, aElement = this._target) {
for (let [name, value] of aAttributes) { for (let [name, value] of aAttributes) {
aElement.setAttribute(name, value); aElement.setAttribute(name, value);
} }
@ -431,7 +427,7 @@ MenuItem.prototype = {
} }
this._itemsByElement.set(aElement, aItem); this._itemsByElement.set(aElement, aItem);
aItem.target = aElement; aItem._target = aElement;
}, },
/** /**
@ -448,8 +444,19 @@ MenuItem.prototype = {
aItem.remove(childItem); aItem.remove(childItem);
} }
this._itemsByElement.delete(aItem.target); this._unlinkItem(aItem);
aItem.target = null; aItem._prebuiltTarget = null;
aItem._target = null;
},
/**
* Deletes an item from the its parent's storage maps.
*
* @param MenuItem aItem
* The item to forget.
*/
_unlinkItem: function MC__unlinkItem(aItem) {
this._itemsByElement.delete(aItem._target);
}, },
/** /**
@ -469,7 +476,8 @@ MenuItem.prototype = {
_label: "", _label: "",
_value: "", _value: "",
_description: "", _description: "",
target: null, _prebuiltTarget: null,
_target: null,
finalize: null, finalize: null,
attachment: null attachment: null
}; };
@ -537,8 +545,8 @@ MenuContainer.prototype = {
* (items with "undefined" or "null" labels/values). This can, as well, be * (items with "undefined" or "null" labels/values). This can, as well, be
* overridden via the "relaxed" flag. * overridden via the "relaxed" flag.
* *
* @param nsIDOMNode | object aContents * @param nsIDOMNode | nsIDOMDocumentFragment array aContents
* An nsIDOMNode, or an array containing the following properties: * A prebuilt node, or an array containing the following properties:
* - label: the label displayed in the container * - label: the label displayed in the container
* - value: the actual internal value of the item * - value: the actual internal value of the item
* - description: an optional description of the item * - description: an optional description of the item
@ -555,23 +563,17 @@ MenuContainer.prototype = {
* undefined if the item was staged for a later commit. * undefined if the item was staged for a later commit.
*/ */
push: function MC_push(aContents, aOptions = {}) { push: function MC_push(aContents, aOptions = {}) {
if (aContents instanceof Ci.nsIDOMNode || let item = new MenuItem(aOptions.attachment, aContents);
aContents instanceof Ci.nsIDOMElement) {
// Allow the insertion of prebuilt nodes.
aOptions.node = aContents;
aContents = ["", "", ""];
}
let [label, value, description] = aContents;
let item = new MenuItem(aOptions.attachment, label, value, description);
// Batch the item to be added later. // Batch the item to be added later.
if (aOptions.staged) { if (aOptions.staged) {
// Commit operations will ignore any specified index.
delete aOptions.index;
return void this._stagedItems.push({ item: item, options: aOptions }); return void this._stagedItems.push({ item: item, options: aOptions });
} }
// Find the target position in this container and insert the item there. // Find the target position in this container and insert the item there.
if (!("index" in aOptions)) { if (!("index" in aOptions)) {
return this._insertItemAt(this._findExpectedIndex(label), item, aOptions); return this._insertItemAt(this._findExpectedIndex(item), item, aOptions);
} }
// Insert the item at the specified index. If negative or out of bounds, // Insert the item at the specified index. If negative or out of bounds,
// the item will be simply appended. // the item will be simply appended.
@ -580,6 +582,7 @@ MenuContainer.prototype = {
/** /**
* Flushes all the prepared items into this container. * Flushes all the prepared items into this container.
* Any specified index on the items will be ignored. Everything is appended.
* *
* @param object aOptions [optional] * @param object aOptions [optional]
* Additional options or flags supported by this operation: * Additional options or flags supported by this operation:
@ -590,8 +593,7 @@ MenuContainer.prototype = {
// Sort the items before adding them to this container, if preferred. // Sort the items before adding them to this container, if preferred.
if (aOptions.sorted) { if (aOptions.sorted) {
stagedItems.sort(function(a, b) a.item._label.toLowerCase() > stagedItems.sort((a, b) => this._sortPredicate(a.item, b.item));
b.item._label.toLowerCase());
} }
// Append the prepared items to this container. // Append the prepared items to this container.
for (let { item, options } of stagedItems) { for (let { item, options } of stagedItems) {
@ -630,7 +632,7 @@ MenuContainer.prototype = {
if (!aItem) { if (!aItem) {
return; return;
} }
this._container.removeChild(aItem.target); this._container.removeChild(aItem._target);
this._untangleItem(aItem); this._untangleItem(aItem);
}, },
@ -684,10 +686,97 @@ MenuContainer.prototype = {
*/ */
toggleContents: function MC_toggleContents(aVisibleFlag) { toggleContents: function MC_toggleContents(aVisibleFlag) {
for (let [, item] of this._itemsByElement) { for (let [, item] of this._itemsByElement) {
item.target.hidden = !aVisibleFlag; item._target.hidden = !aVisibleFlag;
} }
}, },
/**
* Sorts all the items in this container based on a predicate.
*
* @param function aPredicate [optional]
* Items are sorted according to the return value of the function, which
* will become the new default sorting predicate in this container.
* If unspecified, all items will be sorted by their label.
*/
sortContents: function MC_sortContents(aPredicate = this._sortPredicate) {
let sortedItems = this.allItems.sort(this._sortPredicate = aPredicate);
for (let i = 0, len = sortedItems.length; i < len; i++) {
this.swapItems(this.getItemAtIndex(i), sortedItems[i]);
}
},
/**
* Visually swaps two items in this container.
*
* @param MenuItem aFirst
* The first menu item to be swapped.
* @param MenuItem aSecond
* The second menu item to be swapped.
*/
swapItems: function MC_swapItems(aFirst, aSecond) {
if (aFirst == aSecond) { // We're just dandy, thank you.
return;
}
let { _prebuiltTarget: firstPrebuiltTarget, target: firstTarget } = aFirst;
let { _prebuiltTarget: secondPrebuiltTarget, target: secondTarget } = aSecond;
// If the two items were constructed with prebuilt nodes as DocumentFragments,
// then those DocumentFragments are now empty and need to be reassembled.
if (firstPrebuiltTarget instanceof Ci.nsIDOMDocumentFragment) {
for (let node of firstTarget.childNodes) {
firstPrebuiltTarget.appendChild(node.cloneNode(true));
}
}
if (secondPrebuiltTarget instanceof Ci.nsIDOMDocumentFragment) {
for (let node of secondTarget.childNodes) {
secondPrebuiltTarget.appendChild(node.cloneNode(true));
}
}
// 1. Get the indices of the two items to swap.
let i = this._indexOfElement(firstTarget);
let j = this._indexOfElement(secondTarget);
// 2. Remeber the selection index, to reselect an item, if necessary.
let selectedTarget = this._container.selectedItem;
let selectedIndex = -1;
if (selectedTarget == firstTarget) {
selectedIndex = i;
} else if (selectedTarget == secondTarget) {
selectedIndex = j;
}
// 3. Silently nuke both items, nobody needs to know about this.
this._container.removeChild(firstTarget);
this._container.removeChild(secondTarget);
this._unlinkItem(aFirst);
this._unlinkItem(aSecond);
// 4. Add the items again, but reversing their indices.
this._insertItemAt.apply(this, i < j ? [i, aSecond] : [j, aFirst]);
this._insertItemAt.apply(this, i < j ? [j, aFirst] : [i, aSecond]);
// 5. Restore the previous selection, if necessary.
if (selectedIndex == i) {
this._container.selectedItem = aFirst._target;
} else if (selectedIndex == j) {
this._container.selectedItem = aSecond._target;
}
},
/**
* Visually swaps two items in this container at specific indices.
*
* @param number aFirst
* The index of the first menu item to be swapped.
* @param number aSecond
* The index of the second menu item to be swapped.
*/
swapItemsAtIndices: function MC_swapItemsAtIndices(aFirst, aSecond) {
this.swapItems(this.getItemAtIndex(aFirst), this.getItemAtIndex(aSecond));
},
/** /**
* Checks whether an item with the specified label is among the elements * Checks whether an item with the specified label is among the elements
* shown in this container. * shown in this container.
@ -776,14 +865,15 @@ MenuContainer.prototype = {
*/ */
set selectedItem(aItem) { set selectedItem(aItem) {
// A falsy item is allowed to invalidate the current selection. // A falsy item is allowed to invalidate the current selection.
let targetNode = aItem ? aItem.target : null; let targetElement = aItem ? aItem._target : null;
// Prevent selecting the same item again, so return early. // Prevent selecting the same item again, so return early.
if (this._container.selectedItem == targetNode) { if (this._container.selectedItem == targetElement) {
return; return;
} }
this._container.selectedItem = targetNode;
ViewHelpers.dispatchEvent(targetNode, "select", aItem); this._container.selectedItem = targetElement;
ViewHelpers.dispatchEvent(targetElement, "select", aItem);
}, },
/** /**
@ -877,7 +967,7 @@ MenuContainer.prototype = {
* The index of the matched item, or -1 if nothing is found. * The index of the matched item, or -1 if nothing is found.
*/ */
indexOfItem: function MC_indexOfItem(aItem) { indexOfItem: function MC_indexOfItem(aItem) {
return this._indexOfElement(aItem.target); return this._indexOfElement(aItem._target);
}, },
/** /**
@ -931,7 +1021,20 @@ MenuContainer.prototype = {
get itemCount() this._itemsByElement.size, get itemCount() this._itemsByElement.size,
/** /**
* Returns a list of all the visible (non-hidden) items in this container. * Returns a list of all items in this container, in the displayed order.
* @return array
*/
get allItems() {
let items = [];
for (let i = 0; i < this.itemCount; i++) {
items.push(this.getItemAtIndex(i));
}
return items;
},
/**
* Returns a list of all the visible (non-hidden) items in this container,
* in no particular order.
* @return array * @return array
*/ */
get visibleItems() { get visibleItems() {
@ -987,25 +1090,26 @@ MenuContainer.prototype = {
* True if the element is eligible, false otherwise. * True if the element is eligible, false otherwise.
*/ */
isEligible: function MC_isEligible(aItem) { isEligible: function MC_isEligible(aItem) {
return this.isUnique(aItem) && return aItem._prebuiltTarget || (this.isUnique(aItem) &&
aItem._label != "undefined" && aItem._label != "null" && aItem._label != "undefined" && aItem._label != "null" &&
aItem._value != "undefined" && aItem._value != "null"; aItem._value != "undefined" && aItem._value != "null");
}, },
/** /**
* Finds the expected item index in this container based on its label. * Finds the expected item index in this container based on the default
* sort predicate.
* *
* @param string aLabel * @param MenuItem aItem
* The label used to identify the element. * The item to get the expected index for.
* @return number * @return number
* The expected item index. * The expected item index.
*/ */
_findExpectedIndex: function MC__findExpectedIndex(aLabel) { _findExpectedIndex: function MC__findExpectedIndex(aItem) {
let container = this._container; let container = this._container;
let itemCount = this.itemCount; let itemCount = this.itemCount;
for (let i = 0; i < itemCount; i++) { for (let i = 0; i < itemCount; i++) {
if (this.getItemAtIndex(i)._label > aLabel) { if (this._sortPredicate(this.getItemAtIndex(i), aItem) > 0) {
return i; return i;
} }
} }
@ -1028,7 +1132,7 @@ MenuContainer.prototype = {
* @return MenuItem * @return MenuItem
* The item associated with the displayed element, null if rejected. * The item associated with the displayed element, null if rejected.
*/ */
_insertItemAt: function MC__insertItemAt(aIndex, aItem, aOptions) { _insertItemAt: function MC__insertItemAt(aIndex, aItem, aOptions = {}) {
// Relaxed nodes may be appended without verifying their eligibility. // Relaxed nodes may be appended without verifying their eligibility.
if (!aOptions.relaxed && !this.isEligible(aItem)) { if (!aOptions.relaxed && !this.isEligible(aItem)) {
return null; return null;
@ -1036,14 +1140,14 @@ MenuContainer.prototype = {
// Entangle the item with the newly inserted node. // Entangle the item with the newly inserted node.
this._entangleItem(aItem, this._container.insertItemAt(aIndex, this._entangleItem(aItem, this._container.insertItemAt(aIndex,
aOptions.node || aItem._label, aItem._prebuiltTarget || aItem._label, // Allow the insertion of prebuilt nodes.
aItem._value, aItem._value,
aItem._description, aItem._description,
aOptions.attachment)); aItem.attachment));
// Handle any additional options after entangling the item. // Handle any additional options after entangling the item.
if (aOptions.attributes) { if (aOptions.attributes) {
aItem.setAttributes(aOptions.attributes, aItem.target); aItem.setAttributes(aOptions.attributes, aItem._target);
} }
if (aOptions.finalize) { if (aOptions.finalize) {
aItem.finalize = aOptions.finalize; aItem.finalize = aOptions.finalize;
@ -1065,7 +1169,7 @@ MenuContainer.prototype = {
this._itemsByLabel.set(aItem._label, aItem); this._itemsByLabel.set(aItem._label, aItem);
this._itemsByValue.set(aItem._value, aItem); this._itemsByValue.set(aItem._value, aItem);
this._itemsByElement.set(aElement, aItem); this._itemsByElement.set(aElement, aItem);
aItem.target = aElement; aItem._target = aElement;
}, },
/** /**
@ -1082,10 +1186,38 @@ MenuContainer.prototype = {
aItem.remove(childItem); aItem.remove(childItem);
} }
this._unlinkItem(aItem);
aItem._prebuiltTarget = null;
aItem._target = null;
},
/**
* Deletes an item from the its parent's storage maps.
*
* @param MenuItem aItem
* The item to forget.
*/
_unlinkItem: function MI__unlinkItem(aItem) {
this._itemsByLabel.delete(aItem._label); this._itemsByLabel.delete(aItem._label);
this._itemsByValue.delete(aItem._value); this._itemsByValue.delete(aItem._value);
this._itemsByElement.delete(aItem.target); this._itemsByElement.delete(aItem._target);
aItem.target = null; },
/**
* The predicate used when sorting items. By default, items in this view
* are sorted by their label.
*
* @param MenuItem aFirst
* The first menu item used in the comparison.
* @param MenuItem aSecond
* The second menu item used in the comparison.
* @return number
* -1 to sort aFirst to a lower index than aSecond
* 0 to leave aFirst and aSecond unchanged with respect to each other
* 1 to sort aSecond to a lower index than aFirst
*/
_sortPredicate: function MC__sortPredicate(aFirst, aSecond) {
return +(aFirst._label.toLowerCase() > aSecond._label.toLowerCase());
}, },
_container: null, _container: null,

Просмотреть файл

@ -16,10 +16,6 @@
overflow-y: auto; overflow-y: auto;
} }
.side-menu-widget-group[name=""] > .side-menu-widget-group-title {
display: none;
}
/* VariablesView */ /* VariablesView */
.variables-view-container { .variables-view-container {

Просмотреть файл

@ -7,7 +7,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
this.EXPORTED_SYMBOLS = [ ]; this.EXPORTED_SYMBOLS = [ ];
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/devtools/gcli.jsm"); Cu.import("resource://gre/modules/devtools/gcli.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
"resource:///modules/devtools/gDevTools.jsm"); "resource:///modules/devtools/gDevTools.jsm");

Просмотреть файл

@ -23,7 +23,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
* the target's document. It wraps remote debugging protocol comunications. * the target's document. It wraps remote debugging protocol comunications.
* *
* It emits these events: * It emits these events:
* 'stylesheet-added': A stylesheet has been added to the debuggee's document * 'document-load': debuggee's document is loaded, style sheets are argument
* 'stylesheets-cleared': The debuggee's stylesheets have been reset (e.g. the * 'stylesheets-cleared': The debuggee's stylesheets have been reset (e.g. the
* page navigated) * page navigated)
* *
@ -37,12 +37,12 @@ let StyleEditorDebuggee = function(target) {
this.clear = this.clear.bind(this); this.clear = this.clear.bind(this);
this._onNewDocument = this._onNewDocument.bind(this); this._onNewDocument = this._onNewDocument.bind(this);
this._onStyleSheetsAdded = this._onStyleSheetsAdded.bind(this); this._onDocumentLoad = this._onDocumentLoad.bind(this);
this._target = target; this._target = target;
this._actor = this.target.form.styleEditorActor; this._actor = this.target.form.styleEditorActor;
this.client.addListener("styleSheetsAdded", this._onStyleSheetsAdded); this.client.addListener("documentLoad", this._onDocumentLoad);
this._target.on("navigate", this._onNewDocument); this._target.on("navigate", this._onNewDocument);
this._onNewDocument(); this._onNewDocument();
@ -128,18 +128,21 @@ StyleEditorDebuggee.prototype = {
}, },
/** /**
* Handle stylesheet-added event from the target * Handler for document load, forward event with
* all the stylesheets available on load.
* *
* @param {string} type * @param {string} type
* Type of event * Event type
* @param {object} request * @param {object} request
* Event details * Object with 'styleSheets' array of actor forms
*/ */
_onStyleSheetsAdded: function(type, request) { _onDocumentLoad: function(type, request) {
let sheets = [];
for (let form of request.styleSheets) { for (let form of request.styleSheets) {
let sheet = this._addStyleSheet(form); let sheet = this._addStyleSheet(form);
this.emit("stylesheet-added", sheet); sheets.push(sheet);
} }
this.emit("document-load", sheets);
}, },
/** /**
@ -191,7 +194,6 @@ StyleEditorDebuggee.prototype = {
destroy: function() { destroy: function() {
this.clear(); this.clear();
this._target.off("will-navigate", this.clear);
this._target.off("navigate", this._onNewDocument); this._target.off("navigate", this._onNewDocument);
} }
} }

Просмотреть файл

@ -110,7 +110,6 @@ StyleEditorPanel.prototype = {
if (!this._destroyed) { if (!this._destroyed) {
this._destroyed = true; this._destroyed = true;
this._target.off("will-navigate", this.beforeNavigate);
this._target.off("close", this.destroy); this._target.off("close", this.destroy);
this._target = null; this._target = null;
this._toolbox = null; this._toolbox = null;

Просмотреть файл

@ -49,12 +49,12 @@ function StyleEditorUI(debuggee, panelDoc) {
this.editors = []; this.editors = [];
this.selectedStyleSheetIndex = -1; this.selectedStyleSheetIndex = -1;
this._onStyleSheetAdded = this._onStyleSheetAdded.bind(this);
this._onStyleSheetCreated = this._onStyleSheetCreated.bind(this); this._onStyleSheetCreated = this._onStyleSheetCreated.bind(this);
this._onStyleSheetsCleared = this._onStyleSheetsCleared.bind(this); this._onStyleSheetsCleared = this._onStyleSheetsCleared.bind(this);
this._onDocumentLoad = this._onDocumentLoad.bind(this);
this._onError = this._onError.bind(this); this._onError = this._onError.bind(this);
debuggee.on("stylesheet-added", this._onStyleSheetAdded); debuggee.on("document-load", this._onDocumentLoad);
debuggee.on("stylesheets-cleared", this._onStyleSheetsCleared); debuggee.on("stylesheets-cleared", this._onStyleSheetsCleared);
this.createUI(); this.createUI();
@ -156,17 +156,21 @@ StyleEditorUI.prototype = {
}, },
/** /**
* Handler for debuggee's 'stylesheet-added' event. Add an editor. * Handler for debuggee's 'document-load' event. Add editors
* for all style sheets in the document
* *
* @param {string} event * @param {string} event
* Event name * Event name
* @param {StyleSheet} styleSheet * @param {StyleSheet} styleSheet
* StyleSheet object for new sheet * StyleSheet object for new sheet
*/ */
_onStyleSheetAdded: function(event, styleSheet) { _onDocumentLoad: function(event, styleSheets) {
for (let sheet of styleSheets) {
this._addStyleSheetEditor(sheet);
}
// this might be the first stylesheet, so remove loading indicator // this might be the first stylesheet, so remove loading indicator
this._root.classList.remove("loading"); this._root.classList.remove("loading");
this._addStyleSheetEditor(styleSheet); this.emit("document-load");
}, },
/** /**
@ -421,7 +425,7 @@ StyleEditorUI.prototype = {
destroy: function() { destroy: function() {
this._clearStyleSheetEditors(); this._clearStyleSheetEditors();
this._debuggee.off("stylesheet-added", this._onStyleSheetAdded); this._debuggee.off("document-load", this._onDocumentLoad);
this._debuggee.off("stylesheets-cleared", this._onStyleSheetsCleared); this._debuggee.off("stylesheets-cleared", this._onStyleSheetsCleared);
} }
} }

Просмотреть файл

@ -27,6 +27,7 @@ _BROWSER_TEST_FILES = \
browser_styleeditor_sv_resize.js \ browser_styleeditor_sv_resize.js \
browser_styleeditor_bug_740541_iframes.js \ browser_styleeditor_bug_740541_iframes.js \
browser_styleeditor_bug_851132_middle_click.js \ browser_styleeditor_bug_851132_middle_click.js \
browser_styleeditor_nostyle.js \
head.js \ head.js \
helpers.js \ helpers.js \
four.html \ four.html \
@ -39,6 +40,7 @@ _BROWSER_TEST_FILES = \
media.html \ media.html \
media-small.css \ media-small.css \
minified.html \ minified.html \
nostyle.html \
resources_inpage.jsi \ resources_inpage.jsi \
resources_inpage1.css \ resources_inpage1.css \
resources_inpage2.css \ resources_inpage2.css \

Просмотреть файл

@ -0,0 +1,41 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "nostyle.html";
function test()
{
waitForExplicitFinish();
// launch Style Editor right when the tab is created (before load)
// this checks that the Style Editor still launches correctly when it is opened
// *while* the page is still loading. The Style Editor should not signal that
// it is loaded until the accompanying content page is loaded.
addTabAndOpenStyleEditor(function(panel) {
panel.UI.once("document-load", testDocumentLoad);
content.location = TESTCASE_URI;
});
}
function testDocumentLoad(event)
{
let root = gPanelWindow.document.querySelector(".splitview-root");
ok(!root.classList.contains("loading"),
"style editor root element does not have 'loading' class name anymore");
ok(root.querySelector(".empty.placeholder"), "showing 'no style' indicator");
let button = gPanelWindow.document.querySelector(".style-editor-newButton");
ok(!button.hasAttribute("disabled"),
"new style sheet button is enabled");
button = gPanelWindow.document.querySelector(".style-editor-importButton");
ok(!button.hasAttribute("disabled"),
"import button is enabled");
finish();
}

Просмотреть файл

@ -21,11 +21,10 @@ this.EXPORTED_SYMBOLS = [ 'helpers' ];
var helpers = {}; var helpers = {};
this.helpers = helpers; this.helpers = helpers;
let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require; let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).require;
Components.utils.import("resource:///modules/devtools/gcli.jsm", {}); Components.utils.import("resource://gre/modules/devtools/gcli.jsm", {});
let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console; let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
let devtools = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools; let TargetFactory = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools.TargetFactory;
let TargetFactory = devtools.TargetFactory;
let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise; let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise;
let assert = { ok: ok, is: is, log: info }; let assert = { ok: ok, is: is, log: info };
@ -158,6 +157,7 @@ helpers.runTests = function(options, tests) {
}); });
var recover = function(error) { var recover = function(error) {
ok(false, error);
console.error(error); console.error(error);
}; };
@ -341,7 +341,7 @@ helpers._createDebugCheck = function(options) {
output += ' current: \'' + helpers._actual.current(options) + '\',\n'; output += ' current: \'' + helpers._actual.current(options) + '\',\n';
output += ' status: \'' + helpers._actual.status(options) + '\',\n'; output += ' status: \'' + helpers._actual.status(options) + '\',\n';
output += ' options: ' + outputArray(helpers._actual.options(options)) + ',\n'; output += ' options: ' + outputArray(helpers._actual.options(options)) + ',\n';
output += ' error: \'' + helpers._actual.message(options) + '\',\n'; output += ' message: \'' + helpers._actual.message(options) + '\',\n';
output += ' predictions: ' + outputArray(predictions) + ',\n'; output += ' predictions: ' + outputArray(predictions) + ',\n';
output += ' unassigned: ' + outputArray(requisition._unassigned) + ',\n'; output += ' unassigned: ' + outputArray(requisition._unassigned) + ',\n';
output += ' outputState: \'' + helpers._actual.outputState(options) + '\',\n'; output += ' outputState: \'' + helpers._actual.outputState(options) + '\',\n';
@ -378,6 +378,8 @@ helpers._createDebugCheck = function(options) {
output += ' exec: {\n'; output += ' exec: {\n';
output += ' output: \'\',\n'; output += ' output: \'\',\n';
output += ' completed: true,\n'; output += ' completed: true,\n';
output += ' type: \'string\',\n';
output += ' error: false\n';
output += ' }\n'; output += ' }\n';
output += ' }\n'; output += ' }\n';
output += ']);'; output += ']);';
@ -702,7 +704,7 @@ helpers._check = function(options, name, checks) {
*/ */
helpers._exec = function(options, name, expected) { helpers._exec = function(options, name, expected) {
if (expected == null) { if (expected == null) {
return Promise.resolve(); return Promise.resolve({});
} }
var output = options.display.requisition.exec({ hidden: true }); var output = options.display.requisition.exec({ hidden: true });
@ -715,20 +717,32 @@ helpers._exec = function(options, name, expected) {
if (!options.window.document.createElement) { if (!options.window.document.createElement) {
assert.log('skipping output tests (missing doc.createElement) for ' + name); assert.log('skipping output tests (missing doc.createElement) for ' + name);
return Promise.resolve(); return Promise.resolve({ output: output });
} }
if (!('output' in expected)) { if (!('output' in expected)) {
return Promise.resolve(); return Promise.resolve({ output: output });
} }
var deferred = Promise.defer();
var checkOutput = function() { var checkOutput = function() {
var div = options.window.document.createElement('div'); var div = options.window.document.createElement('div');
var nodePromise = converters.convert(output.data, output.type, 'dom', var conversionContext = options.display.requisition.conversionContext;
options.display.requisition.context);
nodePromise.then(function(node) { if ('type' in expected) {
assert.is(output.type,
expected.type,
'output.type for: ' + name);
}
if ('error' in expected) {
assert.is(output.error,
expected.error,
'output.error for: ' + name);
}
var convertPromise = converters.convert(output.data, output.type, 'dom',
conversionContext);
return convertPromise.then(function(node) {
div.appendChild(node); div.appendChild(node);
var actualOutput = div.textContent.trim(); var actualOutput = div.textContent.trim();
@ -757,24 +771,11 @@ helpers._exec = function(options, name, expected) {
doTest(expected.output, actualOutput); doTest(expected.output, actualOutput);
} }
deferred.resolve(actualOutput); return { output: output, text: actualOutput };
}); });
}; };
if (output.completed !== false) { return output.promise.then(checkOutput, checkOutput);
checkOutput();
}
else {
var changed = function() {
if (output.completed !== false) {
checkOutput();
output.onChange.remove(changed);
}
};
output.onChange.add(changed);
}
return deferred.promise;
}; };
/** /**
@ -789,15 +790,15 @@ helpers._setup = function(options, name, action) {
return Promise.resolve(action()); return Promise.resolve(action());
} }
return Promise.reject('setup must be a string or a function'); return Promise.reject('\'setup\' property must be a string or a function. Is ' + action);
}; };
/** /**
* Helper to shutdown the test * Helper to shutdown the test
*/ */
helpers._post = function(name, action, output) { helpers._post = function(name, action, data) {
if (typeof action === 'function') { if (typeof action === 'function') {
return Promise.resolve(action(output)); return Promise.resolve(action(data.output, data.text));
} }
return Promise.resolve(action); return Promise.resolve(action);
}; };
@ -943,19 +944,22 @@ helpers.audit = function(options, audits) {
if (typeof chunkLen !== 'number') { if (typeof chunkLen !== 'number') {
chunkLen = 1; chunkLen = 1;
} }
var responseTime = (new Date().getTime() - start) / chunkLen;
totalResponseTime += responseTime; if (assert.currentTest) {
if (responseTime > maxResponseTime) { var responseTime = (new Date().getTime() - start) / chunkLen;
maxResponseTime = responseTime; totalResponseTime += responseTime;
maxResponseCulprit = assert.currentTest + '/' + name; if (responseTime > maxResponseTime) {
maxResponseTime = responseTime;
maxResponseCulprit = assert.currentTest + '/' + name;
}
averageOver++;
} }
averageOver++;
var checkDone = helpers._check(options, name, audit.check); var checkDone = helpers._check(options, name, audit.check);
return checkDone.then(function() { return checkDone.then(function() {
var execDone = helpers._exec(options, name, audit.exec); var execDone = helpers._exec(options, name, audit.exec);
return execDone.then(function(output) { return execDone.then(function(data) {
return helpers._post(name, audit.post, output).then(function() { return helpers._post(name, audit.post, data).then(function() {
if (assert.testLogging) { if (assert.testLogging) {
log('- END \'' + name + '\' in ' + assert.currentTest); log('- END \'' + name + '\' in ' + assert.currentTest);
} }
@ -963,9 +967,8 @@ helpers.audit = function(options, audits) {
}); });
}); });
}); });
}).then(null, function(ex) { }).then(function() {
console.error(ex.stack); return options.display.inputter.setInput('');
throw(ex);
}); });
}; };

Просмотреть файл

@ -0,0 +1,5 @@
<html>
<div>
Page with no stylesheets
</div>
</html>

Просмотреть файл

@ -12,7 +12,7 @@ let {CssLogic} = require("devtools/styleinspector/css-logic");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PluralForm.jsm"); Cu.import("resource://gre/modules/PluralForm.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/devtools/Templater.jsm"); Cu.import("resource://gre/modules/devtools/Templater.jsm");
Cu.import("resource:///modules/devtools/gDevTools.jsm"); Cu.import("resource:///modules/devtools/gDevTools.jsm");

Просмотреть файл

@ -941,8 +941,8 @@ CssRuleView.prototype = {
*/ */
nodeChanged: function CssRuleView_nodeChanged() nodeChanged: function CssRuleView_nodeChanged()
{ {
// Ignore refreshes during editing. // Ignore refreshes during editing or when no element is selected.
if (this.isEditing) { if (this.isEditing || !this._elementStyle) {
return; return;
} }
@ -1324,6 +1324,7 @@ function TextPropertyEditor(aRuleEditor, aProperty)
this.doc = aRuleEditor.doc; this.doc = aRuleEditor.doc;
this.prop = aProperty; this.prop = aProperty;
this.prop.editor = this; this.prop.editor = this;
this.browserWindow = this.doc.defaultView.top;
let sheet = this.prop.rule.sheet; let sheet = this.prop.rule.sheet;
let href = sheet ? CssLogic.href(sheet) : null; let href = sheet ? CssLogic.href(sheet) : null;
@ -1520,9 +1521,14 @@ TextPropertyEditor.prototype = {
href: this.resolveURI(resourceURI) href: this.resolveURI(resourceURI)
}); });
a.addEventListener("click", function(aEvent) { a.addEventListener("click", (aEvent) => {
// Clicks within the link shouldn't trigger editing. // Clicks within the link shouldn't trigger editing.
aEvent.stopPropagation(); aEvent.stopPropagation();
aEvent.preventDefault();
this.browserWindow.openUILinkIn(aEvent.target.href, "tab");
}, false); }, false);
appendText(this.valueSpan, val.split(resourceURI)[1]); appendText(this.valueSpan, val.split(resourceURI)[1]);

Просмотреть файл

@ -6,7 +6,7 @@
this.EXPORTED_SYMBOLS = [ ]; this.EXPORTED_SYMBOLS = [ ];
Components.utils.import('resource://gre/modules/XPCOMUtils.jsm'); Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
Components.utils.import("resource:///modules/devtools/gcli.jsm"); Components.utils.import("resource://gre/modules/devtools/gcli.jsm");
Components.utils.import("resource:///modules/devtools/gDevTools.jsm"); Components.utils.import("resource:///modules/devtools/gDevTools.jsm");
// Fetch TiltManager using the current loader, but don't save a // Fetch TiltManager using the current loader, but don't save a

Просмотреть файл

@ -11,7 +11,6 @@ include $(DEPTH)/config/autoconf.mk
EXTRA_JS_MODULES = \ EXTRA_JS_MODULES = \
HUDService.jsm \ HUDService.jsm \
PropertyPanel.jsm \
NetworkPanel.jsm \ NetworkPanel.jsm \
WebConsolePanel.jsm \ WebConsolePanel.jsm \
$(NULL) $(NULL)

Просмотреть файл

@ -1,501 +0,0 @@
/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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/. */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
this.EXPORTED_SYMBOLS = ["PropertyPanel", "PropertyTreeView"];
///////////////////////////////////////////////////////////////////////////
//// PropertyTreeView.
/**
* This is an implementation of the nsITreeView interface. For comments on the
* interface properties, see the documentation:
* https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsITreeView
*/
this.PropertyTreeView = function() {
this._rows = [];
this._objectActors = [];
};
PropertyTreeView.prototype = {
/**
* Stores the visible rows of the tree.
* @private
*/
_rows: null,
/**
* Stores the nsITreeBoxObject for this tree.
* @private
*/
_treeBox: null,
/**
* Track known object actor IDs. We clean these when the panel is
* destroyed/cleaned up.
*
* @private
* @type array
*/
_objectActors: null,
/**
* Map fake object actors to their IDs. This is used when we inspect local
* objects.
* @private
* @type Object
*/
_localObjectActors: null,
_releaseObject: null,
_objectPropertiesProvider: null,
/**
* Use this setter to update the content of the tree.
*
* @param object aData
* A meta object that holds information about the object you want to
* display in the property panel. Object properties:
* - object:
* This is the raw object you want to display. You can only provide
* this object if you want the property panel to work in sync mode.
* - objectProperties:
* An array that holds information on the remote object being
* inspected. Each element in this array describes each property in the
* remote object. See WebConsoleUtils.inspectObject() for details.
* - objectPropertiesProvider:
* A function that is invoked when a new object is needed. This is
* called when the user tries to expand an inspectable property. The
* callback must take four arguments:
* - actorID:
* The object actor ID from which we request the properties.
* - callback:
* The callback function to be invoked when the remote object is
* received. This function takes one argument: the array of
* descriptors for each property in the object represented by the
* actor.
* - releaseObject:
* Function to invoke when an object actor should be released. The
* function must take one argument: the object actor ID.
*/
set data(aData) {
let oldLen = this._rows.length;
this.cleanup();
if (!aData) {
return;
}
if (aData.objectPropertiesProvider) {
this._objectPropertiesProvider = aData.objectPropertiesProvider;
this._releaseObject = aData.releaseObject;
this._propertiesToRows(aData.objectProperties, 0);
this._rows = aData.objectProperties;
}
else if (aData.object) {
this._localObjectActors = Object.create(null);
this._rows = this._inspectObject(aData.object);
}
else {
throw new Error("First argument must have an objectActor or an " +
"object property!");
}
if (this._treeBox) {
this._treeBox.beginUpdateBatch();
if (oldLen) {
this._treeBox.rowCountChanged(0, -oldLen);
}
this._treeBox.rowCountChanged(0, this._rows.length);
this._treeBox.endUpdateBatch();
}
},
/**
* Update a remote object so it can be used with the tree view. This method
* adds properties to each array element.
*
* @private
* @param array aObject
* The remote object you want prepared for use with the tree view.
* @param number aLevel
* The level you want to give to each property in the remote object.
*/
_propertiesToRows: function PTV__propertiesToRows(aObject, aLevel)
{
aObject.forEach(function(aItem) {
aItem._level = aLevel;
aItem._open = false;
aItem._children = null;
if (this._releaseObject) {
["value", "get", "set"].forEach(function(aProp) {
let val = aItem[aProp];
if (val && val.actor) {
this._objectActors.push(val.actor);
if (typeof val.displayString == "object" &&
val.displayString.type == "longString") {
this._objectActors.push(val.displayString.actor);
}
}
}, this);
}
}, this);
},
/**
* Inspect a local object.
*
* @private
* @param object aObject
* The object you want to inspect.
* @return array
* The array of properties, each being described in a way that is
* usable by the tree view.
*/
_inspectObject: function PTV__inspectObject(aObject)
{
this._objectPropertiesProvider = this._localPropertiesProvider.bind(this);
let children =
WebConsoleUtils.inspectObject(aObject, this._localObjectGrip.bind(this));
this._propertiesToRows(children, 0);
return children;
},
/**
* Make a local fake object actor for the given object.
*
* @private
* @param object aObject
* The object to make an actor for.
* @return object
* The fake actor grip that represents the given object.
*/
_localObjectGrip: function PTV__localObjectGrip(aObject)
{
let grip = WebConsoleUtils.getObjectGrip(aObject);
grip.actor = "obj" + gSequenceId();
this._localObjectActors[grip.actor] = aObject;
return grip;
},
/**
* A properties provider for when the user inspects local objects (not remote
* ones).
*
* @private
* @param string aActor
* The ID of the object actor you want.
* @param function aCallback
* The function you want to receive the list of properties.
*/
_localPropertiesProvider:
function PTV__localPropertiesProvider(aActor, aCallback)
{
let object = this._localObjectActors[aActor];
let properties =
WebConsoleUtils.inspectObject(object, this._localObjectGrip.bind(this));
aCallback(properties);
},
/** nsITreeView interface implementation **/
selection: null,
get rowCount() { return this._rows.length; },
setTree: function(treeBox) { this._treeBox = treeBox; },
getCellText: function PTV_getCellText(idx, column)
{
let row = this._rows[idx];
return row.name + ": " + WebConsoleUtils.getPropertyPanelValue(row);
},
getLevel: function(idx) {
return this._rows[idx]._level;
},
isContainer: function(idx) {
return typeof this._rows[idx].value == "object" && this._rows[idx].value &&
this._rows[idx].value.inspectable;
},
isContainerOpen: function(idx) {
return this._rows[idx]._open;
},
isContainerEmpty: function(idx) { return false; },
isSeparator: function(idx) { return false; },
isSorted: function() { return false; },
isEditable: function(idx, column) { return false; },
isSelectable: function(row, col) { return true; },
getParentIndex: function(idx)
{
if (this.getLevel(idx) == 0) {
return -1;
}
for (var t = idx - 1; t >= 0; t--) {
if (this.isContainer(t)) {
return t;
}
}
return -1;
},
hasNextSibling: function(idx, after)
{
let thisLevel = this.getLevel(idx);
return this._rows.slice(after + 1).some(function (r) r._level == thisLevel);
},
toggleOpenState: function(idx)
{
let item = this._rows[idx];
if (!this.isContainer(idx)) {
return;
}
if (item._open) {
this._treeBox.beginUpdateBatch();
item._open = false;
var thisLevel = item._level;
var t = idx + 1, deleteCount = 0;
while (t < this._rows.length && this.getLevel(t++) > thisLevel) {
deleteCount++;
}
if (deleteCount) {
this._rows.splice(idx + 1, deleteCount);
this._treeBox.rowCountChanged(idx + 1, -deleteCount);
}
this._treeBox.invalidateRow(idx);
this._treeBox.endUpdateBatch();
}
else {
let levelUpdate = true;
let callback = function _onRemoteResponse(aProperties) {
this._treeBox.beginUpdateBatch();
if (levelUpdate) {
this._propertiesToRows(aProperties, item._level + 1);
item._children = aProperties;
}
this._rows.splice.apply(this._rows, [idx + 1, 0].concat(item._children));
this._treeBox.rowCountChanged(idx + 1, item._children.length);
this._treeBox.invalidateRow(idx);
this._treeBox.endUpdateBatch();
item._open = true;
}.bind(this);
if (!item._children) {
this._objectPropertiesProvider(item.value.actor, callback);
}
else {
levelUpdate = false;
callback(item._children);
}
}
},
getImageSrc: function(idx, column) { },
getProgressMode : function(idx,column) { },
getCellValue: function(idx, column) { },
cycleHeader: function(col, elem) { },
selectionChanged: function() { },
cycleCell: function(idx, column) { },
performAction: function(action) { },
performActionOnCell: function(action, index, column) { },
performActionOnRow: function(action, row) { },
getRowProperties: function(idx) { return ""; },
getCellProperties: function(idx, column) { return ""; },
getColumnProperties: function(column, element) { return ""; },
setCellValue: function(row, col, value) { },
setCellText: function(row, col, value) { },
drop: function(index, orientation, dataTransfer) { },
canDrop: function(index, orientation, dataTransfer) { return false; },
/**
* Cleanup the property tree view.
*/
cleanup: function PTV_cleanup()
{
if (this._releaseObject) {
this._objectActors.forEach(this._releaseObject);
delete this._objectPropertiesProvider;
delete this._releaseObject;
}
if (this._localObjectActors) {
delete this._localObjectActors;
delete this._objectPropertiesProvider;
}
this._rows = [];
this._objectActors = [];
},
};
///////////////////////////////////////////////////////////////////////////
//// Helper for creating the panel.
/**
* Creates a DOMNode and sets all the attributes of aAttributes on the created
* element.
*
* @param nsIDOMDocument aDocument
* Document to create the new DOMNode.
* @param string aTag
* Name of the tag for the DOMNode.
* @param object aAttributes
* Attributes set on the created DOMNode.
* @returns nsIDOMNode
*/
function createElement(aDocument, aTag, aAttributes)
{
let node = aDocument.createElement(aTag);
for (var attr in aAttributes) {
node.setAttribute(attr, aAttributes[attr]);
}
return node;
}
/**
* Creates a new DOMNode and appends it to aParent.
*
* @param nsIDOMDocument aDocument
* Document to create the new DOMNode.
* @param nsIDOMNode aParent
* A parent node to append the created element.
* @param string aTag
* Name of the tag for the DOMNode.
* @param object aAttributes
* Attributes set on the created DOMNode.
* @returns nsIDOMNode
*/
function appendChild(aDocument, aParent, aTag, aAttributes)
{
let node = createElement(aDocument, aTag, aAttributes);
aParent.appendChild(node);
return node;
}
///////////////////////////////////////////////////////////////////////////
//// PropertyPanel
/**
* Creates a new PropertyPanel.
*
* @see PropertyTreeView
* @param nsIDOMNode aParent
* Parent node to append the created panel to.
* @param string aTitle
* Title for the panel.
* @param string aObject
* Object to display in the tree. For details about this object please
* see the PropertyTreeView constructor in this file.
* @param array of objects aButtons
* Array with buttons to display at the bottom of the panel.
*/
this.PropertyPanel = function PropertyPanel(aParent, aTitle, aObject, aButtons)
{
let document = aParent.ownerDocument;
// Create the underlying panel
this.panel = createElement(document, "panel", {
label: aTitle,
titlebar: "normal",
noautofocus: "true",
noautohide: "true",
close: "true",
});
// Create the tree.
let tree = this.tree = createElement(document, "tree", {
flex: 1,
hidecolumnpicker: "true"
});
let treecols = document.createElement("treecols");
appendChild(document, treecols, "treecol", {
primary: "true",
flex: 1,
hideheader: "true",
ignoreincolumnpicker: "true"
});
tree.appendChild(treecols);
tree.appendChild(document.createElement("treechildren"));
this.panel.appendChild(tree);
// Create the footer.
let footer = createElement(document, "hbox", { align: "end" });
appendChild(document, footer, "spacer", { flex: 1 });
// The footer can have butttons.
let self = this;
if (aButtons) {
aButtons.forEach(function(button) {
let buttonNode = appendChild(document, footer, "button", {
label: button.label,
accesskey: button.accesskey || "",
class: button.class || "",
});
buttonNode.addEventListener("command", button.oncommand, false);
});
}
appendChild(document, footer, "resizer", { dir: "bottomend" });
this.panel.appendChild(footer);
aParent.appendChild(this.panel);
// Create the treeView object.
this.treeView = new PropertyTreeView();
this.treeView.data = aObject;
// Set the treeView object on the tree view. This has to be done *after* the
// panel is shown. This is because the tree binding must be attached first.
this.panel.addEventListener("popupshown", function onPopupShow()
{
self.panel.removeEventListener("popupshown", onPopupShow, false);
self.tree.view = self.treeView;
}, false);
this.panel.addEventListener("popuphidden", function onPopupHide()
{
self.panel.removeEventListener("popuphidden", onPopupHide, false);
self.destroy();
}, false);
}
/**
* Destroy the PropertyPanel. This closes the panel and removes it from the
* browser DOM.
*/
PropertyPanel.prototype.destroy = function PP_destroy()
{
this.treeView.data = null;
this.panel.parentNode.removeChild(this.panel);
this.treeView = null;
this.panel = null;
this.tree = null;
}
function gSequenceId()
{
return gSequenceId.n++;
}
gSequenceId.n = 0;

Просмотреть файл

@ -125,6 +125,10 @@ MOCHITEST_BROWSER_FILES = \
browser_webconsole_bug_837351_securityerrors.js \ browser_webconsole_bug_837351_securityerrors.js \
browser_bug_865871_variables_view_close_on_esc_key.js \ browser_bug_865871_variables_view_close_on_esc_key.js \
browser_bug_865288_repeat_different_objects.js \ browser_bug_865288_repeat_different_objects.js \
browser_jsterm_inspect.js \
browser_bug_869003_inspect_cross_domain_object.js \
browser_bug_862916_console_dir_and_filter_off.js \
browser_console_native_getters.js \
head.js \ head.js \
$(NULL) $(NULL)
@ -228,6 +232,8 @@ MOCHITEST_BROWSER_FILES += \
test-eval-in-stackframe.html \ test-eval-in-stackframe.html \
test-bug-859170-longstring-hang.html \ test-bug-859170-longstring-hang.html \
test-bug-837351-security-errors.html \ test-bug-837351-security-errors.html \
test-bug-869003-top-window.html \
test-bug-869003-iframe.html \
$(NULL) $(NULL)
include $(topsrcdir)/config/rules.mk include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -0,0 +1,34 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Check that the output for console.dir() works even if Logging filter is off.
const TEST_URI = "data:text/html;charset=utf8,<p>test for bug 862916";
function test()
{
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, consoleOpened);
}, true);
}
function consoleOpened(hud)
{
ok(hud, "web console opened");
hud.setFilterState("log", false);
registerCleanupFunction(() => hud.setFilterState("log", true));
content.wrappedJSObject.fooBarz = "bug862916";
hud.jsterm.execute("console.dir(window)");
hud.jsterm.once("variablesview-fetched", (aEvent, aVar) => {
ok(aVar, "variables view object");
findVariableViewProperties(aVar, [
{ name: "fooBarz", value: "bug862916" },
], { webconsole: hud }).then(finishTest);
});
}

Просмотреть файл

@ -0,0 +1,89 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Check that users can inspect objects logged from cross-domain iframes -
// bug 869003.
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-869003-top-window.html";
let gWebConsole, gJSTerm, gVariablesView;
function test()
{
addTab("data:text/html;charset=utf8,<p>hello");
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, consoleOpened);
}, true);
}
function consoleOpened(hud)
{
gWebConsole = hud;
gJSTerm = hud.jsterm;
content.location = TEST_URI;
waitForMessages({
webconsole: hud,
messages: [{
name: "console.log message",
text: "foobar",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
objects: true,
}],
}).then(onConsoleMessage);
}
function onConsoleMessage(aResults)
{
let clickable = aResults[0].clickableElements[0];
ok(clickable, "clickable object found");
isnot(clickable.textContent.indexOf("[object Object]"), -1,
"message text check");
gJSTerm.once("variablesview-fetched", onObjFetch);
EventUtils.synthesizeMouse(clickable, 2, 2, {}, gWebConsole.iframeWindow)
}
function onObjFetch(aEvent, aVar)
{
gVariablesView = aVar._variablesView;
ok(gVariablesView, "variables view object");
findVariableViewProperties(aVar, [
{ name: "hello", value: "world!" },
{ name: "bug", value: 869003 },
], { webconsole: gWebConsole }).then(onPropFound);
}
function onPropFound(aResults)
{
let prop = aResults[0].matchedProp;
ok(prop, "matched the |hello| property in the variables view");
// Check that property value updates work.
updateVariablesViewProperty({
property: prop,
field: "value",
string: "'omgtest'",
webconsole: gWebConsole,
callback: onFetchAfterUpdate,
});
}
function onFetchAfterUpdate(aEvent, aVar)
{
info("onFetchAfterUpdate");
findVariableViewProperties(aVar, [
{ name: "hello", value: "omgtest" },
{ name: "bug", value: 869003 },
], { webconsole: gWebConsole }).then(() => {
gWebConsole = gJSTerm = gVariablesView = null;
finishTest();
});
}

Просмотреть файл

@ -0,0 +1,121 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Check that native getters and setters for DOM elements work as expected in
// variables view - bug 870220.
const TEST_URI = "data:text/html;charset=utf8,<title>bug870220</title>\n" +
"<p>hello world\n<p>native getters!";
let gWebConsole, gJSTerm, gVariablesView;
function test()
{
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, consoleOpened);
}, true);
}
function consoleOpened(hud)
{
gWebConsole = hud;
gJSTerm = hud.jsterm;
gJSTerm.execute("document");
waitForMessages({
webconsole: hud,
messages: [{
text: "[object HTMLDocument]",
category: CATEGORY_OUTPUT,
objects: true,
}],
}).then(onEvalResult);
}
function onEvalResult(aResults)
{
let clickable = aResults[0].clickableElements[0];
ok(clickable, "clickable object found");
gJSTerm.once("variablesview-fetched", onDocumentFetch);
EventUtils.synthesizeMouse(clickable, 2, 2, {}, gWebConsole.iframeWindow)
}
function onDocumentFetch(aEvent, aVar)
{
gVariablesView = aVar._variablesView;
ok(gVariablesView, "variables view object");
findVariableViewProperties(aVar, [
{ name: "title", value: "bug870220" },
{ name: "bgColor" },
], { webconsole: gWebConsole }).then(onDocumentPropsFound);
}
function onDocumentPropsFound(aResults)
{
let prop = aResults[1].matchedProp;
ok(prop, "matched the |bgColor| property in the variables view");
// Check that property value updates work.
updateVariablesViewProperty({
property: prop,
field: "value",
string: "'red'",
webconsole: gWebConsole,
callback: onFetchAfterBackgroundUpdate,
});
}
function onFetchAfterBackgroundUpdate(aEvent, aVar)
{
info("onFetchAfterBackgroundUpdate");
is(content.document.bgColor, "red", "document background color changed");
findVariableViewProperties(aVar, [
{ name: "bgColor", value: "red" },
], { webconsole: gWebConsole }).then(testParagraphs);
}
function testParagraphs()
{
gJSTerm.execute("$$('p')");
waitForMessages({
webconsole: gWebConsole,
messages: [{
text: "[object NodeList]",
category: CATEGORY_OUTPUT,
objects: true,
}],
}).then(onEvalNodeList);
}
function onEvalNodeList(aResults)
{
let clickable = aResults[0].clickableElements[0];
ok(clickable, "clickable object found");
gJSTerm.once("variablesview-fetched", onNodeListFetch);
EventUtils.synthesizeMouse(clickable, 2, 2, {}, gWebConsole.iframeWindow)
}
function onNodeListFetch(aEvent, aVar)
{
gVariablesView = aVar._variablesView;
ok(gVariablesView, "variables view object");
findVariableViewProperties(aVar, [
{ name: "0.textContent", value: /hello world/ },
{ name: "1.textContent", value: /native getters/ },
], { webconsole: gWebConsole }).then(() => {
gWebConsole = gJSTerm = gVariablesView = null;
finishTest();
});
}

Просмотреть файл

@ -0,0 +1,35 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Check that the inspect() jsterm helper function works.
function test()
{
const TEST_URI = "data:text/html;charset=utf8,<p>hello bug 869981";
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, consoleOpened);
}, true);
function consoleOpened(hud)
{
content.wrappedJSObject.testProp = "testValue";
hud.jsterm.once("variablesview-fetched", onObjFetch);
hud.jsterm.execute("inspect(window)");
}
function onObjFetch(aEvent, aVar)
{
ok(aVar._variablesView, "variables view object");
findVariableViewProperties(aVar, [
{ name: "testProp", value: "testValue" },
{ name: "document", value: "[object HTMLDocument]" },
], { webconsole: hud }).then(finishTest);
}
}

Просмотреть файл

@ -23,7 +23,7 @@ let initialString = longString.substring(0,
tempScope.DebuggerServer.LONG_STRING_INITIAL_LENGTH); tempScope.DebuggerServer.LONG_STRING_INITIAL_LENGTH);
let inputValues = [ let inputValues = [
// [showsPropertyPanel?, input value, expected output format, // [showsVariablesView?, input value, expected output format,
// print() output, console API output, optional console API test] // print() output, console API output, optional console API test]
// 0 // 0
@ -132,7 +132,7 @@ function testNext() {
function testGen() { function testGen() {
let cpos = pos; let cpos = pos;
let showsPropertyPanel = inputValues[cpos][0]; let showsVariablesView = inputValues[cpos][0];
let inputValue = inputValues[cpos][1]; let inputValue = inputValues[cpos][1];
let expectedOutput = inputValues[cpos][2]; let expectedOutput = inputValues[cpos][2];
@ -224,35 +224,35 @@ function testGen() {
// Test click on output. // Test click on output.
let eventHandlerID = eventHandlers.length + 1; let eventHandlerID = eventHandlers.length + 1;
let propertyPanelShown = function(aEvent, aView, aOptions) { let variablesViewShown = function(aEvent, aView, aOptions) {
if (aOptions.label.indexOf(expectedOutput) == -1) { if (aOptions.label.indexOf(expectedOutput) == -1) {
return; return;
} }
HUD.jsterm.off("variablesview-open", propertyPanelShown); HUD.jsterm.off("variablesview-open", variablesViewShown);
eventHandlers[eventHandlerID] = null; eventHandlers[eventHandlerID] = null;
ok(showsPropertyPanel, ok(showsVariablesView,
"the property panel shown for inputValues[" + cpos + "]"); "the variables view shown for inputValues[" + cpos + "]");
popupShown[cpos] = true; popupShown[cpos] = true;
if (showsPropertyPanel) { if (showsVariablesView) {
executeSoon(subtestNext); executeSoon(subtestNext);
} }
}; };
HUD.jsterm.on("variablesview-open", propertyPanelShown); HUD.jsterm.on("variablesview-open", variablesViewShown);
eventHandlers.push(propertyPanelShown); eventHandlers.push(variablesViewShown);
// Send the mousedown, mouseup and click events to check if the property // Send the mousedown, mouseup and click events to check if the variables
// panel opens. // view opens.
EventUtils.sendMouseEvent({ type: "mousedown" }, messageBody, window); EventUtils.sendMouseEvent({ type: "mousedown" }, messageBody, window);
EventUtils.sendMouseEvent({ type: "click" }, messageBody, window); EventUtils.sendMouseEvent({ type: "click" }, messageBody, window);
if (showsPropertyPanel) { if (showsVariablesView) {
yield; // wait for the panel to open if we need to. yield; // wait for the panel to open if we need to.
} }
@ -276,7 +276,7 @@ function testEnd() {
for (let i = 0; i < inputValues.length; i++) { for (let i = 0; i < inputValues.length; i++) {
if (inputValues[i][0] && !popupShown[i]) { if (inputValues[i][0] && !popupShown[i]) {
ok(false, "the property panel failed to show for inputValues[" + i + "]"); ok(false, "the variables view failed to show for inputValues[" + i + "]");
} }
} }

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше