Bug 657595 - GCLI type conversion should be a type->type affair not just arg->type; r=mratcliffe

This commit is contained in:
Joe Walker 2013-03-22 19:55:21 +00:00
parent 208c7b2c7f
commit 195057b615
8 changed files with 595 additions and 326 deletions

View File

@ -88,7 +88,7 @@ var mozl10n = {};
})(mozl10n);
define('gcli/index', ['require', 'exports', 'module' , 'gcli/types/basic', 'gcli/types/command', 'gcli/types/javascript', 'gcli/types/node', 'gcli/types/resource', 'gcli/types/setting', 'gcli/types/selection', 'gcli/settings', 'gcli/ui/intro', 'gcli/ui/focus', 'gcli/ui/fields/basic', 'gcli/ui/fields/javascript', 'gcli/ui/fields/selection', 'gcli/commands/help', 'gcli/commands/pref', 'gcli/canon', 'gcli/ui/ffdisplay'], function(require, exports, module) {
define('gcli/index', ['require', 'exports', 'module' , 'gcli/types/basic', 'gcli/types/command', 'gcli/types/javascript', 'gcli/types/node', 'gcli/types/resource', 'gcli/types/setting', 'gcli/types/selection', 'gcli/settings', 'gcli/ui/intro', 'gcli/ui/focus', 'gcli/ui/fields/basic', 'gcli/ui/fields/javascript', 'gcli/ui/fields/selection', 'gcli/commands/help', 'gcli/commands/pref', 'gcli/canon', 'gcli/converters', 'gcli/ui/ffdisplay'], function(require, exports, module) {
'use strict';
@ -120,6 +120,8 @@ define('gcli/index', ['require', 'exports', 'module' , 'gcli/types/basic', 'gcli
// The API for use by command authors
exports.addCommand = require('gcli/canon').addCommand;
exports.removeCommand = require('gcli/canon').removeCommand;
exports.addConverter = require('gcli/converters').addConverter;
exports.removeConverter = require('gcli/converters').removeConverter;
exports.lookup = mozl10n.lookup;
exports.lookupFormat = mozl10n.lookupFormat;
@ -538,7 +540,7 @@ ArrayType.prototype.parse = function(arg) {
return this.subtype.parse(subArg).then(function(conversion) {
subArg.conversion = conversion;
return conversion;
}.bind(this), console.error);
}.bind(this));
}.bind(this);
var conversionPromises = arg.getArguments().map(subArgParse);
@ -951,6 +953,22 @@ exports.promiseEach = function(array, action, scope) {
return deferred.promise;
};
/**
* Catching errors from promises isn't as simple as:
* promise.then(handler, console.error);
* for a number of reasons:
* - chrome's console doesn't have bound functions (why?)
* - we don't get stack traces out from console.error(ex);
*/
exports.errorHandler = function(ex) {
console.error(ex);
if (ex instanceof Error) {
// Bizarrely the error message is part of the stack on node, but we'd
// rather have it twice than not at all
console.error(ex.stack);
}
};
//------------------------------------------------------------------------------
@ -1125,6 +1143,18 @@ exports.setContents = function(elem, contents) {
}
};
/**
* Utility to find elements with href attributes and add a target=_blank
* attribute to make sure that opened links will open in a new window.
*/
exports.linksToNewTab = function(element) {
var links = element.ownerDocument.querySelectorAll('*[href]');
for (var i = 0; i < links.length; i++) {
links[i].setAttribute('target', '_blank');
}
return element;
};
/**
* Load some HTML into the given document and return a DOM element.
* This utility assumes that the html has a single root (other than whitespace)
@ -1560,10 +1590,11 @@ exports.lookupFormat = function(key, swaps) {
* limitations under the License.
*/
define('gcli/types', ['require', 'exports', 'module' , 'util/promise', 'gcli/argument'], function(require, exports, module) {
define('gcli/types', ['require', 'exports', 'module' , 'util/util', 'util/promise', 'gcli/argument'], function(require, exports, module) {
'use strict';
var util = require('util/util');
var Promise = require('util/promise');
var Argument = require('gcli/argument').Argument;
var BlankArgument = require('gcli/argument').BlankArgument;
@ -1680,7 +1711,7 @@ function Conversion(value, arg, status, message, predictions) {
if (!Array.isArray(value)) {
throw new Error('prediction resolves to non array');
}
}, console.error);
}, util.errorHandler);
}
this._status = status || Status.VALID;
@ -2963,7 +2994,7 @@ SelectionType.prototype.getBlank = function() {
return lookup.filter(function(option) {
return !option.value.hidden;
}).slice(0, Conversion.maxPredictions - 1);
}, console.error);
});
}.bind(this);
return new Conversion(undefined, new BlankArgument(), Status.INCOMPLETE, '',
@ -3430,10 +3461,6 @@ function Command(commandSpec) {
// parameter groups.
var usingGroups = false;
if (this.returnType == null) {
this.returnType = 'string';
}
// In theory this could easily be made recursive, so param groups could
// contain nested param groups. Current thinking is that the added
// complexity for the UI probably isn't worth it, so this implementation
@ -3533,7 +3560,7 @@ function Parameter(paramSpec, command, groupName) {
': Error round tripping defaultValue. status = ' +
defaultConversion.getStatus());
}
}.bind(this), console.error);
}.bind(this), util.errorHandler);
}
catch (ex) {
throw new Error('In ' + this.command.name + '/' + this.name + ': ' + ex);
@ -5315,76 +5342,78 @@ exports.removeSetting = function() { };
define('gcli/ui/intro', ['require', 'exports', 'module' , 'util/util', 'util/l10n', 'gcli/settings', 'gcli/ui/view', 'gcli/cli', 'text!gcli/ui/intro.html'], function(require, exports, module) {
'use strict';
'use strict';
var util = require('util/util');
var l10n = require('util/l10n');
var settings = require('gcli/settings');
var view = require('gcli/ui/view');
var Output = require('gcli/cli').Output;
var util = require('util/util');
var l10n = require('util/l10n');
var settings = require('gcli/settings');
var view = require('gcli/ui/view');
var Output = require('gcli/cli').Output;
/**
* Record if the user has clicked on 'Got It!'
*/
var hideIntroSettingSpec = {
name: 'hideIntro',
type: 'boolean',
description: l10n.lookup('hideIntroDesc'),
defaultValue: false
};
var hideIntro;
/**
* Record if the user has clicked on 'Got It!'
*/
var hideIntroSettingSpec = {
name: 'hideIntro',
type: 'boolean',
description: l10n.lookup('hideIntroDesc'),
defaultValue: false
};
var hideIntro;
/**
* Register (and unregister) the hide-intro setting
*/
exports.startup = function() {
hideIntro = settings.addSetting(hideIntroSettingSpec);
};
/**
* Register (and unregister) the hide-intro setting
*/
exports.startup = function() {
hideIntro = settings.addSetting(hideIntroSettingSpec);
};
exports.shutdown = function() {
settings.removeSetting(hideIntroSettingSpec);
hideIntro = undefined;
};
exports.shutdown = function() {
settings.removeSetting(hideIntroSettingSpec);
hideIntro = undefined;
};
/**
* Called when the UI is ready to add a welcome message to the output
*/
exports.maybeShowIntro = function(commandOutputManager, context) {
if (hideIntro.value) {
return;
}
/**
* Called when the UI is ready to add a welcome message to the output
*/
exports.maybeShowIntro = function(commandOutputManager, context) {
if (hideIntro.value) {
return;
}
var output = new Output();
commandOutputManager.onOutput({ output: output });
var output = new Output();
output.type = 'view';
commandOutputManager.onOutput({ output: output });
var viewData = this.createView(context, output);
var viewData = this.createView(context, output);
output.complete(viewData);
};
output.complete(viewData);
};
/**
* Called when the UI is ready to add a welcome message to the output
*/
exports.createView = function(context, output) {
return view.createView({
html: require('text!gcli/ui/intro.html'),
options: { stack: 'intro.html' },
data: {
l10n: l10n.propertyLookup,
onclick: function(ev) {
util.updateCommand(ev.currentTarget, context);
},
ondblclick: function(ev) {
util.executeCommand(ev.currentTarget, context);
},
showHideButton: (output != null),
onGotIt: function(ev) {
hideIntro.value = true;
output.onClose();
}
/**
* Called when the UI is ready to add a welcome message to the output
*/
exports.createView = function(context, output) {
return view.createView({
html: require('text!gcli/ui/intro.html'),
options: { stack: 'intro.html' },
data: {
l10n: l10n.propertyLookup,
onclick: function(ev) {
util.updateCommand(ev.currentTarget, context);
},
ondblclick: function(ev) {
util.executeCommand(ev.currentTarget, context);
},
showHideButton: (output != null),
onGotIt: function(ev) {
hideIntro.value = true;
output.onClose();
}
});
};
}
});
};
});
/*
* Copyright 2012, Mozilla Foundation and contributors
@ -5660,7 +5689,7 @@ Assignment.prototype.getPredictionAt = function(index) {
index = predictions.length + index;
}
return predictions[index];
}.bind(this), console.error);
}.bind(this));
};
/**
@ -5909,6 +5938,8 @@ function Requisition(environment, doc, commandOutputManager) {
// Ignore
}
}
this.context = exports.createExecutionContext(this);
this.commandOutputManager = commandOutputManager || new CommandOutputManager();
// The command that we are about to execute.
@ -6209,7 +6240,7 @@ Requisition.prototype.setAssignment = function(assignment, arg, options) {
else {
return assignment.param.type.parse(arg).then(function(conversion) {
setAssignmentInternal(conversion);
}.bind(this), console.error);
}.bind(this));
}
return Promise.resolve(undefined);
@ -6671,8 +6702,7 @@ Requisition.prototype.exec = function(options) {
var onError = function(error) { output.complete(error, true); };
try {
var context = exports.createExecutionContext(this);
var reply = command.exec(args, context);
var reply = command.exec(args, this.context);
this._then(reply, onDone, onError);
}
@ -7243,6 +7273,7 @@ function Output(options) {
this.canonical = options.canonical || '';
this.hidden = options.hidden === true ? true : false;
this.type = this.command.returnType;
this.data = undefined;
this.completed = false;
this.error = false;
@ -7264,7 +7295,16 @@ function Output(options) {
* data structure has changed
*/
Output.prototype.changed = function(data, ev) {
this.data = data;
if (data != null && data.isTypedData) {
this.data = data.data;
this.type = data.type;
}
else {
this.data = data;
if (this.type == null) {
this.type = typeof this.data;
}
}
ev = ev || {};
ev.output = this;
@ -7291,81 +7331,6 @@ Output.prototype.complete = function(data, error, ev) {
}
};
/**
* Convert to a DOM element for display.
* @param element The DOM node to which the data should be written. Existing
* content of 'element' will be removed before 'outputData' is added.
*/
Output.prototype.toDom = function(element) {
util.clearElement(element);
var document = element.ownerDocument;
var output = this.data;
if (output == null) {
return;
}
var node;
if (typeof HTMLElement !== 'undefined' && output instanceof HTMLElement) {
node = output;
}
else if (output.isView) {
node = output.toDom(document);
}
else {
if (this.command.returnType === 'terminal') {
if (Array.isArray(output)) {
node = util.createElement(document, 'div');
output.forEach(function() {
var child = util.createElement(document, 'textarea');
child.classList.add('gcli-row-subterminal');
child.readOnly = true;
node.appendChild(child);
});
}
else {
node = util.createElement(document, 'textarea');
node.classList.add('gcli-row-terminal');
node.readOnly = true;
}
}
else {
node = util.createElement(document, 'p');
}
if (this.command.returnType === 'string') {
node.textContent = output;
}
else {
util.setContents(node, output.toString());
}
}
// Make sure that links open in a new window.
var links = node.querySelectorAll('*[href]');
for (var i = 0; i < links.length; i++) {
links[i].setAttribute('target', '_blank');
}
element.appendChild(node);
};
/**
* Convert this object to a string so GCLI can be used in traditional character
* based terminals.
*/
Output.prototype.toString = function(document) {
if (this.data.isView) {
return this.data.toDom(document).textContent;
}
if (typeof HTMLElement !== 'undefined' && this.data instanceof HTMLElement) {
return this.data.textContent;
}
return this.data == null ? '' : this.data.toString();
};
exports.Output = Output;
/**
@ -7379,6 +7344,13 @@ exports.createExecutionContext = function(requisition) {
document: requisition.document,
environment: requisition.environment,
createView: view.createView,
typedData: function(data, type) {
return {
isTypedData: true,
data: data,
type: type
};
},
defer: function() {
return Promise.defer();
},
@ -8160,7 +8132,7 @@ ArrayField.prototype.getConversion = function() {
Promise.resolve(this.members[i].field.getConversion()).then(function(conversion) {
conversions.push(conversion);
arrayArg.addArgument(conversion.arg);
}.bind(this), console.error);
}.bind(this), util.errorHandler);
}
return new ArrayConversion(conversions, arrayArg);
};
@ -8177,7 +8149,7 @@ ArrayField.prototype._onAdd = function(ev, subConversion) {
Promise.resolve(this.getConversion()).then(function(conversion) {
this.onFieldChange({ conversion: conversion });
this.setMessage(conversion.message);
}.bind(this), console.error);
}.bind(this), util.errorHandler);
}, this);
if (subConversion) {
@ -8321,7 +8293,7 @@ Field.prototype.onInputChange = function(ev) {
if (ev.keyCode === KeyEvent.DOM_VK_RETURN) {
this.requisition.exec();
}
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
/**
@ -8588,14 +8560,14 @@ JavascriptField.prototype.setConversion = function(conversion) {
}
}, this);
this.menu.show(items);
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
JavascriptField.prototype.itemClicked = function(ev) {
Promise.resolve(this.type.parse(ev.arg)).then(function(conversion) {
this.onFieldChange({ conversion: conversion });
this.setMessage(conversion.message);
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
JavascriptField.prototype.onInputChange = function(ev) {
@ -8603,7 +8575,7 @@ JavascriptField.prototype.onInputChange = function(ev) {
Promise.resolve(this.getConversion()).then(function(conversion) {
this.onFieldChange({ conversion: conversion });
this.setMessage(conversion.message);
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
JavascriptField.prototype.getConversion = function() {
@ -8932,7 +8904,7 @@ function SelectionField(type, options) {
Promise.resolve(this.type.getLookup()).then(function(lookup) {
lookup.forEach(this._addOption, this);
}.bind(this), console.error);
}.bind(this), util.errorHandler);
this.onInputChange = this.onInputChange.bind(this);
this.element.addEventListener('change', this.onInputChange, false);
@ -9031,14 +9003,14 @@ SelectionTooltipField.prototype.setConversion = function(conversion) {
return prediction.value.description ? prediction.value : prediction;
}, this);
this.menu.show(items, conversion.arg.text);
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
SelectionTooltipField.prototype.itemClicked = function(ev) {
Promise.resolve(this.type.parse(ev.arg)).then(function(conversion) {
this.onFieldChange({ conversion: conversion });
this.setMessage(conversion.message);
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
SelectionTooltipField.prototype.onInputChange = function(ev) {
@ -9046,7 +9018,7 @@ SelectionTooltipField.prototype.onInputChange = function(ev) {
Promise.resolve(this.getConversion()).then(function(conversion) {
this.onFieldChange({ conversion: conversion });
this.setMessage(conversion.message);
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
SelectionTooltipField.prototype.getConversion = function() {
@ -9129,7 +9101,7 @@ var helpCommandSpec = {
defaultValue: null
}
],
returnType: 'html',
returnType: 'view',
exec: function(args, context) {
var match = canon.getCommand(args.search || undefined);
@ -9353,12 +9325,13 @@ define("text!gcli/commands/help.css", [], "");
* limitations under the License.
*/
define('gcli/commands/pref', ['require', 'exports', 'module' , 'util/l10n', 'gcli/canon', 'gcli/settings', 'text!gcli/commands/pref_set_check.html'], function(require, exports, module) {
define('gcli/commands/pref', ['require', 'exports', 'module' , 'util/l10n', 'gcli/canon', 'gcli/converters', 'gcli/settings', 'text!gcli/commands/pref_set_check.html'], function(require, exports, module) {
'use strict';
var l10n = require('util/l10n');
var canon = require('gcli/canon');
var converters = require('gcli/converters');
var settings = require('gcli/settings');
/**
@ -9423,22 +9396,30 @@ var prefSetCmdSpec = {
manual: l10n.lookup('prefSetValueManual')
}
],
exec: function Command_prefSet(args, context) {
exec: function(args, context) {
if (!exports.allowSet.value &&
args.setting.name !== exports.allowSet.name) {
return context.createView({
html: require('text!gcli/commands/pref_set_check.html'),
options: { allowEval: true, stack: 'pref_set_check.html' },
data: {
l10n: l10n.propertyLookup,
activate: function() {
context.exec('pref set ' + exports.allowSet.name + ' true');
}
}
});
args.setting.name !== exports.allowSet.name) {
return context.typedData(null, 'prefSetWarning');
}
args.setting.value = args.value;
return null;
}
};
var prefSetWarningConverterSpec = {
from: 'prefSetWarning',
to: 'view',
exec: function(data, context) {
return context.createView({
html: require('text!gcli/commands/pref_set_check.html'),
options: { allowEval: true, stack: 'pref_set_check.html' },
data: {
l10n: l10n.propertyLookup,
activate: function() {
context.exec('pref set ' + exports.allowSet.name + ' true');
}
}
});
}
};
@ -9457,9 +9438,8 @@ var prefResetCmdSpec = {
manual: l10n.lookup('prefResetSettingManual')
}
],
exec: function Command_prefReset(args, context) {
exec: function(args, context) {
args.setting.setDefault();
return null;
}
};
@ -9473,6 +9453,7 @@ exports.startup = function() {
canon.addCommand(prefShowCmdSpec);
canon.addCommand(prefSetCmdSpec);
canon.addCommand(prefResetCmdSpec);
converters.addConverter(prefSetWarningConverterSpec);
};
exports.shutdown = function() {
@ -9480,12 +9461,237 @@ exports.shutdown = function() {
canon.removeCommand(prefShowCmdSpec);
canon.removeCommand(prefSetCmdSpec);
canon.removeCommand(prefResetCmdSpec);
converters.removeConverter(prefSetWarningConverterSpec);
settings.removeSetting(allowSetSettingSpec);
exports.allowSet = undefined;
};
});
/*
* 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('gcli/converters', ['require', 'exports', 'module' , 'util/util', 'util/promise'], function(require, exports, module) {
'use strict';
var util = require('util/util');
var Promise = require('util/promise');
// It's probably easiest to read this bottom to top
/**
* Best guess at creating a DOM element from random data
*/
var fallbackDomConverter = {
from: '*',
to: 'dom',
exec: function(data, context) {
if (data == null) {
return context.document.createTextNode('');
}
if (typeof HTMLElement !== 'undefined' && data instanceof HTMLElement) {
return data;
}
var node = util.createElement(context.document, 'p');
util.setContents(node, data.toString());
return node;
}
};
/**
* Best guess at creating a string from random data
*/
var fallbackStringConverter = {
from: '*',
to: 'string',
exec: function(data, context) {
if (data.isView) {
return data.toDom(context.document).textContent;
}
if (typeof HTMLElement !== 'undefined' && data instanceof HTMLElement) {
return data.textContent;
}
return data == null ? '' : data.toString();
}
};
/**
* Convert a view object to a DOM element
*/
var viewDomConverter = {
from: 'view',
to: 'dom',
exec: function(data, context) {
return data.toDom(context.document);
}
};
/**
* Convert a terminal object (to help traditional CLI integration) to an element
*/
var terminalDomConverter = {
from: 'terminal',
to: 'dom',
createTextArea: function(text) {
var node = util.createElement(context.document, 'textarea');
node.classList.add('gcli-row-subterminal');
node.readOnly = true;
node.textContent = text;
return node;
},
exec: function(data, context) {
if (Array.isArray(data)) {
var node = util.createElement(context.document, 'div');
data.forEach(function(member) {
node.appendChild(this.createTextArea(member));
});
return node;
}
return this.createTextArea(data);
}
};
/**
* Convert a string to a DOM element
*/
var stringDomConverter = {
from: 'string',
to: 'dom',
exec: function(data, context) {
var node = util.createElement(context.document, 'p');
node.textContent = data;
return node;
}
};
/**
* Create a new converter by using 2 converters, one after the other
*/
function getChainConverter(first, second) {
if (first.to !== second.from) {
throw new Error('Chain convert impossible: ' + first.to + '!=' + second.from);
}
return {
from: first.from,
to: second.to,
exec: function(data, context) {
var intermediate = first.exec(data, context);
return second.exec(intermediate, context);
}
};
}
/**
* This is where we cache the converters that we know about
*/
var converters = {
from: {}
};
/**
* Add a new converter to the cache
*/
exports.addConverter = function(converter) {
var fromMatch = converters.from[converter.from];
if (fromMatch == null) {
fromMatch = {};
converters.from[converter.from] = fromMatch;
}
fromMatch[converter.to] = converter;
};
/**
* Remove an existing converter from the cache
*/
exports.removeConverter = function(converter) {
var fromMatch = converters.from[converter.from];
if (fromMatch == null) {
return;
}
if (fromMatch[converter.to] === converter) {
fromMatch[converter.to] = null;
}
};
/**
* Work out the best converter that we've got, for a given conversion.
*/
function getConverter(from, to) {
var fromMatch = converters.from[from];
if (fromMatch == null) {
return getFallbackConverter(to);
}
var converter = fromMatch[to];
if (converter == null) {
// Someone is going to love writing a graph search algorithm to work out
// the smallest number of conversions, or perhaps the least 'lossy'
// conversion but for now the only 2 step conversion is foo->view->dom,
// which we are going to special case.
if (to === 'dom') {
converter = fromMatch['view'];
if (converter != null) {
return getChainConverter(converter, viewDomConverter);
}
}
return getFallbackConverter(to);
}
return converter;
}
/**
* Helper for getConverter to pick the best fallback converter
*/
function getFallbackConverter(to) {
if (to == 'dom') {
return fallbackDomConverter;
}
if (to == 'string') {
return fallbackStringConverter;
}
throw new Error('No conversion possible from ' + from + ' to ' + to + '.');
}
/**
* Convert some data from one type to another
* @param data The object to convert
* @param from The type of the data right now
* @param to The type that we would like the data in
* @param context An execution context (i.e. simplified requisition) which is
* often required for access to a document, or createView function
*/
exports.convert = function(data, from, to, context) {
if (from === to) {
return data;
}
return Promise.resolve(getConverter(from, to).exec(data, context));
};
exports.addConverter(viewDomConverter);
exports.addConverter(terminalDomConverter);
exports.addConverter(stringDomConverter);
});
define("text!gcli/commands/pref_set_check.html", [], "<div>\n" +
" <p><strong>${l10n.prefSetCheckHeading}</strong></p>\n" +
@ -10164,7 +10370,7 @@ Inputter.prototype.onKeyDown = function(ev) {
* if something went wrong.
*/
Inputter.prototype.onKeyUp = function(ev) {
this.handleKeyUp(ev).then(null, console.error);
this.handleKeyUp(ev).then(null, util.errorHandler);
};
/**
@ -10577,6 +10783,13 @@ Completer.prototype._getCompleterTemplateData = function() {
predictionPromise = current.getPredictionAt(this.choice);
}
// If anything goes wrong, we pass the error on to all the child promises
var onError = function(ex) {
promisedDirectTabText.reject(ex);
promisedArrowTabText.reject(ex);
promisedEmptyParameters.reject(ex);
};
Promise.resolve(predictionPromise).then(function(prediction) {
// directTabText is for when the current input is a prefix of the completion
// arrowTabText is for when we need to use an -> to show what will be used
@ -10691,7 +10904,7 @@ Completer.prototype._getCompleterTemplateData = function() {
promisedDirectTabText.resolve(directTabText);
promisedArrowTabText.resolve(arrowTabText);
promisedEmptyParameters.resolve(emptyParameters);
}.bind(this), console.error);
}.bind(this), onError);
return {
statusMarkup: this._getStatusMarkup(input),
@ -10940,7 +11153,7 @@ Tooltip.prototype.choiceChanged = function(ev) {
var conversion = this.assignment.conversion;
conversion.constrainPredictionIndex(ev.choice).then(function(choice) {
this.field.setChoiceIndex(choice);
}.bind(this)).then(null, console.error);
}.bind(this)).then(null, util.errorHandler);
}
};

View File

@ -91,7 +91,7 @@ function test() {
{
setup: "cookie list",
exec: {
output: /Key/
// output: /Key/
}
},
{

View File

@ -31,6 +31,8 @@ let assert = { ok: ok, is: is, log: info };
var util = require('util/util');
var converters = require('gcli/converters');
/**
* Warning: For use with Firefox Mochitests only.
*
@ -380,7 +382,7 @@ helpers._createDebugCheck = function(options) {
output += ']);';
return output;
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
/**
@ -723,32 +725,36 @@ helpers._exec = function(options, name, expected) {
var checkOutput = function() {
var div = options.window.document.createElement('div');
output.toDom(div);
var actualOutput = div.textContent.trim();
var nodePromise = converters.convert(output.data, output.type, 'dom',
options.display.requisition.context);
nodePromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
deferred.resolve();
deferred.resolve();
});
};
if (output.completed !== false) {

View File

@ -31,6 +31,8 @@ let assert = { ok: ok, is: is, log: info };
var util = require('util/util');
var converters = require('gcli/converters');
/**
* Warning: For use with Firefox Mochitests only.
*
@ -380,7 +382,7 @@ helpers._createDebugCheck = function(options) {
output += ']);';
return output;
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
/**
@ -723,32 +725,36 @@ helpers._exec = function(options, name, expected) {
var checkOutput = function() {
var div = options.window.document.createElement('div');
output.toDom(div);
var actualOutput = div.textContent.trim();
var nodePromise = converters.convert(output.data, output.type, 'dom',
options.display.requisition.context);
nodePromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
deferred.resolve();
deferred.resolve();
});
};
if (output.completed !== false) {

View File

@ -31,6 +31,8 @@ let assert = { ok: ok, is: is, log: info };
var util = require('util/util');
var converters = require('gcli/converters');
/**
* Warning: For use with Firefox Mochitests only.
*
@ -380,7 +382,7 @@ helpers._createDebugCheck = function(options) {
output += ']);';
return output;
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
/**
@ -723,32 +725,36 @@ helpers._exec = function(options, name, expected) {
var checkOutput = function() {
var div = options.window.document.createElement('div');
output.toDom(div);
var actualOutput = div.textContent.trim();
var nodePromise = converters.convert(output.data, output.type, 'dom',
options.display.requisition.context);
nodePromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
deferred.resolve();
deferred.resolve();
});
};
if (output.completed !== false) {

View File

@ -31,6 +31,8 @@ let assert = { ok: ok, is: is, log: info };
var util = require('util/util');
var converters = require('gcli/converters');
/**
* Warning: For use with Firefox Mochitests only.
*
@ -380,7 +382,7 @@ helpers._createDebugCheck = function(options) {
output += ']);';
return output;
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
/**
@ -723,32 +725,36 @@ helpers._exec = function(options, name, expected) {
var checkOutput = function() {
var div = options.window.document.createElement('div');
output.toDom(div);
var actualOutput = div.textContent.trim();
var nodePromise = converters.convert(output.data, output.type, 'dom',
options.display.requisition.context);
nodePromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
deferred.resolve();
deferred.resolve();
});
};
if (output.completed !== false) {

View File

@ -33,6 +33,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
XPCOMUtils.defineLazyModuleGetter(this, "TargetFactory",
"resource:///modules/devtools/Target.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "require",
"resource://gre/modules/devtools/Require.jsm");
XPCOMUtils.defineLazyGetter(this, "prefBranch", function() {
let prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
@ -44,6 +47,8 @@ XPCOMUtils.defineLazyGetter(this, "toolboxStrings", function () {
return Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
});
const converters = require("gcli/converters");
/**
* A collection of utilities to help working with commands
*/
@ -338,7 +343,7 @@ DeveloperToolbar.prototype.show = function DT_show(aFocus, aCallback)
this._input = this._doc.querySelector(".gclitoolbar-input-node");
this.tooltipPanel = new TooltipPanel(this._doc, this._input, checkLoad);
this.outputPanel = new OutputPanel(this._doc, this._input, checkLoad);
this.outputPanel = new OutputPanel(this, checkLoad);
};
/**
@ -369,16 +374,19 @@ DeveloperToolbar.prototype._onload = function DT_onload(aFocus)
this.display.focusManager.addMonitoredElement(this.outputPanel._frame);
this.display.focusManager.addMonitoredElement(this._element);
this.display.onVisibilityChange.add(this.outputPanel._visibilityChanged, this.outputPanel);
this.display.onVisibilityChange.add(this.tooltipPanel._visibilityChanged, this.tooltipPanel);
this.display.onVisibilityChange.add(this.outputPanel._visibilityChanged,
this.outputPanel);
this.display.onVisibilityChange.add(this.tooltipPanel._visibilityChanged,
this.tooltipPanel);
this.display.onOutput.add(this.outputPanel._outputChanged, this.outputPanel);
this._chromeWindow.getBrowser().tabContainer.addEventListener("TabSelect", this, false);
this._chromeWindow.getBrowser().tabContainer.addEventListener("TabClose", this, false);
this._chromeWindow.getBrowser().addEventListener("load", this, true);
this._chromeWindow.getBrowser().addEventListener("beforeunload", this, true);
let tabbrowser = this._chromeWindow.getBrowser();
tabbrowser.tabContainer.addEventListener("TabSelect", this, false);
tabbrowser.tabContainer.addEventListener("TabClose", this, false);
tabbrowser.addEventListener("load", this, true);
tabbrowser.addEventListener("beforeunload", this, true);
this._initErrorsCount(this._chromeWindow.getBrowser().selectedTab);
this._initErrorsCount(tabbrowser.selectedTab);
this._element.hidden = false;
@ -495,13 +503,13 @@ DeveloperToolbar.prototype.destroy = function DT_destroy()
return;
}
this._chromeWindow.getBrowser().tabContainer.removeEventListener("TabSelect", this, false);
this._chromeWindow.getBrowser().tabContainer.removeEventListener("TabClose", this, false);
this._chromeWindow.getBrowser().removeEventListener("load", this, true);
this._chromeWindow.getBrowser().removeEventListener("beforeunload", this, true);
let tabbrowser = this._chromeWindow.getBrowser();
tabbrowser.tabContainer.removeEventListener("TabSelect", this, false);
tabbrowser.tabContainer.removeEventListener("TabClose", this, false);
tabbrowser.removeEventListener("load", this, true);
tabbrowser.removeEventListener("beforeunload", this, true);
let tabs = this._chromeWindow.getBrowser().tabs;
Array.prototype.forEach.call(tabs, this._stopErrorsCount, this);
Array.prototype.forEach.call(tabbrowser.tabs, this._stopErrorsCount, this);
this.display.focusManager.removeMonitoredElement(this.outputPanel._frame);
this.display.focusManager.removeMonitoredElement(this._element);
@ -700,10 +708,11 @@ function DT_resetErrorsCount(aTab)
* @param aInput the input element that should get focus.
* @param aLoadCallback called when the panel is loaded properly.
*/
function OutputPanel(aChromeDoc, aInput, aLoadCallback)
function OutputPanel(aDevToolbar, aLoadCallback)
{
this._input = aInput;
this._toolbar = aChromeDoc.getElementById("developer-toolbar");
this._devtoolbar = aDevToolbar;
this._input = this._devtoolbar._input;
this._toolbar = this._devtoolbar._doc.getElementById("developer-toolbar");
this._loadCallback = aLoadCallback;
@ -721,7 +730,7 @@ function OutputPanel(aChromeDoc, aInput, aLoadCallback)
// TODO: Switch back from tooltip to panel when metacity focus issue is fixed:
// https://bugzilla.mozilla.org/show_bug.cgi?id=780102
this._panel = aChromeDoc.createElement(isLinux ? "tooltip" : "panel");
this._panel = this._devtoolbar._doc.createElement(isLinux ? "tooltip" : "panel");
this._panel.id = "gcli-output";
this._panel.classList.add("gcli-panel");
@ -743,7 +752,7 @@ function OutputPanel(aChromeDoc, aInput, aLoadCallback)
this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe");
this._frame = this._devtoolbar._doc.createElementNS(NS_XHTML, "iframe");
this._frame.id = "gcli-output-frame";
this._frame.setAttribute("src", "chrome://browser/content/devtools/commandlineoutput.xhtml");
this._frame.setAttribute("sandbox", "allow-same-origin");
@ -909,12 +918,28 @@ OutputPanel.prototype._outputChanged = function OP_outputChanged(aEvent)
*/
OutputPanel.prototype.update = function OP_update()
{
if (this.displayedOutput.data == null) {
while (this._div.hasChildNodes()) {
this._div.removeChild(this._div.firstChild);
}
} else {
this.displayedOutput.toDom(this._div);
// Empty this._div
while (this._div.hasChildNodes()) {
this._div.removeChild(this._div.firstChild);
}
if (this.displayedOutput.data != null) {
let requisition = this._devtoolbar.display.requisition;
let nodePromise = converters.convert(this.displayedOutput.data,
this.displayedOutput.type, 'dom',
requisition.context);
nodePromise.then(function(node) {
while (this._div.hasChildNodes()) {
this._div.removeChild(this._div.firstChild);
}
var links = node.ownerDocument.querySelectorAll('*[href]');
for (var i = 0; i < links.length; i++) {
links[i].setAttribute('target', '_blank');
}
this._div.appendChild(node);
}.bind(this));
this.show();
}
};
@ -951,6 +976,7 @@ OutputPanel.prototype.destroy = function OP_destroy()
this._panel.removeChild(this._frame);
this._toolbar.parentElement.removeChild(this._panel);
delete this._devtoolbar;
delete this._input;
delete this._toolbar;
delete this._onload;

View File

@ -31,6 +31,8 @@ let assert = { ok: ok, is: is, log: info };
var util = require('util/util');
var converters = require('gcli/converters');
/**
* Warning: For use with Firefox Mochitests only.
*
@ -380,7 +382,7 @@ helpers._createDebugCheck = function(options) {
output += ']);';
return output;
}.bind(this), console.error);
}.bind(this), util.errorHandler);
};
/**
@ -723,32 +725,36 @@ helpers._exec = function(options, name, expected) {
var checkOutput = function() {
var div = options.window.document.createElement('div');
output.toDom(div);
var actualOutput = div.textContent.trim();
var nodePromise = converters.convert(output.data, output.type, 'dom',
options.display.requisition.context);
nodePromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
var doTest = function(match, against) {
if (!match.test(against)) {
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
log('Actual textContent');
log(against);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
};
if (typeof expected.output === 'string') {
assert.is(actualOutput,
expected.output,
'html output for ' + name);
}
else if (Array.isArray(expected.output)) {
expected.output.forEach(function(match) {
doTest(match, actualOutput);
});
}
else {
doTest(expected.output, actualOutput);
}
deferred.resolve();
deferred.resolve();
});
};
if (output.completed !== false) {