Commit cc91acb0 authored by Erik Luo's avatar Erik Luo Committed by Commit Bot

DevTools: add runtime experiment for prompt

Bug: 810176
Change-Id: Ic78c5e6ef5557067679f3f4ed2dcf6b76f1ee8e2
Reviewed-on: https://chromium-review.googlesource.com/1014348
Commit-Queue: Erik Luo <luoe@chromium.org>
Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#553228}
parent d744e398
Tests that console fills the empty element below the prompt editor.
Running: testUnsafeExpressions
Expression: "var should_not_be_defined" yielded preview: ""
Expression: "window.prop_should_not_be_set = 1" yielded preview: ""
Evaluating "should_not_be_defined"
value: undefined
exceptionDetails: {
columnNumber : 0
exception : {
className : "ReferenceError"
description : "ReferenceError: should_not_be_defined is not defined
at test://evaluations/0/console-runtime-result-below-prompt.js:20:1"
objectId : <string>
subtype : "error"
type : "object"
}
exceptionId : <number>
lineNumber : 19
scriptId : <string>
stackTrace : {
callFrames : [
{
columnNumber : 0
functionName : ""
lineNumber : 19
scriptId : <string>
url : "test://evaluations/0/console-runtime-result-below-prompt.js"
}
]
}
text : "Uncaught"
}
Evaluating "window.prop_should_not_be_set"
value: undefined
exceptionDetails: undefined
Running: testSafeExpressions
Expression: "1 + 2" yielded preview: "3"
Expression: "123" yielded preview: ""
Running: testNoOpForLongText
Setting max length for evaluation to 0
Expression: "1 + 2" yielded preview: ""
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
(async function() {
TestRunner.addResult(`Tests that console fills the empty element below the prompt editor.\n`);
await TestRunner.loadModule('console_test_runner');
await TestRunner.showPanel('console');
await ConsoleTestRunner.waitUntilConsoleEditorLoaded();
await ConsoleTestRunner.waitForPendingViewportUpdates();
const consoleView = Console.ConsoleView.instance();
const prompt = consoleView._prompt;
TestRunner.runTestSuite([
async function testUnsafeExpressions(next) {
await checkExpression(`var should_not_be_defined`);
await checkExpression(`window.prop_should_not_be_set = 1`);
await evaluateAndDumpResult(`should_not_be_defined`);
await evaluateAndDumpResult(`window.prop_should_not_be_set`);
next();
},
async function testSafeExpressions(next) {
await checkExpression(`1 + 2`);
await checkExpression(`123`);
next();
},
async function testNoOpForLongText(next) {
TestRunner.addResult('Setting max length for evaluation to 0');
const originalMaxLength = Console.ConsolePrompt._MaxLengthForEvaluation;
Console.ConsolePrompt._MaxLengthForEvaluation = 0;
await checkExpression(`1 + 2`);
Console.ConsolePrompt._MaxLengthForEvaluation = originalMaxLength;
next();
},
]);
async function checkExpression(expression) {
prompt.setText(expression);
await prompt._previewRequestForTest;
const previewText = prompt._innerPreviewElement.deepTextContent();
TestRunner.addResult(`Expression: "${expression}" yielded preview: "${previewText}"`);
}
async function evaluateAndDumpResult(expression) {
const customFormatters = {};
for (let name of ['objectId', 'scriptId', 'exceptionId'])
customFormatters[name] = 'formatAsTypeNameOrNull';
TestRunner.addResult(`Evaluating "${expression}"`);
await TestRunner.evaluateInPage(expression, (value, exceptionDetails) => {
TestRunner.dump(value, customFormatters, '', 'value: ');
TestRunner.dump(exceptionDetails, customFormatters, '', 'exceptionDetails: ');
});
}
})();
...@@ -14,6 +14,11 @@ Console.ConsolePrompt = class extends UI.Widget { ...@@ -14,6 +14,11 @@ Console.ConsolePrompt = class extends UI.Widget {
this._editor = null; this._editor = null;
this._isBelowPromptEnabled = Runtime.experiments.isEnabled('consoleBelowPrompt'); this._isBelowPromptEnabled = Runtime.experiments.isEnabled('consoleBelowPrompt');
this._eagerPreviewElement = createElementWithClass('div', 'console-eager-preview'); this._eagerPreviewElement = createElementWithClass('div', 'console-eager-preview');
this._textChangeThrottler = new Common.Throttler(150);
this._formatter = new ObjectUI.RemoteObjectPreviewFormatter();
this._requestPreviewBound = this._requestPreview.bind(this);
this._innerPreviewElement = this._eagerPreviewElement.createChild('div', 'console-eager-inner-preview');
this._eagerPreviewElement.appendChild(UI.Icon.create('smallicon-command-result', 'preview-result-icon'));
this.element.tabIndex = 0; this.element.tabIndex = 0;
...@@ -56,9 +61,56 @@ Console.ConsolePrompt = class extends UI.Widget { ...@@ -56,9 +61,56 @@ Console.ConsolePrompt = class extends UI.Widget {
} }
_onTextChanged() { _onTextChanged() {
// ConsoleView and prompt both use a throttler, so we clear the preview
// ASAP to avoid inconsistency between a fresh viewport and stale preview.
if (this._isBelowPromptEnabled) {
const asSoonAsPossible = !this.text();
this._previewRequestForTest = this._textChangeThrottler.schedule(this._requestPreviewBound, asSoonAsPossible);
}
this.dispatchEventToListeners(Console.ConsolePrompt.Events.TextChanged); this.dispatchEventToListeners(Console.ConsolePrompt.Events.TextChanged);
} }
/**
* @return {!Promise}
*/
async _requestPreview() {
const text = this.text().trim();
const executionContext = UI.context.flavor(SDK.ExecutionContext);
if (!executionContext || !text || text.length > Console.ConsolePrompt._MaxLengthForEvaluation) {
this._innerPreviewElement.removeChildren();
return;
}
const options = {
expression: SDK.RuntimeModel.wrapObjectLiteralExpressionIfNeeded(text),
includeCommandLineAPI: true,
generatePreview: true,
throwOnSideEffect: true,
timeout: 500
};
const result = await executionContext.evaluate(options, true /* userGesture */, false /* awaitPromise */);
this._innerPreviewElement.removeChildren();
if (result.error)
return;
if (result.exceptionDetails) {
const exception = result.exceptionDetails.exception.description;
if (exception.startsWith('TypeError: '))
this._innerPreviewElement.textContent = exception;
return;
}
const {preview, type, subtype, description} = result.object;
if (preview && type === 'object' && subtype !== 'node') {
this._formatter.appendObjectPreview(this._innerPreviewElement, preview, false /* isEntry */);
} else {
const nonObjectPreview = this._formatter.renderPropertyPreview(type, subtype, description.trimEnd(400));
this._innerPreviewElement.appendChild(nonObjectPreview);
}
if (this._innerPreviewElement.deepTextContent() === this.text().trim())
this._innerPreviewElement.removeChildren();
}
/** /**
* @return {!Console.ConsoleHistoryManager} * @return {!Console.ConsoleHistoryManager}
*/ */
...@@ -311,6 +363,12 @@ Console.ConsolePrompt = class extends UI.Widget { ...@@ -311,6 +363,12 @@ Console.ConsolePrompt = class extends UI.Widget {
} }
}; };
/**
* @const
* @type {number}
*/
Console.ConsolePrompt._MaxLengthForEvaluation = 2000;
/** /**
* @unrestricted * @unrestricted
*/ */
......
...@@ -450,5 +450,30 @@ ...@@ -450,5 +450,30 @@
} }
.console-eager-preview { .console-eager-preview {
padding-bottom: 2px;
opacity: 0.6;
position: relative;
height: 15px; height: 15px;
} }
.console-eager-inner-preview {
text-overflow: ellipsis;
overflow: hidden;
margin-left: 4px;
height: 100%;
}
.console-eager-inner-preview * {
white-space: nowrap;
}
.console-eager-inner-preview:empty,
.console-eager-inner-preview:empty + .preview-result-icon {
opacity: 0;
}
.preview-result-icon {
position: absolute;
left: -13px;
top: 1px;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment