diff --git a/front_end/sources/CallStackSidebarPane.js b/front_end/sources/CallStackSidebarPane.js
index 1d3ee45201..d991ee8ad7 100644
--- a/front_end/sources/CallStackSidebarPane.js
+++ b/front_end/sources/CallStackSidebarPane.js
@@ -38,6 +38,7 @@ export default class CallStackSidebarPane extends UI.SimpleView {
this._notPausedMessageElement = this.contentElement.createChild('div', 'gray-info-message');
this._notPausedMessageElement.textContent = Common.UIString('Not paused');
+ this._notPausedMessageElement.tabIndex = -1;
/** @type {!UI.ListModel} */
this._items = new UI.ListModel();
@@ -45,7 +46,13 @@ export default class CallStackSidebarPane extends UI.SimpleView {
this._list = new UI.ListControl(this._items, this, UI.ListMode.NonViewport);
this.contentElement.appendChild(this._list.element);
this._list.element.addEventListener('contextmenu', this._onContextMenu.bind(this), false);
- this._list.element.addEventListener('click', this._onClick.bind(this), false);
+ self.onInvokeElement(this._list.element, event => {
+ const item = this._list.itemForNode(/** @type {?Node} */ (event.target));
+ if (item) {
+ this._activateItem(item);
+ event.consume(true);
+ }
+ });
this._showMoreMessageElement = this._createShowMoreMessageElement();
this._showMoreMessageElement.classList.add('hidden');
@@ -84,6 +91,7 @@ export default class CallStackSidebarPane extends UI.SimpleView {
const details = UI.context.flavor(SDK.DebuggerPausedDetails);
if (!details) {
+ this.setDefaultFocusedElement(this._notPausedMessageElement);
this._notPausedMessageElement.classList.remove('hidden');
this._blackboxedMessageElement.classList.add('hidden');
this._showMoreMessageElement.classList.add('hidden');
@@ -141,6 +149,10 @@ export default class CallStackSidebarPane extends UI.SimpleView {
this._items.replaceAll(items);
if (this._maxAsyncStackChainDepth === _defaultMaxAsyncStackChainDepth) {
this._list.selectNextItem(true /* canWrap */, false /* center */);
+ const selectedItem = this._list.selectedItem();
+ if (selectedItem) {
+ this._activateItem(selectedItem);
+ }
}
this._updatedForTest();
}
@@ -203,9 +215,20 @@ export default class CallStackSidebarPane extends UI.SimpleView {
linkElement.textContent = item.linkText.trimMiddle(30);
linkElement.title = item.linkText;
element.classList.toggle('blackboxed-call-frame', item.isBlackboxed);
+ if (item.isBlackboxed) {
+ UI.ARIAUtils.setDescription(element, ls`blackboxed`);
+ }
+ if (!item[Sources.CallStackSidebarPane._debuggerCallFrameSymbol]) {
+ UI.ARIAUtils.setDisabled(element, true);
+ }
}
+ const isSelected =
+ item[Sources.CallStackSidebarPane._debuggerCallFrameSymbol] === UI.context.flavor(SDK.DebuggerModel.CallFrame);
+ element.classList.toggle('selected', isSelected);
+ UI.ARIAUtils.setSelected(element, isSelected);
element.classList.toggle('hidden', !this._showBlackboxed && item.isBlackboxed);
element.appendChild(UI.Icon.create('smallicon-thick-right-arrow', 'selected-call-frame-icon'));
+ element.tabIndex = item === this._list.selectedItem() ? 0 : -1;
return element;
}
@@ -225,7 +248,7 @@ export default class CallStackSidebarPane extends UI.SimpleView {
* @return {boolean}
*/
isItemSelectable(item) {
- return !!item[_debuggerCallFrameSymbol];
+ return true;
}
/**
@@ -237,13 +260,14 @@ export default class CallStackSidebarPane extends UI.SimpleView {
*/
selectedItemChanged(from, to, fromElement, toElement) {
if (fromElement) {
- fromElement.classList.remove('selected');
+ fromElement.tabIndex = -1;
}
if (toElement) {
- toElement.classList.add('selected');
- }
- if (to) {
- this._activateItem(to);
+ this.setDefaultFocusedElement(toElement);
+ toElement.tabIndex = 0;
+ if (this.hasFocus()) {
+ toElement.focus();
+ }
}
}
@@ -254,7 +278,7 @@ export default class CallStackSidebarPane extends UI.SimpleView {
* @return {boolean}
*/
updateSelectedItemARIA(fromElement, toElement) {
- return false;
+ return true;
}
/**
@@ -265,13 +289,17 @@ export default class CallStackSidebarPane extends UI.SimpleView {
element.createChild('span');
const showAllLink = element.createChild('span', 'link');
showAllLink.textContent = Common.UIString('Show blackboxed frames');
- showAllLink.addEventListener('click', () => {
+ UI.ARIAUtils.markAsLink(showAllLink);
+ showAllLink.tabIndex = 0;
+ const showAll = () => {
this._showBlackboxed = true;
for (const item of this._items) {
this._refreshItem(item);
}
this._blackboxedMessageElement.classList.toggle('hidden', true);
- });
+ };
+ showAllLink.addEventListener('click', showAll);
+ showAllLink.addEventListener('keydown', event => isEnterKey(event) && showAll());
return element;
}
@@ -328,15 +356,34 @@ export default class CallStackSidebarPane extends UI.SimpleView {
if (this._muteActivateItem || !uiLocation) {
return;
}
+ this._list.selectItem(item);
const debuggerCallFrame = item[_debuggerCallFrameSymbol];
- if (debuggerCallFrame && UI.context.flavor(SDK.DebuggerModel.CallFrame) !== debuggerCallFrame) {
+ const oldItem = this.activeCallFrameItem();
+ if (debuggerCallFrame && oldItem !== item) {
debuggerCallFrame.debuggerModel.setSelectedCallFrame(debuggerCallFrame);
UI.context.setFlavor(SDK.DebuggerModel.CallFrame, debuggerCallFrame);
+ if (oldItem) {
+ this._refreshItem(oldItem);
+ }
+ this._refreshItem(item);
} else {
Common.Revealer.reveal(uiLocation);
}
}
+ /**
+ * @return {?Sources.CallStackSidebarPane.Item}
+ */
+ activeCallFrameItem() {
+ const callFrame = UI.context.flavor(SDK.DebuggerModel.CallFrame);
+ if (callFrame) {
+ return this._items.find(
+ callFrameItem => callFrameItem[Sources.CallStackSidebarPane._debuggerCallFrameSymbol] === callFrame) ||
+ null;
+ }
+ return null;
+ }
+
/**
* @param {!UI.ContextMenu} contextMenu
* @param {!Workspace.UISourceCode} uiSourceCode
@@ -374,18 +421,28 @@ export default class CallStackSidebarPane extends UI.SimpleView {
}
}
- /**
- * @return {boolean}
- */
_selectNextCallFrameOnStack() {
- return this._list.selectNextItem(false /* canWrap */, false /* center */);
+ const oldItem = this.activeCallFrameItem();
+ const startIndex = oldItem ? this._items.indexOf(oldItem) + 1 : 0;
+ for (let i = startIndex; i < this._items.length; i++) {
+ const newItem = this._items.at(i);
+ if (newItem[Sources.CallStackSidebarPane._debuggerCallFrameSymbol]) {
+ this._activateItem(newItem);
+ break;
+ }
+ }
}
- /**
- * @return {boolean}
- */
_selectPreviousCallFrameOnStack() {
- return this._list.selectPreviousItem(false /* canWrap */, false /* center */);
+ const oldItem = this.activeCallFrameItem();
+ const startIndex = oldItem ? this._items.indexOf(oldItem) - 1 : this._items.length - 1;
+ for (let i = startIndex; i >= 0; i--) {
+ const newItem = this._items.at(i);
+ if (newItem[Sources.CallStackSidebarPane._debuggerCallFrameSymbol]) {
+ this._activateItem(newItem);
+ break;
+ }
+ }
}
_copyStackTrace() {
diff --git a/front_end/sources/callStackSidebarPane.css b/front_end/sources/callStackSidebarPane.css
index 66d967fc0a..1f0b6c0aaf 100644
--- a/front_end/sources/callStackSidebarPane.css
+++ b/front_end/sources/callStackSidebarPane.css
@@ -16,6 +16,10 @@
margin-left: 5px;
}
+.blackboxed-message > .link:focus {
+ outline-width: unset;
+}
+
.show-more-message {
text-align: center;
font-style: italic;
@@ -49,6 +53,11 @@
background-color: #eee;
}
+.call-frame-item[data-keyboard-focus="true"]:focus,
+.call-frame-item.async-header[data-keyboard-focus="true"]:focus .call-frame-item-title {
+ background-color: var(--focus-bg-color);
+}
+
.async-header + .call-frame-item {
border-top: 0;
}
@@ -71,7 +80,6 @@
border-top: 1px solid #d8d8d8;
margin-top: 8px;
position: absolute;
- z-index: -1;
left: 0;
}
@@ -81,6 +89,7 @@
background-color: white;
margin-left: -5px;
padding: 0 5px;
+ z-index: 1;
}
.blackboxed-call-frame {
diff --git a/front_end/sources/sources_strings.grdp b/front_end/sources/sources_strings.grdp
index f8056236ea..cafd8fe3a6 100644
--- a/front_end/sources/sources_strings.grdp
+++ b/front_end/sources/sources_strings.grdp
@@ -321,6 +321,9 @@
Associated files should be added to the file tree. You can debug these resolved source files as regular JavaScript files.
+
+ blackboxed
+
Pause script execution
diff --git a/front_end/ui/ARIAUtils.js b/front_end/ui/ARIAUtils.js
index f12f171bf3..db315a9602 100644
--- a/front_end/ui/ARIAUtils.js
+++ b/front_end/ui/ARIAUtils.js
@@ -340,6 +340,14 @@ export function setCheckboxAsIndeterminate(element) {
element.setAttribute('aria-checked', 'mixed');
}
+/**
+ * @param {!Element} element
+ * @param {boolean} value
+ */
+export function setDisabled(element, value) {
+ element.setAttribute('aria-disabled', !!value);
+}
+
/**
* @param {!Element} element
* @param {boolean} value