This commit is contained in:
Richard Newman 2011-12-09 00:14:26 -08:00
Родитель 5deccb376e 742a9855ce
Коммит 4b3ecc07a6
17 изменённых файлов: 1346 добавлений и 262 удалений

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

@ -169,6 +169,7 @@ _BROWSER_FILES = \
browser_tabview_bug705621.js \ browser_tabview_bug705621.js \
browser_tabview_bug706430.js \ browser_tabview_bug706430.js \
browser_tabview_bug706736.js \ browser_tabview_bug706736.js \
browser_tabview_bug707466.js \
browser_tabview_click_group.js \ browser_tabview_click_group.js \
browser_tabview_dragdrop.js \ browser_tabview_dragdrop.js \
browser_tabview_exit_button.js \ browser_tabview_exit_button.js \

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

@ -0,0 +1,60 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
waitForExplicitFinish();
// create two groups and each group has one tab item
let newState = {
windows: [{
tabs: [{
entries: [{ url: "about:blank" }],
hidden: true,
attributes: {},
extData: {
"tabview-tab":
'{"bounds":{"left":21,"top":29,"width":204,"height":153},' +
'"userSize":null,"url":"about:blank","groupID":1,' +
'"imageData":null,"title":null}'
}
},{
entries: [{ url: "about:blank" }],
hidden: false,
attributes: {},
extData: {
"tabview-tab":
'{"bounds":{"left":315,"top":29,"width":111,"height":84},' +
'"userSize":null,"url":"about:blank","groupID":2,' +
'"imageData":null,"title":null}'
},
}],
selected:2,
_closedTabs: [],
extData: {
"tabview-groups": '{"nextID":3,"activeGroupId":2}',
"tabview-group":
'{"1":{"bounds":{"left":15,"top":5,"width":280,"height":232},' +
'"userSize":null,"title":"","id":1},' +
'"2":{"bounds":{"left":309,"top":5,"width":267,"height":226},' +
'"userSize":null,"title":"","id":2}}',
"tabview-ui": '{"pageBounds":{"left":0,"top":0,"width":788,"height":548}}'
}, sizemode:"normal"
}]
};
newWindowWithState(newState, function(win) {
registerCleanupFunction(function () win.close());
whenTabViewIsShown(function() {
let cw = win.TabView.getContentWindow();
is(cw.GroupItems.groupItems.length, 2, "There are still two groups");
is(win.gBrowser.tabs.length, 1, "There is only one tab");
is(cw.UI.getActiveTab(), win.gBrowser.selectedTab._tabViewTabItem, "The last tab is selected");
finish();
}, win);
win.gBrowser.removeTab(win.gBrowser.selectedTab);
});
}

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

