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

DevTools: allow storing DOM nodes as global variables

Adds a context menu item onto DOM nodes and elements to store as a
global `tempN` variable.

Screenshot: https://imgur.com/a/5ph3Rqj

Bug: 867855
Change-Id: If76a5c85ec47dae3df6fcb95d843cb4663efbb42
Reviewed-on: https://chromium-review.googlesource.com/1217745Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Commit-Queue: Erik Luo <luoe@chromium.org>
Cr-Commit-Position: refs/heads/master@{#590394}
parent 9eda51db
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
function didEvaluate(result) { function didEvaluate(result) {
TestRunner.assertTrue(!result.exceptionDetails, 'FAIL: was thrown. Expression: ' + expression); TestRunner.assertTrue(!result.exceptionDetails, 'FAIL: was thrown. Expression: ' + expression);
UI.panels.sources._saveToTempVariable(result.object); SDK.consoleModel.saveToTempVariable(UI.context.flavor(SDK.ExecutionContext), result.object);
ConsoleTestRunner.waitUntilNthMessageReceived(2, evaluateNext); ConsoleTestRunner.waitUntilNthMessageReceived(2, evaluateNext);
} }
......
Tests saving nodes to temporary variables.
Message count: 2
temp1
<div id="node"></div>
// 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 saving nodes to temporary variables.\n`);
await TestRunner.loadModule('console_test_runner');
await TestRunner.loadModule('elements_test_runner');
await TestRunner.showPanel('elements');
await TestRunner.loadHTML(`<div id="node"></div>`);
const node = await ElementsTestRunner.nodeWithIdPromise('node');
ElementsTestRunner.firstElementsTreeOutline()._saveNodeToTempVariable(node);
await ConsoleTestRunner.waitForConsoleMessagesPromise(2);
const secondMessage = Console.ConsoleView.instance()._visibleViewMessages[1];
await TestRunner.addSnifferPromise(secondMessage, '_formattedParameterAsNodeForTest');
ConsoleTestRunner.dumpConsoleMessages();
TestRunner.completeTest();
})();
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
function didEvaluate(result) { function didEvaluate(result) {
TestRunner.assertTrue(!result.exceptionDetails, 'FAIL: was thrown. Expression: ' + expression); TestRunner.assertTrue(!result.exceptionDetails, 'FAIL: was thrown. Expression: ' + expression);
UI.panels.sources._saveToTempVariable(result.object); SDK.consoleModel.saveToTempVariable(UI.context.flavor(SDK.ExecutionContext), result.object);
ConsoleTestRunner.waitUntilNthMessageReceived(2, evaluateNext); ConsoleTestRunner.waitUntilNthMessageReceived(2, evaluateNext);
} }
......
...@@ -758,6 +758,8 @@ Elements.ElementsTreeOutline = class extends UI.TreeOutline { ...@@ -758,6 +758,8 @@ Elements.ElementsTreeOutline = class extends UI.TreeOutline {
if (textNode && textNode.classList.contains('bogus')) if (textNode && textNode.classList.contains('bogus'))
textNode = null; textNode = null;
const commentNode = event.target.enclosingNodeOrSelfWithClass('webkit-html-comment'); const commentNode = event.target.enclosingNodeOrSelfWithClass('webkit-html-comment');
contextMenu.saveSection().appendItem(
ls`Store as global variable`, this._saveNodeToTempVariable.bind(this, treeElement.node()));
if (textNode) if (textNode)
treeElement.populateTextContextMenu(contextMenu, textNode); treeElement.populateTextContextMenu(contextMenu, textNode);
else if (isTag) else if (isTag)
...@@ -771,6 +773,14 @@ Elements.ElementsTreeOutline = class extends UI.TreeOutline { ...@@ -771,6 +773,14 @@ Elements.ElementsTreeOutline = class extends UI.TreeOutline {
contextMenu.show(); contextMenu.show();
} }
/**
* @param {!SDK.DOMNode} node
*/
async _saveNodeToTempVariable(node) {
const remoteObjectForConsole = await node.resolveToObject();
await SDK.consoleModel.saveToTempVariable(UI.context.flavor(SDK.ExecutionContext), remoteObjectForConsole);
}
runPendingUpdates() { runPendingUpdates() {
this._updateModifiedNodes(); this._updateModifiedNodes();
} }
......
...@@ -35,6 +35,14 @@ ElementsTestRunner.nodeWithId = function(idValue, callback) { ...@@ -35,6 +35,14 @@ ElementsTestRunner.nodeWithId = function(idValue, callback) {
ElementsTestRunner.findNode(node => node.getAttribute('id') === idValue, callback); ElementsTestRunner.findNode(node => node.getAttribute('id') === idValue, callback);
}; };
/**
* @param {string} idValue
* @param {!Function} callback
*/
ElementsTestRunner.nodeWithIdPromise = function(idValue) {
return new Promise(resolve => ElementsTestRunner.findNode(node => node.getAttribute('id') === idValue, resolve));
};
/** /**
* @param {function(!Element): boolean} matchFunction * @param {function(!Element): boolean} matchFunction
* @param {!Function} callback * @param {!Function} callback
......
...@@ -356,6 +356,63 @@ SDK.ConsoleModel = class extends Common.Object { ...@@ -356,6 +356,63 @@ SDK.ConsoleModel = class extends Common.Object {
warnings() { warnings() {
return this._warnings; return this._warnings;
} }
/**
* @param {?SDK.ExecutionContext} currentExecutionContext
* @param {?SDK.RemoteObject} remoteObject
*/
async saveToTempVariable(currentExecutionContext, remoteObject) {
if (!remoteObject || !currentExecutionContext) {
failedToSave(null);
return;
}
const executionContext = /** @type {!SDK.ExecutionContext} */ (currentExecutionContext);
const result = await executionContext.globalObject(/* objectGroup */ '', /* generatePreview */ false);
if (!!result.exceptionDetails || !result.object) {
failedToSave(result.object || null);
return;
}
const globalObject = result.object;
const callFunctionResult =
await globalObject.callFunctionPromise(saveVariable, [SDK.RemoteObject.toCallArgument(remoteObject)]);
globalObject.release();
if (callFunctionResult.wasThrown || !callFunctionResult.object || callFunctionResult.object.type !== 'string') {
failedToSave(callFunctionResult.object || null);
} else {
const text = /** @type {string} */ (callFunctionResult.object.value);
const message = this.addCommandMessage(executionContext, text);
this.evaluateCommandInConsole(
executionContext, message, text, /* useCommandLineAPI */ false, /* awaitPromise */ false);
}
if (callFunctionResult.object)
callFunctionResult.object.release();
/**
* @suppressReceiverCheck
* @this {Window}
*/
function saveVariable(value) {
const prefix = 'temp';
let index = 1;
while ((prefix + index) in this)
++index;
const name = prefix + index;
this[name] = value;
return name;
}
/**
* @param {?SDK.RemoteObject} result
*/
function failedToSave(result) {
let message = Common.UIString('Failed to save to temp variable.');
if (result)
message += ' ' + result.description;
Common.console.error(message);
}
}
}; };
/** @enum {symbol} */ /** @enum {symbol} */
......
...@@ -811,11 +811,12 @@ Sources.SourcesPanel = class extends UI.Panel { ...@@ -811,11 +811,12 @@ Sources.SourcesPanel = class extends UI.Panel {
if (!(target instanceof SDK.RemoteObject)) if (!(target instanceof SDK.RemoteObject))
return; return;
const remoteObject = /** @type {!SDK.RemoteObject} */ (target); const remoteObject = /** @type {!SDK.RemoteObject} */ (target);
const executionContext = UI.context.flavor(SDK.ExecutionContext);
contextMenu.debugSection().appendItem( contextMenu.debugSection().appendItem(
Common.UIString('Store as global variable'), this._saveToTempVariable.bind(this, remoteObject)); ls`Store as global variable`, () => SDK.consoleModel.saveToTempVariable(executionContext, remoteObject));
if (remoteObject.type === 'function') { if (remoteObject.type === 'function') {
contextMenu.debugSection().appendItem( contextMenu.debugSection().appendItem(
Common.UIString('Show function definition'), this._showFunctionDefinition.bind(this, remoteObject)); ls`Show function definition`, this._showFunctionDefinition.bind(this, remoteObject));
} }
} }
...@@ -834,63 +835,6 @@ Sources.SourcesPanel = class extends UI.Panel { ...@@ -834,63 +835,6 @@ Sources.SourcesPanel = class extends UI.Panel {
contextMenu.revealSection().appendItem(openText, this.showUILocation.bind(this, uiSourceCode.uiLocation(0, 0))); contextMenu.revealSection().appendItem(openText, this.showUILocation.bind(this, uiSourceCode.uiLocation(0, 0)));
} }
/**
* @param {!SDK.RemoteObject} remoteObject
*/
async _saveToTempVariable(remoteObject) {
const currentExecutionContext = UI.context.flavor(SDK.ExecutionContext);
if (!currentExecutionContext)
return;
const result = await currentExecutionContext.globalObject(/* objectGroup */ '', /* generatePreview */ false);
if (!!result.exceptionDetails || !result.object) {
failedToSave(result.object || null);
return;
}
const globalObject = result.object;
const callFunctionResult =
await globalObject.callFunctionPromise(saveVariable, [SDK.RemoteObject.toCallArgument(remoteObject)]);
globalObject.release();
if (callFunctionResult.wasThrown || !callFunctionResult.object || callFunctionResult.object.type !== 'string') {
failedToSave(callFunctionResult.object || null);
} else {
const executionContext = /** @type {!SDK.ExecutionContext} */ (currentExecutionContext);
let text = /** @type {string} */ (callFunctionResult.object.value);
const message = SDK.consoleModel.addCommandMessage(executionContext, text);
text = ObjectUI.JavaScriptREPL.wrapObjectLiteral(text);
SDK.consoleModel.evaluateCommandInConsole(
executionContext, message, text,
/* useCommandLineAPI */ false, /* awaitPromise */ false);
}
if (callFunctionResult.object)
callFunctionResult.object.release();
/**
* @suppressReceiverCheck
* @this {Window}
*/
function saveVariable(value) {
const prefix = 'temp';
let index = 1;
while ((prefix + index) in this)
++index;
const name = prefix + index;
this[name] = value;
return name;
}
/**
* @param {?SDK.RemoteObject} result
*/
function failedToSave(result) {
let message = Common.UIString('Failed to save to temp variable.');
if (result)
message += ' ' + result.description;
Common.console.error(message);
}
}
/** /**
* @param {!SDK.RemoteObject} remoteObject * @param {!SDK.RemoteObject} remoteObject
*/ */
......
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