mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-12 17:45:49 +08:00
Toggle show ignore-listed call frames
This changes the way ignore-listing is disabled in the call stack. In place of the link that previously allowed ignore-listing to be turned off (but not back on), we now have a checkbox allowing the ignored frames to be unhidden and then hidden again. Bug: 1336604 Change-Id: Ie42dbe4884c571bf0a696c83c2695610f0ff53e8 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3769849 Reviewed-by: Victor Porof <victorporof@chromium.org> Commit-Queue: Victor Porof <victorporof@chromium.org> Reviewed-by: Danil Somsikov <dsv@chromium.org>
This commit is contained in:
committed by
Devtools-frontend LUCI CQ
parent
ad851c2381
commit
a6533f0ac1
@@ -107,6 +107,7 @@ let callstackSidebarPaneInstance: CallStackSidebarPane;
|
||||
export class CallStackSidebarPane extends UI.View.SimpleView implements UI.ContextFlavorListener.ContextFlavorListener,
|
||||
UI.ListControl.ListDelegate<Item> {
|
||||
private readonly ignoreListMessageElement: Element;
|
||||
private readonly ignoreListCheckboxElement: HTMLInputElement;
|
||||
private readonly notPausedMessageElement: HTMLElement;
|
||||
private readonly callFrameWarningsElement: HTMLElement;
|
||||
private readonly items: UI.ListModel.ListModel<Item>;
|
||||
@@ -124,14 +125,15 @@ export class CallStackSidebarPane extends UI.View.SimpleView implements UI.Conte
|
||||
private constructor() {
|
||||
super(i18nString(UIStrings.callStack), true);
|
||||
|
||||
this.ignoreListMessageElement = this.createIgnoreListMessageElement();
|
||||
({element: this.ignoreListMessageElement, checkbox: this.ignoreListCheckboxElement} =
|
||||
this.createIgnoreListMessageElementAndCheckbox());
|
||||
this.contentElement.appendChild(this.ignoreListMessageElement);
|
||||
|
||||
this.notPausedMessageElement = this.contentElement.createChild('div', 'gray-info-message');
|
||||
this.notPausedMessageElement.textContent = i18nString(UIStrings.notPaused);
|
||||
this.notPausedMessageElement.tabIndex = -1;
|
||||
|
||||
this.callFrameWarningsElement = this.contentElement.createChild('div', 'ignore-listed-message');
|
||||
this.callFrameWarningsElement = this.contentElement.createChild('div', 'call-frame-warnings-message');
|
||||
const icon = UI.Icon.Icon.create('smallicon-warning', 'call-frame-warning-icon');
|
||||
this.callFrameWarningsElement.appendChild(icon);
|
||||
this.callFrameWarningsElement.appendChild(document.createTextNode(i18nString(UIStrings.callFrameWarnings)));
|
||||
@@ -180,6 +182,7 @@ export class CallStackSidebarPane extends UI.View.SimpleView implements UI.Conte
|
||||
|
||||
flavorChanged(_object: Object|null): void {
|
||||
this.showIgnoreListed = false;
|
||||
this.ignoreListCheckboxElement.checked = false;
|
||||
this.maxAsyncStackChainDepth = defaultMaxAsyncStackChainDepth;
|
||||
this.update();
|
||||
}
|
||||
@@ -278,6 +281,9 @@ export class CallStackSidebarPane extends UI.View.SimpleView implements UI.Conte
|
||||
}
|
||||
this.showMoreMessageElement.classList.toggle('hidden', !asyncStackTrace);
|
||||
this.items.replaceAll(items);
|
||||
for (const item of this.items) {
|
||||
this.refreshItem(item);
|
||||
}
|
||||
if (this.maxAsyncStackChainDepth === defaultMaxAsyncStackChainDepth) {
|
||||
this.list.selectNextItem(true /* canWrap */, false /* center */);
|
||||
const selectedItem = this.list.selectedItem();
|
||||
@@ -305,6 +311,7 @@ export class CallStackSidebarPane extends UI.View.SimpleView implements UI.Conte
|
||||
}
|
||||
this.ignoreListMessageElement.classList.toggle('hidden', true);
|
||||
} else {
|
||||
this.showIgnoreListed = this.ignoreListCheckboxElement.checked;
|
||||
const itemsSet = new Set<Item>(items);
|
||||
let hasIgnoreListed = false;
|
||||
for (let i = 0; i < this.items.length; ++i) {
|
||||
@@ -314,7 +321,7 @@ export class CallStackSidebarPane extends UI.View.SimpleView implements UI.Conte
|
||||
}
|
||||
hasIgnoreListed = hasIgnoreListed || item.isIgnoreListed;
|
||||
}
|
||||
this.ignoreListMessageElement.classList.toggle('hidden', this.showIgnoreListed || !hasIgnoreListed);
|
||||
this.ignoreListMessageElement.classList.toggle('hidden', !hasIgnoreListed);
|
||||
}
|
||||
delete this.muteActivateItem;
|
||||
});
|
||||
@@ -387,24 +394,24 @@ export class CallStackSidebarPane extends UI.View.SimpleView implements UI.Conte
|
||||
return true;
|
||||
}
|
||||
|
||||
private createIgnoreListMessageElement(): Element {
|
||||
private createIgnoreListMessageElementAndCheckbox(): {element: Element, checkbox: HTMLInputElement} {
|
||||
const element = document.createElement('div');
|
||||
element.classList.add('ignore-listed-message');
|
||||
element.createChild('span');
|
||||
const showAllLink = element.createChild('span', 'link');
|
||||
showAllLink.textContent = i18nString(UIStrings.showIgnorelistedFrames);
|
||||
UI.ARIAUtils.markAsLink(showAllLink);
|
||||
showAllLink.tabIndex = 0;
|
||||
const label = element.createChild('label');
|
||||
label.classList.add('ignore-listed-message-label');
|
||||
const checkbox = label.createChild('input') as HTMLInputElement;
|
||||
checkbox.tabIndex = 0;
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.classList.add('ignore-listed-checkbox');
|
||||
label.append(i18nString(UIStrings.showIgnorelistedFrames));
|
||||
const showAll = (): void => {
|
||||
this.showIgnoreListed = true;
|
||||
this.showIgnoreListed = checkbox.checked;
|
||||
for (const item of this.items) {
|
||||
this.refreshItem(item);
|
||||
}
|
||||
this.ignoreListMessageElement.classList.toggle('hidden', true);
|
||||
};
|
||||
showAllLink.addEventListener('click', showAll);
|
||||
showAllLink.addEventListener('keydown', event => event.key === 'Enter' && showAll());
|
||||
return element;
|
||||
checkbox.addEventListener('click', showAll);
|
||||
return {element, checkbox};
|
||||
}
|
||||
|
||||
private createShowMoreMessageElement(): Element {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
.ignore-listed-message {
|
||||
.call-frame-warnings-message {
|
||||
--override-ignore-message-background-color: #ffffc2;
|
||||
|
||||
text-align: center;
|
||||
@@ -14,6 +14,16 @@
|
||||
background-color: var(--override-ignore-message-background-color);
|
||||
}
|
||||
|
||||
.ignore-listed-message {
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.ignore-listed-message-label {
|
||||
color: var(--color-text-secondary);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.-theme-with-dark-background .ignore-listed-message,
|
||||
:host-context(.-theme-with-dark-background) .ignore-listed-message {
|
||||
--override-ignore-message-background-color: rgb(72 72 0);
|
||||
@@ -23,14 +33,6 @@
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.ignore-listed-message > .link {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.ignore-listed-message > .link:focus {
|
||||
outline-width: unset;
|
||||
}
|
||||
|
||||
.show-more-message {
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
@@ -80,6 +82,10 @@
|
||||
background-color: var(--legacy-focus-bg-color);
|
||||
}
|
||||
|
||||
.ignore-listed-checkbox:focus-visible {
|
||||
outline-width: unset;
|
||||
}
|
||||
|
||||
.call-frame-item:not(.async-header):hover {
|
||||
background-color: var(--color-background-elevation-1);
|
||||
}
|
||||
|
||||
@@ -499,7 +499,7 @@ describe('The Debugger Language Plugins', async () => {
|
||||
const text = await details.evaluate(e => e.textContent);
|
||||
assert.deepEqual(text, 'Failed to load debug file "test.wasm".');
|
||||
|
||||
const banners = await $$('.ignore-listed-message');
|
||||
const banners = await $$('.call-frame-warnings-message');
|
||||
const bannerTexts = await Promise.all(banners.map(e => e.evaluate(e => e.textContent)));
|
||||
assert.include(bannerTexts, 'Some call frames have warnings');
|
||||
|
||||
@@ -569,7 +569,7 @@ describe('The Debugger Language Plugins', async () => {
|
||||
const text = await details.evaluate(e => e.textContent);
|
||||
assert.deepEqual(text, 'Failed to load debug file "test.dwo".');
|
||||
|
||||
const banners = await $$('.ignore-listed-message');
|
||||
const banners = await $$('.call-frame-warnings-message');
|
||||
const bannerTexts = await Promise.all(banners.map(e => e.evaluate(e => e.textContent)));
|
||||
assert.include(bannerTexts, 'Some call frames have warnings');
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ export const openSettingsTab = async (tabTitle: string) => {
|
||||
await waitFor(panelSelector);
|
||||
};
|
||||
|
||||
export const closeSettings = async () => {
|
||||
await click('.dialog-close-button');
|
||||
};
|
||||
|
||||
export const togglePreferenceInSettingsTab = async (label: string) => {
|
||||
await openSettingsTab('Preferences');
|
||||
|
||||
@@ -55,5 +59,16 @@ export const togglePreferenceInSettingsTab = async (label: string) => {
|
||||
return newValue !== value;
|
||||
});
|
||||
|
||||
await click('.dialog-close-button');
|
||||
await closeSettings();
|
||||
};
|
||||
|
||||
export const setIgnoreListPattern = async (pattern: string) => {
|
||||
await openSettingsTab('Ignore List');
|
||||
await click('[aria-label="Add filename pattern"]');
|
||||
const textBox = await waitFor('[aria-label="Pattern"]');
|
||||
await click(textBox);
|
||||
await textBox.type(pattern);
|
||||
await textBox.type('\n');
|
||||
await waitFor(`[title="Ignore scripts whose names match '${pattern}'"]`);
|
||||
await closeSettings();
|
||||
};
|
||||
|
||||
@@ -280,8 +280,9 @@ export async function executionLineHighlighted() {
|
||||
}
|
||||
|
||||
export async function getCallFrameNames() {
|
||||
await waitFor('.call-frame-item-title');
|
||||
const items = await $$('.call-frame-item-title');
|
||||
const selector = '.call-frame-item:not(.hidden) .call-frame-item-title';
|
||||
await waitFor(selector);
|
||||
const items = await $$(selector);
|
||||
const promises = items.map(handle => handle.evaluate(el => el.textContent as string));
|
||||
const results = [];
|
||||
for (const promise of promises) {
|
||||
@@ -291,8 +292,9 @@ export async function getCallFrameNames() {
|
||||
}
|
||||
|
||||
export async function getCallFrameLocations() {
|
||||
await waitFor('.call-frame-location');
|
||||
const items = await $$('.call-frame-location');
|
||||
const selector = '.call-frame-item:not(.hidden) .call-frame-location';
|
||||
await waitFor(selector);
|
||||
const items = await $$(selector);
|
||||
const promises = items.map(handle => handle.evaluate(el => el.textContent as string));
|
||||
const results = [];
|
||||
for (const promise of promises) {
|
||||
@@ -484,10 +486,35 @@ export async function readSourcesTreeView(): Promise<string[]> {
|
||||
return results.map(item => item.replace(/localhost:[0-9]+/, 'localhost:XXXX'));
|
||||
}
|
||||
|
||||
async function hasPausedEvents(frontend: puppeteer.Page): Promise<boolean> {
|
||||
const events = await getPendingEvents(frontend, DEBUGGER_PAUSED_EVENT);
|
||||
return Boolean(events && events.length);
|
||||
}
|
||||
|
||||
export async function stepThroughTheCode() {
|
||||
const {frontend} = getBrowserAndPages();
|
||||
await getPendingEvents(frontend, DEBUGGER_PAUSED_EVENT);
|
||||
await frontend.keyboard.press('F9');
|
||||
await waitForFunction(() => getPendingEvents(frontend, DEBUGGER_PAUSED_EVENT));
|
||||
await waitForFunction(() => hasPausedEvents(frontend));
|
||||
await waitFor(PAUSE_INDICATOR_SELECTOR);
|
||||
}
|
||||
|
||||
export async function stepIn() {
|
||||
const {frontend} = getBrowserAndPages();
|
||||
await getPendingEvents(frontend, DEBUGGER_PAUSED_EVENT);
|
||||
await frontend.keyboard.press('F11');
|
||||
await waitForFunction(() => hasPausedEvents(frontend));
|
||||
await waitFor(PAUSE_INDICATOR_SELECTOR);
|
||||
}
|
||||
|
||||
export async function stepOut() {
|
||||
const {frontend} = getBrowserAndPages();
|
||||
await getPendingEvents(frontend, DEBUGGER_PAUSED_EVENT);
|
||||
await frontend.keyboard.down('Shift');
|
||||
await frontend.keyboard.press('F11');
|
||||
await frontend.keyboard.up('Shift');
|
||||
await waitForFunction(() => hasPausedEvents(frontend));
|
||||
|
||||
await waitFor(PAUSE_INDICATOR_SELECTOR);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,9 @@ copy_to_gen("sources") {
|
||||
"memory-workers.rawresponse",
|
||||
"minified-sourcecode.html",
|
||||
"minified-sourcecode.js",
|
||||
"multi-files-mycode.js",
|
||||
"multi-files-thirdparty.js",
|
||||
"multi-files.html",
|
||||
"multi-workers-sourcemap.html",
|
||||
"multi-workers.html",
|
||||
"multi-workers.js",
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import {callfunc} from './multi-files-thirdparty.js';
|
||||
|
||||
function inner() {
|
||||
console.log('Hello');
|
||||
}
|
||||
|
||||
export function outer() {
|
||||
callfunc(inner);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
/**
|
||||
* @param {() => void} func
|
||||
* @returns void
|
||||
*/
|
||||
|
||||
function innercall(func) {
|
||||
func();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {() => void} func
|
||||
* @returns void
|
||||
*/
|
||||
export function callfunc(func) {
|
||||
innercall(func);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<script type="module">
|
||||
import {outer} from './multi-files-mycode.js';
|
||||
|
||||
globalThis.f = outer;
|
||||
|
||||
</script>
|
||||
@@ -20,6 +20,7 @@ node_ts_library("sources") {
|
||||
"debug-raw-wasm_test.ts",
|
||||
"header-overrides_test.ts",
|
||||
"icon-row-bucket_test.ts",
|
||||
"ignore-list_test.ts",
|
||||
"inline-variable_test.ts",
|
||||
"link-to-correct-source-line_test.ts",
|
||||
"live-edit-moving-breakpoint_test.ts",
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
import {assert} from 'chai';
|
||||
|
||||
import {
|
||||
click,
|
||||
getBrowserAndPages,
|
||||
getPendingEvents,
|
||||
installEventListener,
|
||||
step,
|
||||
waitFor,
|
||||
waitForFunction,
|
||||
waitForNone,
|
||||
} from '../../shared/helper.js';
|
||||
import {setIgnoreListPattern} from '../helpers/settings-helpers.js';
|
||||
import {
|
||||
addBreakpointForLine,
|
||||
DEBUGGER_PAUSED_EVENT,
|
||||
getCallFrameNames,
|
||||
openSourceCodeEditorForFile,
|
||||
PAUSE_INDICATOR_SELECTOR,
|
||||
RESUME_BUTTON,
|
||||
stepIn,
|
||||
stepOut,
|
||||
} from '../helpers/sources-helpers.js';
|
||||
|
||||
describe('Ignore list', async function() {
|
||||
it('can be toggled on and off in call stack', async function() {
|
||||
await setIgnoreListPattern('thirdparty');
|
||||
const {target, frontend} = getBrowserAndPages();
|
||||
|
||||
await openSourceCodeEditorForFile('multi-files-mycode.js', 'multi-files.html');
|
||||
await addBreakpointForLine(frontend, 4);
|
||||
|
||||
const scriptEvaluation = target.evaluate('f();');
|
||||
await waitFor(RESUME_BUTTON);
|
||||
await waitFor('.ignore-listed-call-frame.hidden');
|
||||
assert.deepEqual(await getCallFrameNames(), ['inner', 'outer', '(anonymous)']);
|
||||
|
||||
// Toggle to show ignore-listed frames
|
||||
await click('.ignore-listed-message-label');
|
||||
await waitFor('.ignore-listed-call-frame:not(.hidden)');
|
||||
assert.deepEqual(await getCallFrameNames(), ['inner', 'innercall', 'callfunc', 'outer', '(anonymous)']);
|
||||
|
||||
// Toggle back off
|
||||
await click('.ignore-listed-message-label');
|
||||
await waitFor('.ignore-listed-call-frame.hidden');
|
||||
assert.deepEqual(await getCallFrameNames(), ['inner', 'outer', '(anonymous)']);
|
||||
|
||||
await click(RESUME_BUTTON);
|
||||
await scriptEvaluation;
|
||||
});
|
||||
|
||||
it('shows no toggle when everything is ignore-listed', async function() {
|
||||
await setIgnoreListPattern('multi|puppeteer');
|
||||
const {target, frontend} = getBrowserAndPages();
|
||||
|
||||
await openSourceCodeEditorForFile('multi-files-mycode.js', 'multi-files.html');
|
||||
await addBreakpointForLine(frontend, 4);
|
||||
|
||||
const scriptEvaluation = target.evaluate('f();');
|
||||
await waitFor(RESUME_BUTTON);
|
||||
|
||||
// Toggle is not shown
|
||||
await waitFor('.ignore-listed-message.hidden');
|
||||
// There are visible ignore-listed frames
|
||||
await waitFor('.ignore-listed-call-frame:not(.hidden)');
|
||||
// All frames are shown
|
||||
assert.deepEqual(await getCallFrameNames(), ['inner', 'innercall', 'callfunc', 'outer', '(anonymous)']);
|
||||
|
||||
await click(RESUME_BUTTON);
|
||||
await scriptEvaluation;
|
||||
});
|
||||
|
||||
it('skips frames when stepping in and out', async function() {
|
||||
await setIgnoreListPattern('thirdparty');
|
||||
const {target, frontend} = getBrowserAndPages();
|
||||
installEventListener(frontend, DEBUGGER_PAUSED_EVENT);
|
||||
|
||||
await openSourceCodeEditorForFile('multi-files-mycode.js', 'multi-files.html');
|
||||
await addBreakpointForLine(frontend, 8);
|
||||
|
||||
const scriptEvaluation = target.evaluate('f();');
|
||||
await waitFor(RESUME_BUTTON);
|
||||
await waitFor(PAUSE_INDICATOR_SELECTOR);
|
||||
await waitForFunction(async () => await getPendingEvents(frontend, DEBUGGER_PAUSED_EVENT));
|
||||
assert.deepEqual(await getCallFrameNames(), ['outer', '(anonymous)']);
|
||||
|
||||
await step('Step in', stepIn);
|
||||
await waitFor('.call-frame-title-text[title="inner"]');
|
||||
await waitFor('.ignore-listed-call-frame.hidden');
|
||||
assert.deepEqual(await getCallFrameNames(), ['inner', 'outer', '(anonymous)']);
|
||||
|
||||
await step('Step out', stepOut);
|
||||
await waitForNone('.call-frame-title-text[title="inner"]');
|
||||
assert.deepEqual(await getCallFrameNames(), ['outer', '(anonymous)']);
|
||||
|
||||
await click(RESUME_BUTTON);
|
||||
await scriptEvaluation;
|
||||
});
|
||||
});
|
||||
@@ -552,6 +552,30 @@ export const enableCDPLogging = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const enableCDPTracking = async () => {
|
||||
const {frontend} = getBrowserAndPages();
|
||||
await frontend.evaluate(() => {
|
||||
globalThis.__messageMapForTest = new Map();
|
||||
globalThis.ProtocolClient.test.onMessageSent = (message: {method: string, id: number}) => {
|
||||
globalThis.__messageMapForTest.set(message.id, message.method);
|
||||
};
|
||||
globalThis.ProtocolClient.test.onMessageReceived = (message: {id?: number}) => {
|
||||
if (message.id) {
|
||||
globalThis.__messageMapForTest.delete(message.id);
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const logOutstandingCDP = async () => {
|
||||
const {frontend} = getBrowserAndPages();
|
||||
await frontend.evaluate(() => {
|
||||
for (const entry of globalThis.__messageMapForTest) {
|
||||
console.error(entry);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const selectOption = async (select: puppeteer.ElementHandle<HTMLSelectElement>, value: string) => {
|
||||
await select.evaluate(async (node: HTMLSelectElement, _value: string) => {
|
||||
node.value = _value;
|
||||
|
||||
Reference in New Issue
Block a user