Commit b43d0d5f authored by Brandon Goddard's avatar Brandon Goddard Committed by Commit Bot

Devtools: Add Focus and Keyboard navigation to Memory allocation stack

This change adds the ability to navigate to the allocation stacks in
the memory tool from a keyboard. It uses arrow keys to navigate the stack,
and the links can be accessed from pressing enter when the item has focus,
or from a context menu for consistency with other callstacks with links.

Before: https://imgur.com/gz1mCyY

**Note context menu text has been updated since recording After gif. It now
reads 'Reveal in Sources panel'
After: https://imgur.com/aJJgco0

Bug: 963183
Change-Id: I0babaab431c60913f94eca7f0fee9bb0e8c25532
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1696400
Commit-Queue: Brandon Goddard <brgoddar@microsoft.com>
Reviewed-by: default avatarLorne Mitchell <lomitch@microsoft.com>
Reviewed-by: default avatarJeff Fisher <jeffish@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#706660}
parent 653f97ce
...@@ -527,7 +527,15 @@ export default class Linkifier { ...@@ -527,7 +527,15 @@ export default class Linkifier {
if (UI.isBeingEdited(/** @type {!Node} */ (event.target)) || link.hasSelection()) { if (UI.isBeingEdited(/** @type {!Node} */ (event.target)) || link.hasSelection()) {
return false; return false;
} }
const actions = Linkifier._linkActions(link); return Components.Linkifier.invokeFirstAction(link);
}
/**
* @param {!Element} link
* @return {boolean}
*/
static invokeFirstAction(link) {
const actions = Components.Linkifier._linkActions(link);
if (actions.length) { if (actions.length) {
actions[0].handler.call(null); actions[0].handler.call(null);
return true; return true;
......
...@@ -1757,6 +1757,65 @@ Profiler.HeapAllocationStackView = class extends UI.Widget { ...@@ -1757,6 +1757,65 @@ Profiler.HeapAllocationStackView = class extends UI.Widget {
super(); super();
this._heapProfilerModel = heapProfilerModel; this._heapProfilerModel = heapProfilerModel;
this._linkifier = new Components.Linkifier(); this._linkifier = new Components.Linkifier();
/** @type {!Array<!Element>} */
this._frameElements = [];
}
/**
* @param {!Element} link
* @param {!Event} event
*/
_onContextMenu(link, event) {
const contextMenu = new UI.ContextMenu(event);
if (!contextMenu.containsTarget(link)) {
contextMenu.appendApplicableItems(link);
}
contextMenu.show();
event.consume(true);
}
/**
* @param {!Event} event
*/
_onStackViewKeydown(event) {
const target = /** @type {?Element} */ (event.target);
if (!target) {
return;
}
if (isEnterKey(event)) {
const link = target._linkElement;
if (!link) {
return;
}
if (Components.Linkifier.invokeFirstAction(link)) {
event.consume(true);
}
return;
}
let navDown;
if (event.key === 'ArrowUp') {
navDown = false;
} else if (event.key === 'ArrowDown') {
navDown = true;
} else {
return;
}
const index = this._frameElements.indexOf(target);
if (index === -1) {
return;
}
const nextIndex = navDown ? index + 1 : index - 1;
if (nextIndex < 0 || nextIndex >= this._frameElements.length) {
return;
}
const nextFrame = this._frameElements[nextIndex];
nextFrame.tabIndex = 0;
target.tabIndex = -1;
nextFrame.focus();
event.consume(true);
} }
/** /**
...@@ -1775,8 +1834,11 @@ Profiler.HeapAllocationStackView = class extends UI.Widget { ...@@ -1775,8 +1834,11 @@ Profiler.HeapAllocationStackView = class extends UI.Widget {
} }
const stackDiv = this.element.createChild('div', 'heap-allocation-stack'); const stackDiv = this.element.createChild('div', 'heap-allocation-stack');
stackDiv.addEventListener('keydown', this._onStackViewKeydown.bind(this), false);
for (const frame of frames) { for (const frame of frames) {
const frameDiv = stackDiv.createChild('div', 'stack-frame'); const frameDiv = stackDiv.createChild('div', 'stack-frame');
this._frameElements.push(frameDiv);
frameDiv.tabIndex = -1;
const name = frameDiv.createChild('div'); const name = frameDiv.createChild('div');
name.textContent = UI.beautifyFunctionName(frame.functionName); name.textContent = UI.beautifyFunctionName(frame.functionName);
if (!frame.scriptId) { if (!frame.scriptId) {
...@@ -1786,11 +1848,15 @@ Profiler.HeapAllocationStackView = class extends UI.Widget { ...@@ -1786,11 +1848,15 @@ Profiler.HeapAllocationStackView = class extends UI.Widget {
this._heapProfilerModel ? this._heapProfilerModel.target() : null, String(frame.scriptId), frame.scriptName, this._heapProfilerModel ? this._heapProfilerModel.target() : null, String(frame.scriptId), frame.scriptName,
frame.line - 1, frame.column - 1); frame.line - 1, frame.column - 1);
frameDiv.appendChild(urlElement); frameDiv.appendChild(urlElement);
frameDiv._linkElement = urlElement;
frameDiv.addEventListener('contextmenu', this._onContextMenu.bind(this, urlElement));
} }
this._frameElements[0].tabIndex = 0;
} }
clear() { clear() {
this.element.removeChildren(); this.element.removeChildren();
this._frameElements = [];
this._linkifier.reset(); this._linkifier.reset();
} }
}; };
...@@ -189,10 +189,27 @@ ...@@ -189,10 +189,27 @@
padding: 2px; padding: 2px;
} }
.heap-allocation-stack .stack-frame:hover:not(:focus) {
background-color: rgba(0, 0, 0, 0.1);
}
.heap-allocation-stack .stack-frame:focus {
background-color: var(--selection-bg-color);
color: var(--selection-fg-color);
}
.heap-allocation-stack .stack-frame:focus:hover {
background-color: var(--accent-color-hover);
}
.heap-allocation-stack .stack-frame .devtools-link { .heap-allocation-stack .stack-frame .devtools-link {
color: rgb(33%, 33%, 33%); color: rgb(33%, 33%, 33%);
} }
.heap-allocation-stack .stack-frame:focus .devtools-link {
color: var(--selection-fg-color);
}
.no-heap-allocation-stack { .no-heap-allocation-stack {
padding: 5px; padding: 5px;
} }
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