Bug 1033153 - Part 4: Make the console's Object-kind ObjectRenderer display internal promise state. r=jryans

This commit is contained in:
Nick Fitzgerald 2014-10-23 15:53:00 +02:00
parent 3e4e26f0e0
commit acb130b3b2
3 changed files with 186 additions and 70 deletions

View File

@ -1172,17 +1172,15 @@ Messages.Extended.prototype = Heritage.extend(Messages.Simple.prototype,
*/
_renderObjectActor: function(objectActor, options = {})
{
let widget = null;
let {preview} = objectActor;
let widget = Widgets.ObjectRenderers.byClass[objectActor.class];
if (preview && preview.kind) {
let { preview } = objectActor;
if ((!widget || (widget.canRender && !widget.canRender(objectActor)))
&& preview
&& preview.kind) {
widget = Widgets.ObjectRenderers.byKind[preview.kind];
}
if (!widget || (widget.canRender && !widget.canRender(objectActor))) {
widget = Widgets.ObjectRenderers.byClass[objectActor.class];
}
if (!widget || (widget.canRender && !widget.canRender(objectActor))) {
widget = Widgets.JSObject;
}
@ -2299,6 +2297,118 @@ Widgets.JSObject.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
this.element = this._anchor(str, { className: className });
},
/**
* Render a concise representation of an object.
*/
_renderConciseObject: function()
{
this.element = this._anchor(this.objectActor.class,
{ className: "cm-variable" });
},
/**
* Render the `<class> { ` prefix of an object.
*/
_renderObjectPrefix: function()
{
let { kind } = this.objectActor.preview;
this.element = this.el("span.kind-" + kind);
this._anchor(this.objectActor.class, { className: "cm-variable" });
this._text(" { ");
},
/**
* Render the ` }` suffix of an object.
*/
_renderObjectSuffix: function()
{
this._text(" }");
},
/**
* Render an object property.
*
* @param String key
* The property name.
* @param Object value
* The property value, as an RDP grip.
* @param nsIDOMNode container
* The container node to render to.
* @param Boolean needsComma
* True if there was another property before this one and we need to
* separate them with a comma.
* @param Boolean valueIsText
* Add the value as is, don't treat it as a grip and pass it to
* `_renderValueGrip`.
*/
_renderObjectProperty: function(key, value, container, needsComma, valueIsText = false)
{
if (needsComma) {
this._text(", ");
}
container.appendChild(this.el("span.cm-property", key));
this._text(": ");
if (valueIsText) {
this._text(value);
} else {
let valueElem = this.message._renderValueGrip(value, { concise: true });
container.appendChild(valueElem);
}
},
/**
* Render this object's properties.
*
* @param nsIDOMNode container
* The container node to render to.
* @param Boolean needsComma
* True if there was another property before this one and we need to
* separate them with a comma.
*/
_renderObjectProperties: function(container, needsComma)
{
let { preview } = this.objectActor;
let { ownProperties, safeGetterValues } = preview;
let shown = 0;
let getValue = desc => {
if (desc.get) {
return "Getter";
} else if (desc.set) {
return "Setter";
} else {
return desc.value;
}
};
for (let key of Object.keys(ownProperties || {})) {
this._renderObjectProperty(key, getValue(ownProperties[key]), container,
shown > 0 || needsComma,
ownProperties[key].get || ownProperties[key].set);
shown++;
}
let ownPropertiesShown = shown;
for (let key of Object.keys(safeGetterValues || {})) {
this._renderObjectProperty(key, safeGetterValues[key].getterValue,
container, shown > 0 || needsComma);
shown++;
}
if (typeof preview.ownPropertiesLength == "number" &&
ownPropertiesShown < preview.ownPropertiesLength) {
this._text(", ");
let n = preview.ownPropertiesLength - ownPropertiesShown;
let str = VariablesView.stringifiers._getNMoreString(n);
this._anchor(str);
}
},
/**
* Render an anchor with a given text content and link.
*
@ -3085,6 +3195,42 @@ Widgets.ObjectRenderers.add({
},
}); // Widgets.ObjectRenderers.byKind.DOMNode
/**
* The widget user for displaying Promise objects.
*/
Widgets.ObjectRenderers.add({
byClass: "Promise",
render: function()
{
let { ownProperties, safeGetterValues } = this.objectActor.preview;
if ((!ownProperties && !safeGetterValues) || this.options.concise) {
this._renderConciseObject();
return;
}
this._renderObjectPrefix();
let container = this.element;
let addedPromiseInternalProps = false;
if (this.objectActor.promiseState) {
const { state, value, reason } = this.objectActor.promiseState;
this._renderObjectProperty("<state>", state, container, false);
addedPromiseInternalProps = true;
if (state == "fulfilled") {
this._renderObjectProperty("<value>", value, container, true);
} else if (state == "rejected") {
this._renderObjectProperty("<reason>", reason, container, true);
}
}
this._renderObjectProperties(container, addedPromiseInternalProps);
this._renderObjectSuffix();
}
}); // Widgets.ObjectRenderers.byClass.Promise
/**
* The widget used for displaying generic JS object previews.
*/
@ -3093,73 +3239,15 @@ Widgets.ObjectRenderers.add({
render: function()
{
let {preview} = this.objectActor;
let {ownProperties, safeGetterValues} = preview;
let { ownProperties, safeGetterValues } = this.objectActor.preview;
if ((!ownProperties && !safeGetterValues) || this.options.concise) {
this.element = this._anchor(this.objectActor.class,
{ className: "cm-variable" });
this._renderConciseObject();
return;
}
let container = this.element = this.el("span.kind-" + preview.kind);
this._anchor(this.objectActor.class, { className: "cm-variable" });
this._text(" { ");
let addProperty = (str) => {
container.appendChild(this.el("span.cm-property", str));
};
let shown = 0;
for (let key of Object.keys(ownProperties || {})) {
if (shown > 0) {
this._text(", ");
}
let value = ownProperties[key];
addProperty(key);
this._text(": ");
if (value.get) {
addProperty("Getter");
} else if (value.set) {
addProperty("Setter");
} else {
let valueElem = this.message._renderValueGrip(value.value, { concise: true });
container.appendChild(valueElem);
}
shown++;
}
let ownPropertiesShown = shown;
for (let key of Object.keys(safeGetterValues || {})) {
if (shown > 0) {
this._text(", ");
}
addProperty(key);
this._text(": ");
let value = safeGetterValues[key].getterValue;
let valueElem = this.message._renderValueGrip(value, { concise: true });
container.appendChild(valueElem);
shown++;
}
if (typeof preview.ownPropertiesLength == "number" &&
ownPropertiesShown < preview.ownPropertiesLength) {
this._text(", ");
let n = preview.ownPropertiesLength - ownPropertiesShown;
let str = VariablesView.stringifiers._getNMoreString(n);
this._anchor(str);
}
this._text(" }");
this._renderObjectPrefix();
this._renderObjectProperties(this.element, false);
this._renderObjectSuffix();
},
}); // Widgets.ObjectRenderers.byKind.Object

View File

@ -84,6 +84,26 @@ let inputTests = [
inspectable: true,
variablesViewLabel: "String[5]"
},
// 9
{
// XXX: Can't test fulfilled and rejected promises, because promises get
// settled on the next tick of the event loop.
input: "new Promise(function () {})",
output: 'Promise { <state>: "pending" }',
printOutput: "[object Promise]",
inspectable: true,
variablesViewLabel: "Promise"
},
// 10
{
input: "(function () { var p = new Promise(function () {}); p.foo = 1; return p; }())",
output: 'Promise { <state>: "pending", foo: 1 }',
printOutput: "[object Promise]",
inspectable: true,
variablesViewLabel: "Promise"
}
];
function test() {

View File

@ -946,6 +946,8 @@ function openDebugger(aOptions = {})
*/
function waitForMessages(aOptions)
{
info("Waiting for messages...");
gPendingOutputTest++;
let webconsole = aOptions.webconsole;
let rules = WebConsoleUtils.cloneObject(aOptions.messages, true);
@ -1473,6 +1475,7 @@ function checkOutputForInputs(hud, inputTests)
function* checkConsoleLog(entry)
{
info("Logging: " + entry.input);
hud.jsterm.clearOutput();
hud.jsterm.execute("console.log(" + entry.input + ")");
@ -1494,6 +1497,7 @@ function checkOutputForInputs(hud, inputTests)
function checkPrintOutput(entry)
{
info("Printing: " + entry.input);
hud.jsterm.clearOutput();
hud.jsterm.execute("print(" + entry.input + ")");
@ -1511,6 +1515,7 @@ function checkOutputForInputs(hud, inputTests)
function* checkJSEval(entry)
{
info("Evaluating: " + entry.input);
hud.jsterm.clearOutput();
hud.jsterm.execute(entry.input);
@ -1534,6 +1539,7 @@ function checkOutputForInputs(hud, inputTests)
function* checkObjectClick(entry, msg)
{
info("Clicking: " + entry.input);
let body = msg.querySelector(".message-body a") ||
msg.querySelector(".message-body");
ok(body, "the message body");
@ -1569,6 +1575,7 @@ function checkOutputForInputs(hud, inputTests)
function checkLinkToInspector(entry, msg)
{
info("Checking Inspector Link: " + entry.input);
let elementNodeWidget = [...msg._messageObject.widgets][0];
if (!elementNodeWidget) {
ok(!entry.inspectorIcon, "The message has no ElementNode widget");
@ -1592,6 +1599,7 @@ function checkOutputForInputs(hud, inputTests)
function onVariablesViewOpen(entry, {resolve, reject}, event, view, options)
{
info("Variables view opened: " + entry.input);
let label = entry.variablesViewLabel || entry.output;
if (typeof label == "string" && options.label != label) {
return;