@ -475,7 +475,7 @@ let UI = {
} else { } else {
GroupItems.setActiveGroupItem(item); GroupItems.setActiveGroupItem(item);
if (!options || !options.dontSetActiveTabInGroup) { if (!options || !options.dontSetActiveTabInGroup) {
let activeTab = item.getActiveTab() let activeTab = item.getActiveTab();
if (activeTab) if (activeTab)
this._setActiveTab(activeTab); this._setActiveTab(activeTab);
} }
@ -574,7 +574,8 @@ let UI = {
TabItems.resumePainting(); TabItems.resumePainting();
}); });
} else { } else {
self.clearActiveTab(); if (!currentTab || !currentTab._tabViewTabItem)
self.clearActiveTab();
self._isChangingVisibility = false; self._isChangingVisibility = false;
dispatchEvent(event); dispatchEvent(event);

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

@ -37,7 +37,7 @@
* ***** END LICENSE BLOCK ***** */ * ***** END LICENSE BLOCK ***** */
var EXPORTED_SYMBOLS = [ "Templater" ]; var EXPORTED_SYMBOLS = [ "Templater", "template" ];
Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/Services.jsm");
const Node = Components.interfaces.nsIDOMNode; const Node = Components.interfaces.nsIDOMNode;
@ -45,12 +45,53 @@ const Node = Components.interfaces.nsIDOMNode;
// WARNING: do not 'use_strict' without reading the notes in _envEval(); // WARNING: do not 'use_strict' without reading the notes in _envEval();
/** /**
* A templater that allows one to quickly template DOM nodes. * Begin a new templating process.
* @param node A DOM element or string referring to an element's id
* @param data Data to use in filling out the template
* @param options Options to customize the template processing. One of:
* - allowEval: boolean (default false) Basic template interpolations are
* either property paths (e.g. ${a.b.c.d}), however if allowEval=true then we
* allow arbitrary JavaScript
*/ */
function Templater() { function template(node, data, options) {
var template = new Templater(options || {});
template.processNode(node, data);
return template;
}
/**
* Construct a Templater object. Use template() in preference to this ctor.
* @deprecated Use template(node, data, options);
*/
function Templater(options) {
if (options == null) {
options = { allowEval: true };
}
this.options = options;
this.stack = []; this.stack = [];
} }
/**
* Cached regex used to find ${...} sections in some text.
* Performance note: This regex uses ( and ) to capture the 'script' for
* further processing. Not all of the uses of this regex use this feature so
* if use of the capturing group is a performance drain then we should split
* this regex in two.
*/
Templater.prototype._templateRegion = /\$\{([^}]*)\}/g;
/**
* Cached regex used to split a string using the unicode chars F001 and F002.
* See Templater._processTextNode() for details.
*/
Templater.prototype._splitSpecial = /\uF001|\uF002/;
/**
* Cached regex used to detect if a script is capable of being interpreted
* using Template._property() or if we need to use Template._envEval()
*/
Templater.prototype._isPropertyScript = /^[a-zA-Z0-9.]*$/;
/** /**
* Recursive function to walk the tree processing the attributes as it goes. * Recursive function to walk the tree processing the attributes as it goes.
* @param node the node to process. If you pass a string in instead of a DOM * @param node the node to process. If you pass a string in instead of a DOM
@ -111,7 +152,7 @@ Templater.prototype.processNode = function(node, data) {
} }
} else { } else {
// Replace references in all other attributes // Replace references in all other attributes
var newValue = value.replace(/\$\{[^}]*\}/g, function(path) { var newValue = value.replace(this._templateRegion, function(path) {
return this._envEval(path.slice(2, -1), data, value); return this._envEval(path.slice(2, -1), data, value);
}.bind(this)); }.bind(this));
// Remove '_' prefix of attribute names so the DOM won't try // Remove '_' prefix of attribute names so the DOM won't try
@ -295,8 +336,8 @@ Templater.prototype._processTextNode = function(node, data) {
// We can then split using \uF001 or \uF002 to get an array of strings // We can then split using \uF001 or \uF002 to get an array of strings
// where scripts are prefixed with $. // where scripts are prefixed with $.
// \uF001 and \uF002 are just unicode chars reserved for private use. // \uF001 and \uF002 are just unicode chars reserved for private use.
value = value.replace(/\$\{([^}]*)\}/g, '\uF001$$$1\uF002'); value = value.replace(this._templateRegion, '\uF001$$$1\uF002');
var parts = value.split(/\uF001|\uF002/); var parts = value.split(this._splitSpecial);
if (parts.length > 1) { if (parts.length > 1) {
parts.forEach(function(part) { parts.forEach(function(part) {
if (part === null || part === undefined || part === '') { if (part === null || part === undefined || part === '') {
@ -363,7 +404,7 @@ Templater.prototype._handleAsync = function(thing, siblingNode, inserter) {
* @return The string stripped of ${ and }, or untouched if it does not match * @return The string stripped of ${ and }, or untouched if it does not match
*/ */
Templater.prototype._stripBraces = function(str) { Templater.prototype._stripBraces = function(str) {
if (!str.match(/\$\{.*\}/g)) { if (!str.match(this._templateRegion)) {
this._handleError('Expected ' + str + ' to match ${...}'); this._handleError('Expected ' + str + ' to match ${...}');
return str; return str;
} }
@ -427,17 +468,26 @@ Templater.prototype._property = function(path, data, newValue) {
* execution failed. * execution failed.
*/ */
Templater.prototype._envEval = function(script, data, frame) { Templater.prototype._envEval = function(script, data, frame) {
with (data) { try {
try { this.stack.push(frame);
this.stack.push(frame); if (this._isPropertyScript.test(script)) {
return eval(script); return this._property(script, data);
} catch (ex) { } else {
this._handleError('Template error evaluating \'' + script + '\'' + if (!this.options.allowEval) {
' environment=' + Object.keys(data).join(', '), ex); this._handleError('allowEval is not set, however \'' + script + '\'' +
return script; ' can not be resolved using a simple property path.');
} finally { return '${' + script + '}';
this.stack.pop(); }
with (data) {
return eval(script);
}
} }
} catch (ex) {
this._handleError('Template error evaluating \'' + script + '\'' +
' environment=' + Object.keys(data).join(', '), ex);
return '${' + script + '}';
} finally {
this.stack.pop();
} }
}; };

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

@ -22,7 +22,7 @@ function runTest(index) {
holder.innerHTML = options.template; holder.innerHTML = options.template;
info('Running ' + options.name); info('Running ' + options.name);
new Templater().processNode(holder, options.data); template(holder, options.data, options.options);
if (typeof options.result == 'string') { if (typeof options.result == 'string') {
is(holder.innerHTML, options.result, options.name); is(holder.innerHTML, options.result, options.name);
@ -88,6 +88,7 @@ var tests = [
function() { return { function() { return {
name: 'returnDom', name: 'returnDom',
template: '<div id="ex2">${__element.ownerDocument.createTextNode(\'pass 2\')}</div>', template: '<div id="ex2">${__element.ownerDocument.createTextNode(\'pass 2\')}</div>',
options: { allowEval: true },
data: {}, data: {},
result: '<div id="ex2">pass 2</div>' result: '<div id="ex2">pass 2</div>'
};}, };},
@ -102,6 +103,7 @@ var tests = [
function() { return { function() { return {
name: 'ifTrue', name: 'ifTrue',
template: '<p if="${name !== \'jim\'}">hello ${name}</p>', template: '<p if="${name !== \'jim\'}">hello ${name}</p>',
options: { allowEval: true },
data: { name: 'fred' }, data: { name: 'fred' },
result: '<p>hello fred</p>' result: '<p>hello fred</p>'
};}, };},
@ -109,6 +111,7 @@ var tests = [
function() { return { function() { return {
name: 'ifFalse', name: 'ifFalse',
template: '<p if="${name !== \'jim\'}">hello ${name}</p>', template: '<p if="${name !== \'jim\'}">hello ${name}</p>',
options: { allowEval: true },
data: { name: 'jim' }, data: { name: 'jim' },
result: '' result: ''
};}, };},
@ -116,6 +119,7 @@ var tests = [
function() { return { function() { return {
name: 'simpleLoop', name: 'simpleLoop',
template: '<p foreach="index in ${[ 1, 2, 3 ]}">${index}</p>', template: '<p foreach="index in ${[ 1, 2, 3 ]}">${index}</p>',
options: { allowEval: true },
data: {}, data: {},
result: '<p>1</p><p>2</p><p>3</p>' result: '<p>1</p><p>2</p><p>3</p>'
};}, };},
@ -127,6 +131,7 @@ var tests = [
result: '123' result: '123'
};}, };},
// Bug 692028: DOMTemplate memory leak with asynchronous arrays
// Bug 692031: DOMTemplate async loops do not drop the loop element // Bug 692031: DOMTemplate async loops do not drop the loop element
function() { return { function() { return {
name: 'asyncLoopElement', name: 'asyncLoopElement',
@ -150,6 +155,7 @@ var tests = [
function() { return { function() { return {
name: 'useElement', name: 'useElement',
template: '<p id="pass9">${adjust(__element)}</p>', template: '<p id="pass9">${adjust(__element)}</p>',
options: { allowEval: true },
data: { data: {
adjust: function(element) { adjust: function(element) {
is('pass9', element.id, 'useElement adjust'); is('pass9', element.id, 'useElement adjust');
@ -167,6 +173,7 @@ var tests = [
later: 'inline' later: 'inline'
};}, };},
// Bug 692028: DOMTemplate memory leak with asynchronous arrays
function() { return { function() { return {
name: 'asyncArray', name: 'asyncArray',
template: '<p foreach="i in ${delayed}">${i}</p>', template: '<p foreach="i in ${delayed}">${i}</p>',
@ -183,6 +190,7 @@ var tests = [
later: '<p>4</p><p>5</p><p>6</p>' later: '<p>4</p><p>5</p><p>6</p>'
};}, };},
// Bug 692028: DOMTemplate memory leak with asynchronous arrays
function() { return { function() { return {
name: 'asyncBoth', name: 'asyncBoth',
template: '<p foreach="i in ${delayed}">${i}</p>', template: '<p foreach="i in ${delayed}">${i}</p>',
@ -195,6 +203,38 @@ var tests = [
}, },
result: '<span></span>', result: '<span></span>',
later: '<p>4</p><p>5</p><p>6</p>' later: '<p>4</p><p>5</p><p>6</p>'
};},
// Bug 701762: DOMTemplate fails when ${foo()} returns undefined
function() { return {
name: 'functionReturningUndefiend',
template: '<p>${foo()}</p>',
options: { allowEval: true },
data: {
foo: function() {}
},
result: '<p>undefined</p>'
};},
// Bug 702642: DOMTemplate is relatively slow when evaluating JS ${}
function() { return {
name: 'propertySimple',
template: '<p>${a.b.c}</p>',
data: { a: { b: { c: 'hello' } } },
result: '<p>hello</p>'
};},
function() { return {
name: 'propertyPass',
template: '<p>${Math.max(1, 2)}</p>',
options: { allowEval: true },
result: '<p>2</p>'
};},
function() { return {
name: 'propertyFail',
template: '<p>${Math.max(1, 2)}</p>',
result: '<p>${Math.max(1, 2)}</p>'
};} };}
]; ];

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

@ -214,7 +214,10 @@ CssHtmlTree.processTemplate = function CssHtmlTree_processTemplate(aTemplate,
// All the templater does is to populate a given DOM tree with the given // All the templater does is to populate a given DOM tree with the given
// values, so we need to clone the template first. // values, so we need to clone the template first.
let duplicated = aTemplate.cloneNode(true); let duplicated = aTemplate.cloneNode(true);
new Templater().processNode(duplicated, aData);
// See https://github.com/mozilla/domtemplate/blob/master/README.md
// for docs on the template() function
template(duplicated, aData, { allowEval: true });
while (duplicated.firstChild) { while (duplicated.firstChild) {
aDestination.appendChild(duplicated.firstChild); aDestination.appendChild(duplicated.firstChild);
} }

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

@ -62,40 +62,6 @@ gcli.addCommand({
}); });
let canon = gcli._internal.require("gcli/canon");
/**
* 'help' command
*/
gcli.addCommand({
name: "help",
returnType: "html",
description: gcli.lookup("helpDesc"),
exec: function Command_help(args, context) {
let output = [];
output.push("<strong>" + gcli.lookup("helpAvailable") + ":</strong><br/>");
let commandNames = canon.getCommandNames();
commandNames.sort();
output.push("<table>");
for (let i = 0; i < commandNames.length; i++) {
let command = canon.getCommand(commandNames[i]);
if (!command.hidden && command.description) {
output.push("<tr>");
output.push('<th class="gcli-help-right">' + command.name + "</th>");
output.push("<td>&#x2192; " + command.description + "</td>");
output.push("</tr>");
}
}
output.push("</table>");
return output.join("");
}
});
/** /**
* 'console' command * 'console' command
*/ */

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

@ -92,6 +92,12 @@ XPCOMUtils.defineLazyGetter(this, "NetUtil", function () {
return obj.NetUtil; return obj.NetUtil;
}); });
XPCOMUtils.defineLazyGetter(this, "template", function () {
var obj = {};
Cu.import("resource:///modules/devtools/Templater.jsm", obj);
return obj.template;
});
XPCOMUtils.defineLazyGetter(this, "PropertyPanel", function () { XPCOMUtils.defineLazyGetter(this, "PropertyPanel", function () {
var obj = {}; var obj = {};
try { try {
@ -6854,14 +6860,38 @@ GcliTerm.prototype = {
let output = aEvent.output.output; let output = aEvent.output.output;
if (aEvent.output.command.returnType == "html" && typeof output == "string") { if (aEvent.output.command.returnType == "html" && typeof output == "string") {
let frag = this.document.createRange().createContextualFragment( output = this.document.createRange().createContextualFragment(
'<div xmlns="' + HTML_NS + '" xmlns:xul="' + XUL_NS + '">' + '<div xmlns="' + HTML_NS + '" xmlns:xul="' + XUL_NS + '">' +
output + '</div>'); output + '</div>').firstChild;
output = this.document.createElementNS(HTML_NS, "div");
output.appendChild(frag);
} }
this.writeOutput(output);
// See https://github.com/mozilla/domtemplate/blob/master/README.md
// for docs on the template() function
let element = this.document.createRange().createContextualFragment(
'<richlistitem xmlns="' + XUL_NS + '" clipboardText="${clipboardText}"' +
' timestamp="${timestamp}" id="${id}" class="hud-msg-node">' +
' <label class="webconsole-timestamp" value="${timestampString}"/>' +
' <vbox class="webconsole-msg-icon-container" style="${iconContainerStyle}">' +
' <image class="webconsole-msg-icon"/>' +
' <spacer flex="1"/>' +
' </vbox>' +
' <hbox flex="1" class="gcliterm-msg-body">${output}</hbox>' +
' <hbox align="start"><label value="1" class="webconsole-msg-repeat"/></hbox>' +
'</richlistitem>').firstChild;
let hud = HUDService.getHudReferenceById(this.hudId);
let timestamp = ConsoleUtils.timestamp();
template(element, {
iconContainerStyle: "margin-left=" + (hud.groupDepth * GROUP_INDENT) + "px",
output: output,
timestamp: timestamp,
timestampString: ConsoleUtils.timestampString(timestamp),
clipboardText: output.innerText,
id: "console-msg-" + HUDService.sequenceId()
});
ConsoleUtils.setMessageType(element, CATEGORY_OUTPUT, SEVERITY_LOG);
ConsoleUtils.outputMessageNode(element, this.hudId);
}, },
/** /**

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

@ -686,7 +686,7 @@ var mozl10n = {};
})(mozl10n); })(mozl10n);
define('gcli/index', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types/basic', 'gcli/types/javascript', 'gcli/types/node', 'gcli/cli', 'gcli/ui/display'], function(require, exports, module) { define('gcli/index', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types/basic', 'gcli/types/javascript', 'gcli/types/node', 'gcli/cli', 'gcli/commands/help', 'gcli/ui/console'], function(require, exports, module) {
// The API for use by command authors // The API for use by command authors
exports.addCommand = require('gcli/canon').addCommand; exports.addCommand = require('gcli/canon').addCommand;
@ -699,9 +699,10 @@ define('gcli/index', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types
require('gcli/types/javascript').startup(); require('gcli/types/javascript').startup();
require('gcli/types/node').startup(); require('gcli/types/node').startup();
require('gcli/cli').startup(); require('gcli/cli').startup();
require('gcli/commands/help').startup();
var Requisition = require('gcli/cli').Requisition; var Requisition = require('gcli/cli').Requisition;
var Display = require('gcli/ui/display').Display; var Console = require('gcli/ui/console').Console;
var cli = require('gcli/cli'); var cli = require('gcli/cli');
var jstype = require('gcli/types/javascript'); var jstype = require('gcli/types/javascript');
@ -739,15 +740,15 @@ define('gcli/index', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types
opts.requisition = new Requisition(opts.environment, opts.chromeDocument); opts.requisition = new Requisition(opts.environment, opts.chromeDocument);
} }
opts.display = new Display(opts); opts.console = new Console(opts);
}, },
/** /**
* Undo the effects of createView() to prevent memory leaks * Undo the effects of createView() to prevent memory leaks
*/ */
removeView: function(opts) { removeView: function(opts) {
opts.display.destroy(); opts.console.destroy();
delete opts.display; delete opts.console;
opts.requisition.destroy(); opts.requisition.destroy();
delete opts.requisition; delete opts.requisition;
@ -1029,7 +1030,8 @@ canon.removeCommand = function removeCommand(commandOrName) {
* @param name The name of the command to retrieve * @param name The name of the command to retrieve
*/ */
canon.getCommand = function getCommand(name) { canon.getCommand = function getCommand(name) {
return commands[name]; // '|| undefined' is to silence 'reference to undefined property' warnings
return commands[name] || undefined;
}; };
/** /**
@ -1190,8 +1192,16 @@ exports.createEvent = function(name) {
var dom = {}; var dom = {};
/**
* XHTML namespace
*/
dom.NS_XHTML = 'http://www.w3.org/1999/xhtml'; dom.NS_XHTML = 'http://www.w3.org/1999/xhtml';
/**
* XUL namespace
*/
dom.NS_XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
/** /**
* Create an HTML or XHTML element depending on whether the document is HTML * Create an HTML or XHTML element depending on whether the document is HTML
* or XML based. Where HTML/XHTML elements are distinguished by whether they * or XML based. Where HTML/XHTML elements are distinguished by whether they
@ -1246,12 +1256,19 @@ dom.importCss = function(cssText, doc) {
*/ */
dom.setInnerHtml = function(elem, html) { dom.setInnerHtml = function(elem, html) {
if (dom.isXmlDocument(elem.ownerDocument)) { if (dom.isXmlDocument(elem.ownerDocument)) {
dom.clearElement(elem); try {
html = '<div xmlns="' + dom.NS_XHTML + '">' + html + '</div>'; dom.clearElement(elem);
var range = elem.ownerDocument.createRange(); html = '<div xmlns="' + dom.NS_XHTML + '">' + html + '</div>';
var child = range.createContextualFragment(html).childNodes[0]; var range = elem.ownerDocument.createRange();
while (child.hasChildNodes()) { var child = range.createContextualFragment(html).firstChild;
elem.appendChild(child.firstChild); while (child.hasChildNodes()) {
elem.appendChild(child.firstChild);
}
}
catch (ex) {
console.error('Bad XHTML', ex);
console.trace();
throw ex;
} }
} }
else { else {
@ -1260,10 +1277,9 @@ dom.setInnerHtml = function(elem, html) {
}; };
/** /**
* How to detect if we're in an XUL document (and therefore should create * How to detect if we're in an XML document.
* elements in an XHTML namespace) * In a Mozilla we check that document.xmlVersion = null, however in Chrome
* In a Mozilla XUL document, document.xmlVersion = null, however in Chrome * we use document.contentType = undefined.
* document.contentType = undefined.
* @param doc The document element to work from (defaulted to the global * @param doc The document element to work from (defaulted to the global
* 'document' if missing * 'document' if missing
*/ */
@ -1479,6 +1495,13 @@ exports.lookup = function(key) {
} }
}; };
/** @see propertyLookup in lib/gcli/l10n.js */
exports.propertyLookup = Proxy.create({
get: function(rcvr, name) {
return exports.lookup(name);
}
});
/** @see lookupFormat in lib/gcli/l10n.js */ /** @see lookupFormat in lib/gcli/l10n.js */
exports.lookupFormat = function(key, swaps) { exports.lookupFormat = function(key, swaps) {
try { try {
@ -3462,6 +3485,14 @@ exports.unsetDocument = function() {
doc = undefined; doc = undefined;
}; };
/**
* Getter for the document that contains the nodes we're matching
* Most for changing things back to how they were for unit testing
*/
exports.getDocument = function() {
return doc;
};
/** /**
* A CSS expression that refers to a single node * A CSS expression that refers to a single node
@ -4042,7 +4073,15 @@ UnassignedAssignment.prototype.setUnassigned = function(args) {
*/ */
function Requisition(environment, doc) { function Requisition(environment, doc) {
this.environment = environment; this.environment = environment;
this.document = doc || document; this.document = doc;
if (this.document == null) {
try {
this.document = document;
}
catch (ex) {
// Ignore
}
}
// The command that we are about to execute. // The command that we are about to execute.
// @see setCommandConversion() // @see setCommandConversion()
@ -4508,7 +4547,8 @@ Requisition.prototype.exec = function(input) {
var outputObject = { var outputObject = {
command: command, command: command,
args: args, args: args,
typed: this.toCanonicalString(), typed: this.toString(),
canonical: this.toCanonicalString(),
completed: false, completed: false,
start: new Date() start: new Date()
}; };
@ -4527,7 +4567,7 @@ Requisition.prototype.exec = function(input) {
}).bind(this); }).bind(this);
try { try {
var context = new ExecutionContext(this.environment, this.document); var context = new ExecutionContext(this);
var reply = command.exec(args, context); var reply = command.exec(args, context);
if (reply != null && reply.isPromise) { if (reply != null && reply.isPromise) {
@ -5012,9 +5052,10 @@ exports.Requisition = Requisition;
/** /**
* Functions and data related to the execution of a command * Functions and data related to the execution of a command
*/ */
function ExecutionContext(environment, document) { function ExecutionContext(requisition) {
this.environment = environment; this.requisition = requisition;
this.document = document; this.environment = requisition.environment;
this.document = requisition.document;
} }
ExecutionContext.prototype.createPromise = function() { ExecutionContext.prototype.createPromise = function() {
@ -5041,7 +5082,275 @@ define('gcli/promise', ['require', 'exports', 'module' ], function(require, expo
* http://opensource.org/licenses/BSD-3-Clause * http://opensource.org/licenses/BSD-3-Clause
*/ */
define('gcli/ui/display', ['require', 'exports', 'module' , 'gcli/ui/inputter', 'gcli/ui/arg_fetch', 'gcli/ui/menu', 'gcli/ui/focus'], function(require, exports, module) { define('gcli/commands/help', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/util', 'gcli/l10n', 'gcli/ui/domtemplate', 'text!gcli/commands/help.css', 'text!gcli/commands/help_intro.html', 'text!gcli/commands/help_list.html', 'text!gcli/commands/help_man.html'], function(require, exports, module) {
var help = exports;
var canon = require('gcli/canon');
var util = require('gcli/util');
var l10n = require('gcli/l10n');
var domtemplate = require('gcli/ui/domtemplate');
var helpCss = require('text!gcli/commands/help.css');
var helpStyle = undefined;
var helpIntroHtml = require('text!gcli/commands/help_intro.html');
var helpIntroTemplate = undefined;
var helpListHtml = require('text!gcli/commands/help_list.html');
var helpListTemplate = undefined;
var helpManHtml = require('text!gcli/commands/help_man.html');
var helpManTemplate = undefined;
/**
* 'help' command
* We delay definition of helpCommandSpec until help.startup() to ensure that
* the l10n strings have been loaded
*/
var helpCommandSpec;
/**
* Registration and de-registration.
*/
help.startup = function() {
helpCommandSpec = {
name: 'help',
description: l10n.lookup('helpDesc'),
manual: l10n.lookup('helpManual'),
params: [
{
name: 'search',
type: 'string',
description: l10n.lookup('helpSearchDesc'),
manual: l10n.lookup('helpSearchManual'),
defaultValue: null
}
],
returnType: 'html',
exec: function(args, context) {
help.onFirstUseStartup(context.document);
var match = canon.getCommand(args.search);
if (match) {
var clone = helpManTemplate.cloneNode(true);
domtemplate.template(clone, getManTemplateData(match, context),
{ allowEval: true, stack: 'help_man.html' });
return clone;
}
var parent = util.dom.createElement(context.document, 'div');
if (!args.search) {
parent.appendChild(helpIntroTemplate.cloneNode(true));
}
parent.appendChild(helpListTemplate.cloneNode(true));
domtemplate.template(parent, getListTemplateData(args, context),
{ allowEval: true, stack: 'help_intro.html | help_list.html' });
return parent;
}
};
canon.addCommand(helpCommandSpec);
};
help.shutdown = function() {
canon.removeCommand(helpCommandSpec);
helpListTemplate = undefined;
helpStyle.parentElement.removeChild(helpStyle);
helpStyle = undefined;
};
/**
* Called when the command is executed
*/
help.onFirstUseStartup = function(document) {
if (!helpIntroTemplate) {
helpIntroTemplate = util.dom.createElement(document, 'div');
util.dom.setInnerHtml(helpIntroTemplate, helpIntroHtml);
}
if (!helpListTemplate) {
helpListTemplate = util.dom.createElement(document, 'div');
util.dom.setInnerHtml(helpListTemplate, helpListHtml);
}
if (!helpManTemplate) {
helpManTemplate = util.dom.createElement(document, 'div');
util.dom.setInnerHtml(helpManTemplate, helpManHtml);
}
if (!helpStyle && helpCss != null) {
helpStyle = util.dom.importCss(helpCss, document);
}
};
/**
* Find an element within the passed element with the class gcli-help-command
* and update the requisition to contain this text.
*/
function updateCommand(element, context) {
context.requisition.update({
typed: element.querySelector('.gcli-help-command').textContent
});
}
/**
* Find an element within the passed element with the class gcli-help-command
* and execute this text.
*/
function executeCommand(element, context) {
context.requisition.exec({
visible: true,
typed: element.querySelector('.gcli-help-command').textContent
});
}
/**
* Create a block of data suitable to be passed to the help_list.html template
*/
function getListTemplateData(args, context) {
return {
l10n: l10n.propertyLookup,
lang: context.document.defaultView.navigator.language,
onclick: function(ev) {
updateCommand(ev.currentTarget, context);
},
ondblclick: function(ev) {
executeCommand(ev.currentTarget, context);
},
getHeading: function() {
return args.search == null ?
'Available Commands:' :
'Commands starting with \'' + args.search + '\':';
},
getMatchingCommands: function() {
var matching = canon.getCommands().filter(function(command) {
if (args.search && command.name.indexOf(args.search) !== 0) {
// Filtered out because they don't match the search
return false;
}
if (!args.search && command.name.indexOf(' ') != -1) {
// We don't show sub commands with plain 'help'
return false;
}
return true;
});
matching.sort();
return matching;
}
};
}
/**
* Create a block of data suitable to be passed to the help_man.html template
*/
function getManTemplateData(command, context) {
return {
l10n: l10n.propertyLookup,
lang: context.document.defaultView.navigator.language,
command: command,
onclick: function(ev) {
updateCommand(ev.currentTarget, context);
},
getTypeDescription: function(param) {
var input = '';
if (param.defaultValue === undefined) {
input = 'required';
}
else if (param.defaultValue === null) {
input = 'optional';
}
else {
input = param.defaultValue;
}
return '(' + param.type.name + ', ' + input + ')';
}
};
}
});
/*
* 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('gcli/ui/domtemplate', ['require', 'exports', 'module' ], function(require, exports, module) {
var obj = {};
Components.utils.import('resource:///modules/devtools/Templater.jsm', obj);
exports.template = obj.template;
});
define("text!gcli/commands/help.css", [], void 0);
define("text!gcli/commands/help_intro.html", [], "\n" +
"<h2>${l10n.introHeader}</h2>\n" +
"\n" +
"<p>\n" +
" <a target=\"_blank\" href=\"https://developer.mozilla.org/AppLinks/WebConsoleHelp?locale=${lang}\">\n" +
" ${l10n.introBody}\n" +
" </a>\n" +
"</p>\n" +
"");
define("text!gcli/commands/help_list.html", [], "\n" +
"<h3>${getHeading()}</h3>\n" +
"\n" +
"<table>\n" +
" <tr foreach=\"command in ${getMatchingCommands()}\"\n" +
" onclick=\"${onclick}\" ondblclick=\"${ondblclick}\">\n" +
" <th class=\"gcli-help-name\">${command.name}</th>\n" +
" <td class=\"gcli-help-arrow\">&#x2192;</td>\n" +
" <td>\n" +
" ${command.description}\n" +
" <span class=\"gcli-out-shortcut gcli-help-command\">help ${command.name}</span>\n" +
" </td>\n" +
" </tr>\n" +
"</table>\n" +
"");
define("text!gcli/commands/help_man.html", [], "\n" +
"<h3>${command.name}</h3>\n" +
"\n" +
"<h4 class=\"gcli-help-header\">\n" +
" ${l10n.helpManSynopsis}:\n" +
" <span class=\"gcli-help-synopsis\" onclick=\"${onclick}\">\n" +
" <span class=\"gcli-help-command\">${command.name}</span>\n" +
" <span foreach=\"param in ${command.params}\">\n" +
" ${param.defaultValue !== undefined ? '[' + param.name + ']' : param.name}\n" +
" </span>\n" +
" </span>\n" +
"</h4>\n" +
"\n" +
"<h4 class=\"gcli-help-header\">${l10n.helpManDescription}:</h4>\n" +
"\n" +
"<p class=\"gcli-help-description\">\n" +
" ${command.manual || command.description}\n" +
"</p>\n" +
"\n" +
"<h4 class=\"gcli-help-header\">${l10n.helpManParameters}:</h4>\n" +
"\n" +
"<ul class=\"gcli-help-parameter\">\n" +
" <li if=\"${command.params.length === 0}\">${l10n.helpManNone}</li>\n" +
" <li foreach=\"param in ${command.params}\">\n" +
" <tt>${param.name}</tt> ${getTypeDescription(param)}\n" +
" <br/>\n" +
" ${param.manual || param.description}\n" +
" </li>\n" +
"</ul>\n" +
"");
/*
* 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('gcli/ui/console', ['require', 'exports', 'module' , 'gcli/ui/inputter', 'gcli/ui/arg_fetch', 'gcli/ui/menu', 'gcli/ui/focus'], function(require, exports, module) {
var Inputter = require('gcli/ui/inputter').Inputter; var Inputter = require('gcli/ui/inputter').Inputter;
var ArgFetcher = require('gcli/ui/arg_fetch').ArgFetcher; var ArgFetcher = require('gcli/ui/arg_fetch').ArgFetcher;
@ -5049,10 +5358,10 @@ var CommandMenu = require('gcli/ui/menu').CommandMenu;
var FocusManager = require('gcli/ui/focus').FocusManager; var FocusManager = require('gcli/ui/focus').FocusManager;
/** /**
* Display is responsible for generating the UI for GCLI, this implementation * Console is responsible for generating the UI for GCLI, this implementation
* is a special case for use inside Firefox * is a special case for use inside Firefox
*/ */
function Display(options) { function Console(options) {
this.hintElement = options.hintElement; this.hintElement = options.hintElement;
this.gcliTerm = options.gcliTerm; this.gcliTerm = options.gcliTerm;
this.consoleWrap = options.consoleWrap; this.consoleWrap = options.consoleWrap;
@ -5097,7 +5406,7 @@ function Display(options) {
/** /**
* Avoid memory leaks * Avoid memory leaks
*/ */
Display.prototype.destroy = function() { Console.prototype.destroy = function() {
this.chromeWindow.removeEventListener('resize', this.resizer, false); this.chromeWindow.removeEventListener('resize', this.resizer, false);
delete this.resizer; delete this.resizer;
delete this.chromeWindow; delete this.chromeWindow;
@ -5122,10 +5431,17 @@ Display.prototype.destroy = function() {
/** /**
* Called on chrome window resize, or on divider slide * Called on chrome window resize, or on divider slide
*/ */
Display.prototype.resizer = function() { Console.prototype.resizer = function() {
// Bug 705109: There are several numbers hard-coded in this function.
// This is simpler than calculating them, but error-prone when the UI setup,
// the styling or display settings change.
var parentRect = this.consoleWrap.getBoundingClientRect(); var parentRect = this.consoleWrap.getBoundingClientRect();
// Magic number: 64 is the size of the toolbar above the output area
var parentHeight = parentRect.bottom - parentRect.top - 64; var parentHeight = parentRect.bottom - parentRect.top - 64;
// Magic number: 100 is the size at which we decide the hints are too small
// to be useful, so we hide them
if (parentHeight < 100) { if (parentHeight < 100) {
this.hintElement.classList.add('gcliterm-hint-nospace'); this.hintElement.classList.add('gcliterm-hint-nospace');
} }
@ -5136,20 +5452,14 @@ Display.prototype.resizer = function() {
if (isMenuVisible) { if (isMenuVisible) {
this.menu.setMaxHeight(parentHeight); this.menu.setMaxHeight(parentHeight);
// Magic numbers. We have 2 options - lots of complex dom math to derive // Magic numbers: 19 = height of a menu item, 22 = total vertical padding
// the height of a menu item (19 pixels) and the vertical padding // of container
// (22 pixels), or we could just hard-code. The former is *slightly* more
// resilient to refactoring (but still breaks with dom structure changes),
// the latter is simpler, faster and easier.
var idealMenuHeight = (19 * this.menu.items.length) + 22; var idealMenuHeight = (19 * this.menu.items.length) + 22;
if (idealMenuHeight > parentHeight) { if (idealMenuHeight > parentHeight) {
this.hintElement.style.overflowY = 'scroll'; this.hintElement.classList.add('gcliterm-hint-scroll');
this.hintElement.style.borderBottomColor = 'threedshadow';
} }
else { else {
this.hintElement.style.overflowY = null; this.hintElement.classList.remove('gcliterm-hint-scroll');
this.hintElement.style.borderBottomColor = 'white';
} }
} }
else { else {
@ -5161,7 +5471,7 @@ Display.prototype.resizer = function() {
} }
}; };
exports.Display = Display; exports.Console = Console;
}); });
/* /*
@ -5582,8 +5892,9 @@ cliView.Inputter = Inputter;
* - document (required) DOM document to be used in creating elements * - document (required) DOM document to be used in creating elements
* - requisition (required) A GCLI Requisition object whose state is monitored * - requisition (required) A GCLI Requisition object whose state is monitored
* - completeElement (optional) An element to use * - completeElement (optional) An element to use
* - completionPrompt (optional) The prompt to show before a completion. * - completionPrompt (optional) The prompt - defaults to '\u00bb'
* Defaults to '&#x00bb;' (double greater-than, a.k.a right guillemet). * (double greater-than, a.k.a right guillemet). The prompt is used directly
* in a TextNode, so HTML entities are not allowed.
*/ */
function Completer(options) { function Completer(options) {
this.document = options.document || document; this.document = options.document || document;
@ -5606,7 +5917,7 @@ function Completer(options) {
this.completionPrompt = typeof options.completionPrompt === 'string' this.completionPrompt = typeof options.completionPrompt === 'string'
? options.completionPrompt ? options.completionPrompt
: '&#x00bb;'; : '\u00bb';
if (options.inputBackgroundElement) { if (options.inputBackgroundElement) {
this.backgroundElement = options.inputBackgroundElement; this.backgroundElement = options.inputBackgroundElement;
@ -5714,50 +6025,85 @@ Completer.prototype.update = function(input) {
var current = this.requisition.getAssignmentAt(input.cursor.start); var current = this.requisition.getAssignmentAt(input.cursor.start);
var predictions = current.getPredictions(); var predictions = current.getPredictions();
var completion = '<span class="gcli-prompt">' + this.completionPrompt + '</span> '; dom.clearElement(this.element);
// All this DOM manipulation is equivalent to the HTML below.
// It's not a template because it's very simple except appendMarkupStatus()
// which is complex due to a need to merge spans.
// Bug 707131 questions if we couldn't simplify this to use a template.
//
// <span class="gcli-prompt">${completionPrompt}</span>
// ${appendMarkupStatus()}
// ${prefix}
// <span class="gcli-in-ontab">${contents}</span>
// <span class="gcli-in-closebrace" if="${unclosedJs}">}<span>
var document = this.element.ownerDocument;
var prompt = document.createElement('span');
prompt.classList.add('gcli-prompt');
prompt.appendChild(document.createTextNode(this.completionPrompt + ' '));
this.element.appendChild(prompt);
if (input.typed.length > 0) { if (input.typed.length > 0) {
var scores = this.requisition.getInputStatusMarkup(input.cursor.start); var scores = this.requisition.getInputStatusMarkup(input.cursor.start);
completion += this.markupStatusScore(scores, input); this.appendMarkupStatus(this.element, scores, input);
} }
if (input.typed.length > 0 && predictions.length > 0) { if (input.typed.length > 0 && predictions.length > 0) {
var tab = predictions[0].name; var tab = predictions[0].name;
var existing = current.getArg().text; var existing = current.getArg().text;
if (isStrictCompletion(existing, tab) && input.cursor.start === input.typed.length) {
// Display the suffix of the prediction as the completion. var contents;
var prefix = null;
if (isStrictCompletion(existing, tab) &&
input.cursor.start === input.typed.length) {
// Display the suffix of the prediction as the completion
var numLeadingSpaces = existing.match(/^(\s*)/)[0].length; var numLeadingSpaces = existing.match(/^(\s*)/)[0].length;
var suffix = tab.slice(existing.length - numLeadingSpaces); contents = tab.slice(existing.length - numLeadingSpaces);
completion += '<span class="gcli-in-ontab">' + suffix + '</span>';
} else { } else {
// Display the '-> prediction' at the end of the completer element // Display the '-> prediction' at the end of the completer element
completion += ' &#xa0;<span class="gcli-in-ontab">&#x21E5; ' + prefix = ' \u00a0'; // aka &nbsp;
tab + '</span>'; contents = '\u21E5 ' + tab; // aka &rarr; the right arrow
} }
if (prefix != null) {
this.element.appendChild(document.createTextNode(prefix));
}
var suffix = document.createElement('span');
suffix.classList.add('gcli-in-ontab');
suffix.appendChild(document.createTextNode(contents));
this.element.appendChild(suffix);
} }
// A hack to add a grey '}' to the end of the command line when we've opened // Add a grey '}' to the end of the command line when we've opened
// with a { but haven't closed it // with a { but haven't closed it
var command = this.requisition.commandAssignment.getValue(); var command = this.requisition.commandAssignment.getValue();
if (command && command.name === '{') { var unclosedJs = command && command.name === '{' &&
if (this.requisition.getAssignment(0).getArg().suffix.indexOf('}') === -1) { this.requisition.getAssignment(0).getArg().suffix.indexOf('}') === -1;
completion += '<span class="gcli-in-closebrace">}</span>'; if (unclosedJs) {
} var close = document.createElement('span');
close.classList.add('gcli-in-closebrace');
close.appendChild(document.createTextNode('}'));
this.element.appendChild(close);
} }
dom.setInnerHtml(this.element, completion);
}; };
/** /**
* Mark-up an array of Status values with spans * Mark-up an array of Status values with spans
*/ */
Completer.prototype.markupStatusScore = function(scores, input) { Completer.prototype.appendMarkupStatus = function(element, scores, input) {
var completion = '';
if (scores.length === 0) { if (scores.length === 0) {
return completion; return;
} }
var document = element.ownerDocument;
var i = 0; var i = 0;
var lastStatus = -1; var lastStatus = -1;
var span;
var contents = '';
while (true) { while (true) {
if (lastStatus !== scores[i]) { if (lastStatus !== scores[i]) {
var state = scores[i]; var state = scores[i];
@ -5765,25 +6111,27 @@ Completer.prototype.markupStatusScore = function(scores, input) {
console.error('No state at i=' + i + '. scores.len=' + scores.length); console.error('No state at i=' + i + '. scores.len=' + scores.length);
state = Status.VALID; state = Status.VALID;
} }
completion += '<span class="gcli-in-' + state.toString().toLowerCase() + '">'; span = document.createElement('span');
span.classList.add('gcli-in-' + state.toString().toLowerCase());
lastStatus = scores[i]; lastStatus = scores[i];
} }
var char = input.typed[i]; var char = input.typed[i];
if (char === ' ') { if (char === ' ') {
char = '&#xa0;'; char = '\u00a0';
} }
completion += char; contents += char;
i++; i++;
if (i === input.typed.length) { if (i === input.typed.length) {
completion += '</span>'; span.appendChild(document.createTextNode(contents));
this.element.appendChild(span);
break; break;
} }
if (lastStatus !== scores[i]) { if (lastStatus !== scores[i]) {
completion += '</span>'; span.appendChild(document.createTextNode(contents));
this.element.appendChild(span);
contents = '';
} }
} }
return completion;
}; };
cliView.Completer = Completer; cliView.Completer = Completer;
@ -5867,7 +6215,7 @@ var dom = require('gcli/util').dom;
var Status = require('gcli/types').Status; var Status = require('gcli/types').Status;
var getField = require('gcli/ui/field').getField; var getField = require('gcli/ui/field').getField;
var Templater = require('gcli/ui/domtemplate').Templater; var domtemplate = require('gcli/ui/domtemplate');
var editorCss = require('text!gcli/ui/arg_fetch.css'); var editorCss = require('text!gcli/ui/arg_fetch.css');
var argFetchHtml = require('text!gcli/ui/arg_fetch.html'); var argFetchHtml = require('text!gcli/ui/arg_fetch.html');
@ -5896,7 +6244,6 @@ function ArgFetcher(options) {
// We cache the fields we create so we can destroy them later // We cache the fields we create so we can destroy them later
this.fields = []; this.fields = [];
this.tmpl = new Templater();
// Populated by template // Populated by template
this.okElement = null; this.okElement = null;
@ -5953,7 +6300,8 @@ ArgFetcher.prototype.onCommandChange = function(ev) {
this.fields = []; this.fields = [];
var reqEle = this.reqTempl.cloneNode(true); var reqEle = this.reqTempl.cloneNode(true);
this.tmpl.processNode(reqEle, this); domtemplate.template(reqEle, this,
{ allowEval: true, stack: 'arg_fetch.html' });
dom.clearElement(this.element); dom.clearElement(this.element);
this.element.appendChild(reqEle); this.element.appendChild(reqEle);
@ -6008,7 +6356,7 @@ ArgFetcher.prototype.getInputFor = function(assignment) {
return newField.element; return newField.element;
} }
catch (ex) { catch (ex) {
// This is called from within Templater which can make tracing errors hard // This is called from within template() which can make tracing errors hard
// so we log here if anything goes wrong // so we log here if anything goes wrong
console.error(ex); console.error(ex);
return ''; return '';
@ -6252,7 +6600,7 @@ function StringField(type, options) {
this.element = dom.createElement(this.document, 'input'); this.element = dom.createElement(this.document, 'input');
this.element.type = 'text'; this.element.type = 'text';
this.element.className = 'gcli-field'; this.element.classList.add('gcli-field');
this.onInputChange = this.onInputChange.bind(this); this.onInputChange = this.onInputChange.bind(this);
this.element.addEventListener('keyup', this.onInputChange, false); this.element.addEventListener('keyup', this.onInputChange, false);
@ -6412,7 +6760,7 @@ function SelectionField(type, options) {
this.items = []; this.items = [];
this.element = dom.createElement(this.document, 'select'); this.element = dom.createElement(this.document, 'select');
this.element.className = 'gcli-field'; this.element.classList.add('gcli-field');
this._addOption({ this._addOption({
name: l10n.lookupFormat('fieldSelectionSelect', [ options.name ]) name: l10n.lookupFormat('fieldSelectionSelect', [ options.name ])
}); });
@ -6487,8 +6835,8 @@ function JavascriptField(type, options) {
this.input = dom.createElement(this.document, 'input'); this.input = dom.createElement(this.document, 'input');
this.input.type = 'text'; this.input.type = 'text';
this.input.addEventListener('keyup', this.onInputChange, false); this.input.addEventListener('keyup', this.onInputChange, false);
this.input.style.marginBottom = '0'; this.input.classList.add('gcli-field');
this.input.className = 'gcli-field'; this.input.classList.add('gcli-field-javascript');
this.element.appendChild(this.input); this.element.appendChild(this.input);
this.menu = new Menu({ document: this.document, field: true }); this.menu = new Menu({ document: this.document, field: true });
@ -6680,18 +7028,18 @@ function ArrayField(type, options) {
// <div class=gcliArrayParent save="${element}"> // <div class=gcliArrayParent save="${element}">
this.element = dom.createElement(this.document, 'div'); this.element = dom.createElement(this.document, 'div');
this.element.className = 'gcliArrayParent'; this.element.classList.add('gcli-array-parent');
// <button class=gcliArrayMbrAdd onclick="${_onAdd}" save="${addButton}">Add // <button class=gcliArrayMbrAdd onclick="${_onAdd}" save="${addButton}">Add
this.addButton = dom.createElement(this.document, 'button'); this.addButton = dom.createElement(this.document, 'button');
this.addButton.className = 'gcliArrayMbrAdd'; this.addButton.classList.add('gcli-array-member-add');
this.addButton.addEventListener('click', this._onAdd, false); this.addButton.addEventListener('click', this._onAdd, false);
this.addButton.innerHTML = l10n.lookup('fieldArrayAdd'); this.addButton.innerHTML = l10n.lookup('fieldArrayAdd');
this.element.appendChild(this.addButton); this.element.appendChild(this.addButton);
// <div class=gcliArrayMbrs save="${mbrElement}"> // <div class=gcliArrayMbrs save="${mbrElement}">
this.container = dom.createElement(this.document, 'div'); this.container = dom.createElement(this.document, 'div');
this.container.className = 'gcliArrayMbrs'; this.container.classList.add('gcli-array-members');
this.element.appendChild(this.container); this.element.appendChild(this.container);
this.onInputChange = this.onInputChange.bind(this); this.onInputChange = this.onInputChange.bind(this);
@ -6734,7 +7082,7 @@ ArrayField.prototype.getConversion = function() {
ArrayField.prototype._onAdd = function(ev, subConversion) { ArrayField.prototype._onAdd = function(ev, subConversion) {
// <div class=gcliArrayMbr save="${element}"> // <div class=gcliArrayMbr save="${element}">
var element = dom.createElement(this.document, 'div'); var element = dom.createElement(this.document, 'div');
element.className = 'gcliArrayMbr'; element.classList.add('gcli-array-member');
this.container.appendChild(element); this.container.appendChild(element);
// ${field.element} // ${field.element}
@ -6752,7 +7100,7 @@ ArrayField.prototype._onAdd = function(ev, subConversion) {
// <div class=gcliArrayMbrDel onclick="${_onDel}"> // <div class=gcliArrayMbrDel onclick="${_onDel}">
var delButton = dom.createElement(this.document, 'button'); var delButton = dom.createElement(this.document, 'button');
delButton.className = 'gcliArrayMbrDel'; delButton.classList.add('gcli-array-member-del');
delButton.addEventListener('click', this._onDel, false); delButton.addEventListener('click', this._onDel, false);
delButton.innerHTML = l10n.lookup('fieldArrayDel'); delButton.innerHTML = l10n.lookup('fieldArrayDel');
element.appendChild(delButton); element.appendChild(delButton);
@ -6794,7 +7142,7 @@ var Conversion = require('gcli/types').Conversion;
var Argument = require('gcli/argument').Argument; var Argument = require('gcli/argument').Argument;
var canon = require('gcli/canon'); var canon = require('gcli/canon');
var Templater = require('gcli/ui/domtemplate').Templater; var domtemplate = require('gcli/ui/domtemplate');
var menuCss = require('text!gcli/ui/menu.css'); var menuCss = require('text!gcli/ui/menu.css');
var menuHtml = require('text!gcli/ui/menu.html'); var menuHtml = require('text!gcli/ui/menu.html');
@ -6877,7 +7225,7 @@ Menu.prototype.show = function(items, error) {
} }
var options = this.optTempl.cloneNode(true); var options = this.optTempl.cloneNode(true);
new Templater().processNode(options, this); domtemplate.template(options, this, { allowEval: true, stack: 'menu.html' });
dom.clearElement(this.element); dom.clearElement(this.element);
this.element.appendChild(options); this.element.appendChild(options);
@ -6984,18 +7332,6 @@ CommandMenu.prototype.onCommandChange = function(ev) {
exports.CommandMenu = CommandMenu; exports.CommandMenu = CommandMenu;
});
/*
* 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('gcli/ui/domtemplate', ['require', 'exports', 'module' ], function(require, exports, module) {
Components.utils.import("resource:///modules/devtools/Templater.jsm");
exports.Templater = Templater;
}); });
define("text!gcli/ui/menu.css", [], void 0); define("text!gcli/ui/menu.css", [], void 0);
define("text!gcli/ui/menu.html", [], "\n" + define("text!gcli/ui/menu.html", [], "\n" +

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

@ -83,12 +83,12 @@ function testCallCommands() {
is(gcliterm.completeNode.textContent, " ecd", "Completion for \"ecd\""); is(gcliterm.completeNode.textContent, " ecd", "Completion for \"ecd\"");
// Test a normal command's life cycle // Test a normal command's life cycle
gcliterm.opts.display.inputter.setInput("echo hello world"); gcliterm.opts.console.inputter.setInput("echo hello world");
gcliterm.opts.requisition.exec(); gcliterm.opts.requisition.exec();
let nodes = hud.outputNode.querySelectorAll("description"); let nodes = hud.outputNode.querySelectorAll(".gcliterm-msg-body");
is(nodes.length, 2, "Right number of output nodes"); is(nodes.length, 1, "Right number of output nodes");
ok(/hello world/.test(nodes[0].textContent), "the command's output is correct."); ok(/hello world/.test(nodes[0].textContent), "the command's output is correct.");
gcliterm.clearOutput(); gcliterm.clearOutput();

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

@ -54,7 +54,7 @@ var Node = Components.interfaces.nsIDOMNode;
* http://opensource.org/licenses/BSD-3-Clause * http://opensource.org/licenses/BSD-3-Clause
*/ */
define('gclitest/suite', ['require', 'exports', 'module' , 'gcli/index', 'test/examiner', 'gclitest/testTokenize', 'gclitest/testSplit', 'gclitest/testCli', 'gclitest/testHistory', 'gclitest/testRequire', 'gclitest/testJs'], function(require, exports, module) { define('gclitest/suite', ['require', 'exports', 'module' , 'gcli/index', 'test/examiner', 'gclitest/testTokenize', 'gclitest/testSplit', 'gclitest/testCli', 'gclitest/testExec', 'gclitest/testKeyboard', 'gclitest/testHistory', 'gclitest/testRequire', 'gclitest/testJs'], function(require, exports, module) {
// We need to make sure GCLI is initialized before we begin testing it // We need to make sure GCLI is initialized before we begin testing it
require('gcli/index'); require('gcli/index');
@ -67,11 +67,15 @@ define('gclitest/suite', ['require', 'exports', 'module' , 'gcli/index', 'test/e
examiner.addSuite('gclitest/testTokenize', require('gclitest/testTokenize')); examiner.addSuite('gclitest/testTokenize', require('gclitest/testTokenize'));
examiner.addSuite('gclitest/testSplit', require('gclitest/testSplit')); examiner.addSuite('gclitest/testSplit', require('gclitest/testSplit'));
examiner.addSuite('gclitest/testCli', require('gclitest/testCli')); examiner.addSuite('gclitest/testCli', require('gclitest/testCli'));
examiner.addSuite('gclitest/testExec', require('gclitest/testExec'));
examiner.addSuite('gclitest/testKeyboard', require('gclitest/testKeyboard'));
examiner.addSuite('gclitest/testHistory', require('gclitest/testHistory')); examiner.addSuite('gclitest/testHistory', require('gclitest/testHistory'));
examiner.addSuite('gclitest/testRequire', require('gclitest/testRequire')); examiner.addSuite('gclitest/testRequire', require('gclitest/testRequire'));
examiner.addSuite('gclitest/testJs', require('gclitest/testJs')); examiner.addSuite('gclitest/testJs', require('gclitest/testJs'));
examiner.run(); examiner.run();
console.log('Completed test suite');
// examiner.log();
}); });
/* /*
@ -167,6 +171,19 @@ examiner.toRemote = function() {
}; };
}; };
/**
* Output a test summary to console.log
*/
examiner.log = function() {
var remote = this.toRemote();
remote.suites.forEach(function(suite) {
console.log(suite.name);
suite.tests.forEach(function(test) {
console.log('- ' + test.name, test.status.name, test.message || '');
});
});
};
/** /**
* Used by assert to record a failure against the current test * Used by assert to record a failure against the current test
*/ */
@ -299,8 +316,8 @@ Test.prototype.run = function() {
this.status = stati.fail; this.status = stati.fail;
this.messages.push('' + ex); this.messages.push('' + ex);
console.error(ex); console.error(ex);
if (console.trace) { if (ex.stack) {
console.trace(); console.error(ex.stack);
} }
} }
@ -703,11 +720,12 @@ exports.testJavascript = function() {
* http://opensource.org/licenses/BSD-3-Clause * http://opensource.org/licenses/BSD-3-Clause
*/ */
define('gclitest/commands', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types/basic', 'gcli/types'], function(require, exports, module) { define('gclitest/commands', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/util', 'gcli/types/basic', 'gcli/types'], function(require, exports, module) {
var commands = exports; var commands = exports;
var canon = require('gcli/canon'); var canon = require('gcli/canon');
var util = require('gcli/util');
var SelectionType = require('gcli/types/basic').SelectionType; var SelectionType = require('gcli/types/basic').SelectionType;
var DeferredType = require('gcli/types/basic').DeferredType; var DeferredType = require('gcli/types/basic').DeferredType;
@ -725,6 +743,10 @@ commands.setup = function() {
canon.addCommand(commands.tsv); canon.addCommand(commands.tsv);
canon.addCommand(commands.tsr); canon.addCommand(commands.tsr);
canon.addCommand(commands.tse);
canon.addCommand(commands.tsj);
canon.addCommand(commands.tsb);
canon.addCommand(commands.tss);
canon.addCommand(commands.tsu); canon.addCommand(commands.tsu);
canon.addCommand(commands.tsn); canon.addCommand(commands.tsn);
canon.addCommand(commands.tsnDif); canon.addCommand(commands.tsnDif);
@ -734,11 +756,16 @@ commands.setup = function() {
canon.addCommand(commands.tsnExtend); canon.addCommand(commands.tsnExtend);
canon.addCommand(commands.tselarr); canon.addCommand(commands.tselarr);
canon.addCommand(commands.tsm); canon.addCommand(commands.tsm);
canon.addCommand(commands.tsg);
}; };
commands.shutdown = function() { commands.shutdown = function() {
canon.removeCommand(commands.tsv); canon.removeCommand(commands.tsv);
canon.removeCommand(commands.tsr); canon.removeCommand(commands.tsr);
canon.removeCommand(commands.tse);
canon.removeCommand(commands.tsj);
canon.removeCommand(commands.tsb);
canon.removeCommand(commands.tss);
canon.removeCommand(commands.tsu); canon.removeCommand(commands.tsu);
canon.removeCommand(commands.tsn); canon.removeCommand(commands.tsn);
canon.removeCommand(commands.tsnDif); canon.removeCommand(commands.tsnDif);
@ -748,14 +775,15 @@ commands.shutdown = function() {
canon.removeCommand(commands.tsnExtend); canon.removeCommand(commands.tsnExtend);
canon.removeCommand(commands.tselarr); canon.removeCommand(commands.tselarr);
canon.removeCommand(commands.tsm); canon.removeCommand(commands.tsm);
canon.removeCommand(commands.tsg);
types.deregisterType(commands.optionType); types.deregisterType(commands.optionType);
types.deregisterType(commands.optionValue); types.deregisterType(commands.optionValue);
}; };
commands.option1 = { }; commands.option1 = { type: types.getType('string') };
commands.option2 = { }; commands.option2 = { type: types.getType('number') };
commands.optionType = new SelectionType({ commands.optionType = new SelectionType({
name: 'optionType', name: 'optionType',
@ -789,25 +817,62 @@ commands.optionValue = new DeferredType({
} }
}); });
commands.commandExec = util.createEvent('commands.commandExec');
function createExec(name) {
return function(args, context) {
var data = {
command: commands[name],
args: args,
context: context
};
commands.commandExec(data);
return data;
};
}
commands.tsv = { commands.tsv = {
name: 'tsv', name: 'tsv',
params: [ params: [
{ name: 'optionType', type: 'optionType' }, { name: 'optionType', type: 'optionType' },
{ name: 'optionValue', type: 'optionValue' } { name: 'optionValue', type: 'optionValue' }
], ],
exec: function(args, context) { } exec: createExec('tsv')
}; };
commands.tsr = { commands.tsr = {
name: 'tsr', name: 'tsr',
params: [ { name: 'text', type: 'string' } ], params: [ { name: 'text', type: 'string' } ],
exec: function(args, context) { } exec: createExec('tsr')
};
commands.tse = {
name: 'tse',
params: [ { name: 'node', type: 'node' } ],
exec: createExec('tse')
};
commands.tsj = {
name: 'tsj',
params: [ { name: 'javascript', type: 'javascript' } ],
exec: createExec('tsj')
};
commands.tsb = {
name: 'tsb',
params: [ { name: 'toggle', type: 'boolean' } ],
exec: createExec('tsb')
};
commands.tss = {
name: 'tss',
exec: createExec('tss')
}; };
commands.tsu = { commands.tsu = {
name: 'tsu', name: 'tsu',
params: [ { name: 'num', type: 'number' } ], params: [ { name: 'num', type: { name: 'number', max: 10, min: -5, step: 3 } } ],
exec: function(args, context) { } exec: createExec('tsu')
}; };
commands.tsn = { commands.tsn = {
@ -817,31 +882,31 @@ commands.tsn = {
commands.tsnDif = { commands.tsnDif = {
name: 'tsn dif', name: 'tsn dif',
params: [ { name: 'text', type: 'string' } ], params: [ { name: 'text', type: 'string' } ],
exec: function(text) { } exec: createExec('tsnDif')
}; };
commands.tsnExt = { commands.tsnExt = {
name: 'tsn ext', name: 'tsn ext',
params: [ { name: 'text', type: 'string' } ], params: [ { name: 'text', type: 'string' } ],
exec: function(text) { } exec: createExec('tsnExt')
}; };
commands.tsnExte = { commands.tsnExte = {
name: 'tsn exte', name: 'tsn exte',
params: [ { name: 'text', type: 'string' } ], params: [ { name: 'text', type: 'string' } ],
exec: function(text) { } exec: createExec('')
}; };
commands.tsnExten = { commands.tsnExten = {
name: 'tsn exten', name: 'tsn exten',
params: [ { name: 'text', type: 'string' } ], params: [ { name: 'text', type: 'string' } ],
exec: function(text) { } exec: createExec('tsnExte')
}; };
commands.tsnExtend = { commands.tsnExtend = {
name: 'tsn extend', name: 'tsn extend',
params: [ { name: 'text', type: 'string' } ], params: [ { name: 'text', type: 'string' } ],
exec: function(text) { } exec: createExec('tsnExtend')
}; };
commands.tselarr = { commands.tselarr = {
@ -850,7 +915,7 @@ commands.tselarr = {
{ name: 'num', type: { name: 'selection', data: [ '1', '2', '3' ] } }, { name: 'num', type: { name: 'selection', data: [ '1', '2', '3' ] } },
{ name: 'arr', type: { name: 'array', subtype: 'string' } }, { name: 'arr', type: { name: 'array', subtype: 'string' } },
], ],
exec: function(args, context) {} exec: createExec('tselarr')
}; };
commands.tsm = { commands.tsm = {
@ -862,7 +927,31 @@ commands.tsm = {
{ name: 'txt', type: 'string' }, { name: 'txt', type: 'string' },
{ name: 'num', type: { name: 'number', max: 42, min: 0 } }, { name: 'num', type: { name: 'number', max: 42, min: 0 } },
], ],
exec: function(args, context) {} exec: createExec('tsm')
};
commands.tsg = {
name: 'tsg',
hidden: true,
description: 'a param group test',
params: [
{ name: 'solo', type: { name: 'selection', data: [ 'aaa', 'bbb', 'ccc' ] } },
{
group: 'First',
params: [
{ name: 'txt1', type: 'string', defaultValue: null },
{ name: 'boolean1', type: 'boolean' }
]
},
{
group: 'Second',
params: [
{ name: 'txt2', type: 'string', defaultValue: 'd' },
{ name: 'num2', type: { name: 'number', defaultValue: 42 } }
]
}
],
exec: createExec('tsg')
}; };
@ -1211,6 +1300,278 @@ exports.testNestedCommand = function() {
}; };
});
/*
* 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('gclitest/testExec', ['require', 'exports', 'module' , 'gcli/cli', 'gcli/types', 'gcli/canon', 'gclitest/commands', 'gcli/types/node', 'test/assert'], function(require, exports, module) {
var Requisition = require('gcli/cli').Requisition;
var Status = require('gcli/types').Status;
var canon = require('gcli/canon');
var commands = require('gclitest/commands');
var nodetype = require('gcli/types/node');
var test = require('test/assert');
var actualExec;
var actualOutput;
exports.setup = function() {
commands.setup();
commands.commandExec.add(onCommandExec);
canon.commandOutputManager.addListener(onCommandOutput);
};
exports.shutdown = function() {
commands.shutdown();
commands.commandExec.remove(onCommandExec);
canon.commandOutputManager.removeListener(onCommandOutput);
};
function onCommandExec(ev) {
actualExec = ev;
}
function onCommandOutput(ev) {
actualOutput = ev.output;
}
function exec(command, expectedArgs) {
var environment = {};
var requisition = new Requisition(environment);
var reply = requisition.exec({ typed: command });
test.is(command.indexOf(actualExec.command.name), 0, 'Command name: ' + command);
if (reply !== true) {
test.ok(false, 'reply = false for command: ' + command);
}
if (expectedArgs == null) {
test.ok(false, 'expectedArgs == null for ' + command);
return;
}
if (actualExec.args == null) {
test.ok(false, 'actualExec.args == null for ' + command);
return;
}
test.is(Object.keys(expectedArgs).length, Object.keys(actualExec.args).length,
'Arg count: ' + command);
Object.keys(expectedArgs).forEach(function(arg) {
var expectedArg = expectedArgs[arg];
var actualArg = actualExec.args[arg];
if (Array.isArray(expectedArg)) {
if (!Array.isArray(actualArg)) {
test.ok(false, 'actual is not an array. ' + command + '/' + arg);
return;
}
test.is(expectedArg.length, actualArg.length,
'Array length: ' + command + '/' + arg);
for (var i = 0; i < expectedArg.length; i++) {
test.is(expectedArg[i], actualArg[i],
'Member: "' + command + '/' + arg + '/' + i);
}
}
else {
test.is(expectedArg, actualArg, 'Command: "' + command + '" arg: ' + arg);
}
});
test.is(environment, actualExec.context.environment, 'Environment');
test.is(false, actualOutput.error, 'output error is false');
test.is(command, actualOutput.typed, 'command is typed');
test.ok(typeof actualOutput.canonical === 'string', 'canonical exists');
test.is(actualExec.args, actualOutput.args, 'actualExec.args is actualOutput.args');
}
exports.testExec = function() {
exec('tss', {});
// Bug 707008 - GCLI defered types don't work properly
// exec('tsv option1 10', { optionType: commands.option1, optionValue: '10' });
// exec('tsv option2 10', { optionType: commands.option1, optionValue: 10 });
exec('tsr fred', { text: 'fred' });
exec('tsr fred bloggs', { text: 'fred bloggs' });
exec('tsr "fred bloggs"', { text: 'fred bloggs' });
exec('tsb', { toggle: false });
exec('tsb --toggle', { toggle: true });
exec('tsu 10', { num: 10 });
exec('tsu --num 10', { num: 10 });
// Bug 704829 - Enable GCLI Javascript parameters
// The answer to this should be 2
exec('tsj { 1 + 1 }', { javascript: '1 + 1' });
var origDoc = nodetype.getDocument();
nodetype.setDocument(mockDoc);
exec('tse :root', { node: mockBody });
nodetype.setDocument(origDoc);
exec('tsn dif fred', { text: 'fred' });
exec('tsn exten fred', { text: 'fred' });
exec('tsn extend fred', { text: 'fred' });
exec('tselarr 1', { num: '1', arr: [ ] });
exec('tselarr 1 a', { num: '1', arr: [ 'a' ] });
exec('tselarr 1 a b', { num: '1', arr: [ 'a', 'b' ] });
exec('tsm a 10 10', { abc: 'a', txt: '10', num: 10 });
// Bug 707009 - GCLI doesn't always fill in default parameters properly
// exec('tsg a', { solo: 'a', txt1: null, boolean1: false, txt2: 'd', num2: 42 });
};
var mockBody = {
style: {}
};
var mockDoc = {
querySelectorAll: function(css) {
if (css === ':root') {
return {
length: 1,
item: function(i) {
return mockBody;
}
};
}
throw new Error('mockDoc.querySelectorAll(\'' + css + '\') error');
}
};
});
/*
* 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('gclitest/testKeyboard', ['require', 'exports', 'module' , 'gcli/cli', 'gcli/types', 'gcli/canon', 'gclitest/commands', 'gcli/types/node', 'test/assert'], function(require, exports, module) {
var Requisition = require('gcli/cli').Requisition;
var Status = require('gcli/types').Status;
var canon = require('gcli/canon');
var commands = require('gclitest/commands');
var nodetype = require('gcli/types/node');
var test = require('test/assert');
exports.setup = function() {
commands.setup();
};
exports.shutdown = function() {
commands.shutdown();
};
var COMPLETES_TO = 'complete';
var KEY_UPS_TO = 'keyup';
var KEY_DOWNS_TO = 'keydown';
function check(initial, action, after) {
var requisition = new Requisition();
requisition.update({
typed: initial,
cursor: { start: initial.length, end: initial.length }
});
var assignment = requisition.getAssignmentAt(initial.length);
switch (action) {
case COMPLETES_TO:
assignment.complete();
break;
case KEY_UPS_TO:
assignment.increment();
break;
case KEY_DOWNS_TO:
assignment.decrement();
break;
}
test.is(after, requisition.toString(), initial + ' + ' + action + ' -> ' + after);
}
exports.testComplete = function() {
check('tsela', COMPLETES_TO, 'tselarr ');
check('tsn di', COMPLETES_TO, 'tsn dif ');
check('tsg a', COMPLETES_TO, 'tsg aaa ');
check('{ wind', COMPLETES_TO, '{ window');
check('{ window.docum', COMPLETES_TO, '{ window.document');
check('{ window.document.titl', COMPLETES_TO, '{ window.document.title ');
};
exports.testIncrDecr = function() {
check('tsu -70', KEY_UPS_TO, 'tsu -5');
check('tsu -7', KEY_UPS_TO, 'tsu -5');
check('tsu -6', KEY_UPS_TO, 'tsu -5');
check('tsu -5', KEY_UPS_TO, 'tsu -3');
check('tsu -4', KEY_UPS_TO, 'tsu -3');
check('tsu -3', KEY_UPS_TO, 'tsu 0');
check('tsu -2', KEY_UPS_TO, 'tsu 0');
check('tsu -1', KEY_UPS_TO, 'tsu 0');
check('tsu 0', KEY_UPS_TO, 'tsu 3');
check('tsu 1', KEY_UPS_TO, 'tsu 3');
check('tsu 2', KEY_UPS_TO, 'tsu 3');
check('tsu 3', KEY_UPS_TO, 'tsu 6');
check('tsu 4', KEY_UPS_TO, 'tsu 6');
check('tsu 5', KEY_UPS_TO, 'tsu 6');
check('tsu 6', KEY_UPS_TO, 'tsu 9');
check('tsu 7', KEY_UPS_TO, 'tsu 9');
check('tsu 8', KEY_UPS_TO, 'tsu 9');
check('tsu 9', KEY_UPS_TO, 'tsu 10');
check('tsu 10', KEY_UPS_TO, 'tsu 10');
check('tsu 100', KEY_UPS_TO, 'tsu -5');
check('tsu -70', KEY_DOWNS_TO, 'tsu 10');
check('tsu -7', KEY_DOWNS_TO, 'tsu 10');
check('tsu -6', KEY_DOWNS_TO, 'tsu 10');
check('tsu -5', KEY_DOWNS_TO, 'tsu -5');
check('tsu -4', KEY_DOWNS_TO, 'tsu -5');
check('tsu -3', KEY_DOWNS_TO, 'tsu -5');
check('tsu -2', KEY_DOWNS_TO, 'tsu -3');
check('tsu -1', KEY_DOWNS_TO, 'tsu -3');
check('tsu 0', KEY_DOWNS_TO, 'tsu -3');
check('tsu 1', KEY_DOWNS_TO, 'tsu 0');
check('tsu 2', KEY_DOWNS_TO, 'tsu 0');
check('tsu 3', KEY_DOWNS_TO, 'tsu 0');
check('tsu 4', KEY_DOWNS_TO, 'tsu 3');
check('tsu 5', KEY_DOWNS_TO, 'tsu 3');
check('tsu 6', KEY_DOWNS_TO, 'tsu 3');
check('tsu 7', KEY_DOWNS_TO, 'tsu 6');
check('tsu 8', KEY_DOWNS_TO, 'tsu 6');
check('tsu 9', KEY_DOWNS_TO, 'tsu 6');
check('tsu 10', KEY_DOWNS_TO, 'tsu 9');
check('tsu 100', KEY_DOWNS_TO, 'tsu 10');
// Bug 707007 - GCLI increment and decrement operations cycle through
// selection options in the wrong order
check('tselarr 1', KEY_DOWNS_TO, 'tselarr 2');
check('tselarr 2', KEY_DOWNS_TO, 'tselarr 3');
check('tselarr 3', KEY_DOWNS_TO, 'tselarr 1');
check('tselarr 3', KEY_UPS_TO, 'tselarr 2');
};
}); });
/* /*
* Copyright 2009-2011 Mozilla Foundation and contributors * Copyright 2009-2011 Mozilla Foundation and contributors
@ -1544,6 +1905,8 @@ function undefine() {
delete define.modules['gclitest/testSplit']; delete define.modules['gclitest/testSplit'];
delete define.modules['gclitest/commands']; delete define.modules['gclitest/commands'];
delete define.modules['gclitest/testCli']; delete define.modules['gclitest/testCli'];
delete define.modules['gclitest/testExec'];
delete define.modules['gclitest/testKeyboard'];
delete define.modules['gclitest/testHistory']; delete define.modules['gclitest/testHistory'];
delete define.modules['gclitest/testRequire']; delete define.modules['gclitest/testRequire'];
delete define.modules['gclitest/requirable']; delete define.modules['gclitest/requirable'];
@ -1556,6 +1919,8 @@ function undefine() {
delete define.globalDomain.modules['gclitest/testSplit']; delete define.globalDomain.modules['gclitest/testSplit'];
delete define.globalDomain.modules['gclitest/commands']; delete define.globalDomain.modules['gclitest/commands'];
delete define.globalDomain.modules['gclitest/testCli']; delete define.globalDomain.modules['gclitest/testCli'];
delete define.globalDomain.modules['gclitest/testExec'];
delete define.globalDomain.modules['gclitest/testKeyboard'];
delete define.globalDomain.modules['gclitest/testHistory']; delete define.globalDomain.modules['gclitest/testHistory'];
delete define.globalDomain.modules['gclitest/testRequire']; delete define.globalDomain.modules['gclitest/testRequire'];
delete define.globalDomain.modules['gclitest/requirable']; delete define.globalDomain.modules['gclitest/requirable'];

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

@ -67,12 +67,12 @@ typesNumberNan=Can't convert "%S" to a number.
# LOCALIZATION NOTE (typesNumberMax): When the command line is passed a # LOCALIZATION NOTE (typesNumberMax): When the command line is passed a
# number, but the number is bigger than the largest allowed number, this error # number, but the number is bigger than the largest allowed number, this error
# message is displayed. # message is displayed.
typesNumberMax=%1$S is greater that maximum allowed: %2$S. typesNumberMax=%1$S is greater than maximum allowed: %2$S.
# LOCALIZATION NOTE (typesNumberMin): When the command line is passed a # LOCALIZATION NOTE (typesNumberMin): When the command line is passed a
# number, but the number is lower than the smallest allowed number, this error # number, but the number is lower than the smallest allowed number, this error
# message is displayed. # message is displayed.
typesNumberMin=%1$S is smaller that minimum allowed: %2$S. typesNumberMin=%1$S is smaller than minimum allowed: %2$S.
# LOCALIZATION NOTE (typesSelectionNomatch): When the command line is passed # LOCALIZATION NOTE (typesSelectionNomatch): When the command line is passed
# an option with a limited number of correct values, but the passed value is # an option with a limited number of correct values, but the passed value is
@ -94,3 +94,50 @@ nodeParseMultiple=Too many matches (%S)
# displayed. # displayed.
nodeParseNone=No matches nodeParseNone=No matches
# LOCALIZATION NOTE (helpDesc): A very short description of the 'help'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible. See helpManual for a
# fuller description of what it does.
helpDesc=Get help on the available commands
# LOCALIZATION NOTE (helpManual): A fuller description of the 'help' command.
# Displayed when the user asks for help on what it does.
helpManual=Provide help either on a specific command (if a search string is provided and an exact match is found) or on the available commands (if a search string is not provided, or if no exact match is found).
# LOCALIZATION NOTE (helpSearchDesc): A very short description of the 'search'
# parameter to the 'help' command. See helpSearchManual for a fuller
# description of what it does. This string is designed to be shown in a dialog
# with restricted space, which is why it should be as short as possible.
helpSearchDesc=Search string
# LOCALIZATION NOTE (helpSearchManual): A fuller description of the 'search'
# parameter to the 'help' command. Displayed when the user asks for help on
# what it does.
helpSearchManual=A search string to use in narrowing down the list of commands that are displayed to the user. Any part of the string can match, regular expressions are not supported.
# LOCALIZATION NOTE (helpManSynopsis): A heading shown at the top of a help
# page for a command in the console It labels a summary of the parameters to
# the command
helpManSynopsis=Synopsis
# LOCALIZATION NOTE (helpManDescription): A heading shown in a help page for a
# command in the console. This heading precedes the top level description.
helpManDescription=Description
# LOCALIZATION NOTE (helpManParameters): A heading shown above the parameters
# in a help page for a command in the console.
helpManParameters=Parameters
# LOCALIZATION NOTE (helpManNone): Some text shown under the parameters
# heading in a help page for a command which has no parameters.
helpManNone=None
# LOCALIZATION NOTE (introHeader): The heading displayed at the top of the
# output for the help command
introHeader=Welcome to Firefox Developer Tools
# LOCALIZATION NOTE (introBody): The text displayed at the top of the output
# for the help command, just before the list of commands. This text is wrapped
# inside a link to a localized MDN article
introBody=For more information see MDN.

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

@ -44,8 +44,7 @@ inspectDesc=Inspect a node
# LOCALIZATION NOTE (inspectManual) A fuller description of the 'inspect' # LOCALIZATION NOTE (inspectManual) A fuller description of the 'inspect'
# command, displayed when the user asks for help on what it does. # command, displayed when the user asks for help on what it does.
inspectManual=Investigate the dimensions and properties of an element using \ inspectManual=Investigate the dimensions and properties of an element using a CSS selector to open the DOM highlighter
a CSS selector to open the DOM highlighter
# LOCALIZATION NOTE (inspectNodeDesc) A very short string to describe the # LOCALIZATION NOTE (inspectNodeDesc) A very short string to describe the
# 'node' parameter to the 'inspect' command, which is displayed in a dialog # 'node' parameter to the 'inspect' command, which is displayed in a dialog
@ -55,5 +54,4 @@ inspectNodeDesc=CSS selector
# LOCALIZATION NOTE (inspectNodeManual) A fuller description of the 'node' # LOCALIZATION NOTE (inspectNodeManual) A fuller description of the 'node'
# parameter to the 'inspect' command, displayed when the user asks for help # parameter to the 'inspect' command, displayed when the user asks for help
# on what it does. # on what it does.
inspectNodeManual=A CSS selector for use with Document.querySelector which \ inspectNodeManual=A CSS selector for use with Document.querySelector which identifies a single element
identifies a single element

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

@ -46,7 +46,6 @@
height: 100%; height: 100%;
vertical-align: middle; vertical-align: middle;
background-color: transparent; background-color: transparent;
font: 12px Consolas, "Lucida Console", monospace;
} }
.gcliterm-input-node { .gcliterm-input-node {
@ -64,24 +63,6 @@
-moz-padding-end: 4px; -moz-padding-end: 4px;
} }
.gcli-in-valid {
border-bottom: none;
}
.gcli-in-incomplete {
color: #DDD;
border-bottom: 1px dotted #999;
}
.gcli-in-error {
color: #DDD;
border-bottom: 1px dotted #F00;
}
.gcli-in-ontab {
color: #999;
}
.gcliterm-stack-node { .gcliterm-stack-node {
background: url("chrome://global/skin/icons/commandline.png") 4px center no-repeat; background: url("chrome://global/skin/icons/commandline.png") 4px center no-repeat;
width: 100%; width: 100%;
@ -109,19 +90,47 @@
border-bottom: 1px solid threedshadow; border-bottom: 1px solid threedshadow;
} }
.gcli-help-right {
text-align: right;
}
.gcliterm-menu { .gcliterm-menu {
display: -moz-box; display: -moz-box;
-moz-box-flex: 1; -moz-box-flex: 1;
border-bottom-color: white;
}
.gcliterm-hint-scroll {
overflow-y: scroll;
border-bottom-color: threedshadow;
} }
.gcliterm-hint-nospace { .gcliterm-hint-nospace {
display: none; display: none;
} }
.gcliterm-msg-body {
margin-top: 0;
margin-bottom: 3px;
-moz-margin-start: 3px;
-moz-margin-end: 6px;
}
/* Extract from display.css, we only want these 2 rules */
.gcli-out-shortcut {
border: 1px solid #999;
border-radius: 3px;
padding: 0 4px;
margin: 0 4px;
font-size: 70%;
color: #666;
cursor: pointer;
vertical-align: bottom;
}
.gcli-out-shortcut:before {
color: #66F;
content: '\bb';
padding: 0 2px;
}
/* /*
* The language of a console is not en_US or any other common language * The language of a console is not en_US or any other common language
* (i.e we don't attempt to translate 'console.log(x)') * (i.e we don't attempt to translate 'console.log(x)')
@ -151,6 +160,15 @@
/* From: $GCLI/mozilla/gcli/ui/gcliterm-gnomestripe.css */ /* From: $GCLI/mozilla/gcli/ui/gcliterm-gnomestripe.css */
.gcliterm-input-node,
.gcliterm-complete-node {
font: 12px "DejaVu Sans Mono", monospace;
}
.gcli-out-shortcut {
font-family: "DejaVu Sans Mono", monospace;
}
/* From: $GCLI/lib/gcli/ui/arg_fetch.css */ /* From: $GCLI/lib/gcli/ui/arg_fetch.css */
.gcli-argfetch { .gcli-argfetch {
@ -181,7 +199,7 @@
.gcli-af-required { .gcli-af-required {
font-size: 90%; font-size: 90%;
color: #f66; color: #f66;
padding-left: 5px; -moz-padding-start: 5px;
} }
.gcli-af-error { .gcli-af-error {
@ -197,6 +215,10 @@
width: 100%; width: 100%;
} }
.gcli-field-javascript {
margin-bottom: 0;
}
/* From: $GCLI/lib/gcli/ui/menu.css */ /* From: $GCLI/lib/gcli/ui/menu.css */
.gcli-menu { .gcli-menu {
@ -284,3 +306,45 @@
color: #66F; color: #66F;
font-weight: bold; font-weight: bold;
} }
/* From: $GCLI/lib/gcli/commands/help.css */
.gcli-help-name {
text-align: end;
}
.gcli-help-arrow {
font-size: 70%;
color: #AAA;
}
.gcli-help-synopsis {
font-family: monospace;
font-weight: normal;
padding: 0 3px;
margin: 0 10px;
border: 1px solid #999;
border-radius: 3px;
color: #666;
cursor: pointer;
display: inline-block;
}
.gcli-help-synopsis:before {
color: #66F;
content: '\bb';
}
.gcli-help-description {
margin: 0 20px;
padding: 0;
}
.gcli-help-parameter {
margin: 0 30px;
padding: 0;
}
.gcli-help-header {
margin: 10px 0 6px;
}

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

@ -46,7 +46,6 @@
height: 100%; height: 100%;
vertical-align: middle; vertical-align: middle;
background-color: transparent; background-color: transparent;
font: 12px Consolas, "Lucida Console", monospace;
} }
.gcliterm-input-node { .gcliterm-input-node {
@ -64,24 +63,6 @@
-moz-padding-end: 4px; -moz-padding-end: 4px;
} }
.gcli-in-valid {
border-bottom: none;
}
.gcli-in-incomplete {
color: #DDD;
border-bottom: 1px dotted #999;
}
.gcli-in-error {
color: #DDD;
border-bottom: 1px dotted #F00;
}
.gcli-in-ontab {
color: #999;
}
.gcliterm-stack-node { .gcliterm-stack-node {
background: url("chrome://global/skin/icons/commandline.png") 4px center no-repeat; background: url("chrome://global/skin/icons/commandline.png") 4px center no-repeat;
width: 100%; width: 100%;
@ -109,19 +90,47 @@
border-bottom: 1px solid threedshadow; border-bottom: 1px solid threedshadow;
} }
.gcli-help-right {
text-align: right;
}
.gcliterm-menu { .gcliterm-menu {
display: -moz-box; display: -moz-box;
-moz-box-flex: 1; -moz-box-flex: 1;
border-bottom-color: white;
}
.gcliterm-hint-scroll {
overflow-y: scroll;
border-bottom-color: threedshadow;
} }
.gcliterm-hint-nospace { .gcliterm-hint-nospace {
display: none; display: none;
} }
.gcliterm-msg-body {
margin-top: 0;
margin-bottom: 3px;
-moz-margin-start: 3px;
-moz-margin-end: 6px;
}
/* Extract from display.css, we only want these 2 rules */
.gcli-out-shortcut {
border: 1px solid #999;
border-radius: 3px;
padding: 0 4px;
margin: 0 4px;
font-size: 70%;
color: #666;
cursor: pointer;
vertical-align: bottom;
}
.gcli-out-shortcut:before {
color: #66F;
content: '\bb';
padding: 0 2px;
}
/* /*
* The language of a console is not en_US or any other common language * The language of a console is not en_US or any other common language
* (i.e we don't attempt to translate 'console.log(x)') * (i.e we don't attempt to translate 'console.log(x)')
@ -151,10 +160,19 @@
/* From: $GCLI/mozilla/gcli/ui/gcliterm-pinstripe.css */ /* From: $GCLI/mozilla/gcli/ui/gcliterm-pinstripe.css */
.gcliterm-input-node,
.gcliterm-complete-node {
font: 11px Menlo, Monaco, monospace;
}
.gcliterm-complete-node { .gcliterm-complete-node {
padding-top: 6px !important; padding-top: 6px !important;
} }
.gcli-out-shortcut {
font-family: Menlo, Monaco, monospace;
}
/* From: $GCLI/lib/gcli/ui/arg_fetch.css */ /* From: $GCLI/lib/gcli/ui/arg_fetch.css */
.gcli-argfetch { .gcli-argfetch {
@ -185,7 +203,7 @@
.gcli-af-required { .gcli-af-required {
font-size: 90%; font-size: 90%;
color: #f66; color: #f66;
padding-left: 5px; -moz-padding-start: 5px;
} }
.gcli-af-error { .gcli-af-error {
@ -201,6 +219,10 @@
width: 100%; width: 100%;
} }
.gcli-field-javascript {
margin-bottom: 0;
}
/* From: $GCLI/lib/gcli/ui/menu.css */ /* From: $GCLI/lib/gcli/ui/menu.css */
.gcli-menu { .gcli-menu {
@ -288,3 +310,45 @@
color: #66F; color: #66F;
font-weight: bold; font-weight: bold;
} }
/* From: $GCLI/lib/gcli/commands/help.css */
.gcli-help-name {
text-align: end;
}
.gcli-help-arrow {
font-size: 70%;
color: #AAA;
}
.gcli-help-synopsis {
font-family: monospace;
font-weight: normal;
padding: 0 3px;
margin: 0 10px;
border: 1px solid #999;
border-radius: 3px;
color: #666;
cursor: pointer;
display: inline-block;
}
.gcli-help-synopsis:before {
color: #66F;
content: '\bb';
}
.gcli-help-description {
margin: 0 20px;
padding: 0;
}
.gcli-help-parameter {
margin: 0 30px;
padding: 0;
}
.gcli-help-header {
margin: 10px 0 6px;
}

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

@ -46,7 +46,6 @@
height: 100%; height: 100%;
vertical-align: middle; vertical-align: middle;
background-color: transparent; background-color: transparent;
font: 12px Consolas, "Lucida Console", monospace;
} }
.gcliterm-input-node { .gcliterm-input-node {
@ -64,24 +63,6 @@
-moz-padding-end: 4px; -moz-padding-end: 4px;
} }
.gcli-in-valid {
border-bottom: none;
}
.gcli-in-incomplete {
color: #DDD;
border-bottom: 1px dotted #999;
}
.gcli-in-error {
color: #DDD;
border-bottom: 1px dotted #F00;
}
.gcli-in-ontab {
color: #999;
}
.gcliterm-stack-node { .gcliterm-stack-node {
background: url("chrome://global/skin/icons/commandline.png") 4px center no-repeat; background: url("chrome://global/skin/icons/commandline.png") 4px center no-repeat;
width: 100%; width: 100%;
@ -109,19 +90,47 @@
border-bottom: 1px solid threedshadow; border-bottom: 1px solid threedshadow;
} }
.gcli-help-right {
text-align: right;
}
.gcliterm-menu { .gcliterm-menu {
display: -moz-box; display: -moz-box;
-moz-box-flex: 1; -moz-box-flex: 1;
border-bottom-color: white;
}
.gcliterm-hint-scroll {
overflow-y: scroll;
border-bottom-color: threedshadow;
} }
.gcliterm-hint-nospace { .gcliterm-hint-nospace {
display: none; display: none;
} }
.gcliterm-msg-body {
margin-top: 0;
margin-bottom: 3px;
-moz-margin-start: 3px;
-moz-margin-end: 6px;
}
/* Extract from display.css, we only want these 2 rules */
.gcli-out-shortcut {
border: 1px solid #999;
border-radius: 3px;
padding: 0 4px;
margin: 0 4px;
font-size: 70%;
color: #666;
cursor: pointer;
vertical-align: bottom;
}
.gcli-out-shortcut:before {
color: #66F;
content: '\bb';
padding: 0 2px;
}
/* /*
* The language of a console is not en_US or any other common language * The language of a console is not en_US or any other common language
* (i.e we don't attempt to translate 'console.log(x)') * (i.e we don't attempt to translate 'console.log(x)')
@ -151,6 +160,15 @@
/* From: $GCLI/mozilla/gcli/ui/gcliterm-winstripe.css */ /* From: $GCLI/mozilla/gcli/ui/gcliterm-winstripe.css */
.gcliterm-input-node,
.gcliterm-complete-node {
font: 12px Consolas, "Lucida Console", monospace;
}
.gcli-out-shortcut {
font-family: Consolas, Inconsolata, "Courier New", monospace;
}
/* From: $GCLI/lib/gcli/ui/arg_fetch.css */ /* From: $GCLI/lib/gcli/ui/arg_fetch.css */
.gcli-argfetch { .gcli-argfetch {
@ -181,7 +199,7 @@
.gcli-af-required { .gcli-af-required {
font-size: 90%; font-size: 90%;
color: #f66; color: #f66;
padding-left: 5px; -moz-padding-start: 5px;
} }
.gcli-af-error { .gcli-af-error {
@ -197,6 +215,10 @@
width: 100%; width: 100%;
} }
.gcli-field-javascript {
margin-bottom: 0;
}
/* From: $GCLI/lib/gcli/ui/menu.css */ /* From: $GCLI/lib/gcli/ui/menu.css */
.gcli-menu { .gcli-menu {
@ -284,3 +306,45 @@
color: #66F; color: #66F;
font-weight: bold; font-weight: bold;
} }
/* From: $GCLI/lib/gcli/commands/help.css */
.gcli-help-name {
text-align: end;
}
.gcli-help-arrow {
font-size: 70%;
color: #AAA;
}
.gcli-help-synopsis {
font-family: monospace;
font-weight: normal;
padding: 0 3px;
margin: 0 10px;
border: 1px solid #999;
border-radius: 3px;
color: #666;
cursor: pointer;
display: inline-block;
}
.gcli-help-synopsis:before {
color: #66F;
content: '\bb';
}
.gcli-help-description {
margin: 0 20px;
padding: 0;
}
.gcli-help-parameter {
margin: 0 30px;
padding: 0;
}
.gcli-help-header {
margin: 10px 0 6px;
}

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

@ -355,22 +355,17 @@ let PromptUtils = {
getTabModalPrompt : function (domWin) { getTabModalPrompt : function (domWin) {
var promptBox = null; var promptBox = null;
// Given a content DOM window, returns the chrome window it's in.
function getChromeWindow(aWindow) {
var chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler.ownerDocument.defaultView;
return chromeWin;
}
try { try {
// Get the topmost window, in case we're in a frame. // Get the topmost window, in case we're in a frame.
var promptWin = domWin.top; var promptWin = domWin.top;
// Get the chrome window for the content window we're using. // Get the chrome window for the content window we're using.
// (Unwrap because we need a non-IDL property below.) // (Unwrap because we need a non-IDL property below.)
var chromeWin = getChromeWindow(promptWin).wrappedJSObject; var chromeWin = promptWin.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler.ownerDocument
.defaultView.wrappedJSObject;
if (chromeWin.getTabModalPromptBox) if (chromeWin.getTabModalPromptBox)
promptBox = chromeWin.getTabModalPromptBox(promptWin); promptBox = chromeWin.getTabModalPromptBox(promptWin);