UNPKG

chrome-devtools-frontend

Version:
706 lines (650 loc) • 28 kB
// Copyright 2015 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import '../../ui/components/lists/lists.js'; import '../../ui/components/tooltips/tooltips.js'; import '../../ui/legacy/legacy.js'; import type * as Common from '../../core/common/common.js'; import * as i18n from '../../core/i18n/i18n.js'; import * as Platform from '../../core/platform/platform.js'; import * as SDK from '../../core/sdk/sdk.js'; import * as Logs from '../../models/logs/logs.js'; import * as Buttons from '../../ui/components/buttons/buttons.js'; import * as uiI18n from '../../ui/i18n/i18n.js'; import * as UI from '../../ui/legacy/legacy.js'; import {Directives, html, type LitTemplate, nothing, render} from '../../ui/lit/lit.js'; import * as VisualLogging from '../../ui/visual_logging/visual_logging.js'; import * as MobileThrottling from '../mobile_throttling/mobile_throttling.js'; import * as PanelUtils from '../utils/utils.js'; import requestConditionsDrawerStyles from './requestConditionsDrawer.css.js'; const {ref, live, ifDefined} = Directives; const {widget} = UI.Widget; const UIStrings = { /** * @description Text to enable blocking of network requests */ enableBlockingAndThrottling: 'Enable blocking and throttling', /** * @description Tooltip text that appears when hovering over the plus button in the Blocked URLs Pane of the Network panel */ addRule: 'Add rule', /** * @description Accessible label for the button to add request blocking patterns in the network request blocking tool */ addPatternLabel: 'Add network request throttling or blocking pattern', /** * @description Text that shows in the network request blocking panel if no pattern has yet been added. */ noPattern: 'Nothing throttled or blocked', /** * @description Text that shows in the network request blocking panel if no pattern has yet been added. * @example {Learn more} PH1 */ noThrottlingOrBlockingPattern: `To throttle or block a network request, add a rule here manually or via the network panel's context menu. {PH1}`, /** * @description Text in Blocked URLs Pane of the Network panel * @example {4} PH1 */ dAffected: '{PH1} affected', /** * @description Text in Blocked URLs Pane of the Network panel */ textEditPattern: 'Text pattern to block or throttle matching requests; use URL Pattern syntax.', /** * @description Error text for empty list widget input in Request Blocking tool */ patternInputCannotBeEmpty: 'Pattern input cannot be empty.', /** * @description Error text for duplicate list widget input in Request Blocking tool */ patternAlreadyExists: 'Pattern already exists.', /** * @description Tooltip message when a pattern failed to parse as a URLPattern */ patternFailedToParse: 'This pattern failed to parse as a URLPattern', /** * @description Tooltip message when a pattern failed to parse as a URLPattern because it contains RegExp groups */ patternFailedWithRegExpGroups: 'RegExp groups are not allowed', /** * @description Tooltip message when a pattern was converted to a URLPattern * @example {example.com} PH1 */ patternWasUpgraded: 'This pattern was upgraded from "{PH1}"', /** * @description Message to be announced for a when list item is removed from list widget */ itemDeleted: 'Item successfully deleted', /** * @description Message to be announced for a when list item is removed from list widget */ learnMore: 'Learn more', /** * @description Tooltip on a button moving an entry up * @example {*://example.com} PH1 */ increasePriority: 'Move up {PH1}', /** * @description Tooltip on a button moving an entry down * @example {*://example.com} PH1 */ decreasePriority: 'Move down {PH1}', /** * @description Tooltip on a checkbox togging the effects for a pattern * @example {*://example.com} PH1 */ enableThrottlingToggleLabel: 'Throttle or block {PH1}', /** * @description Tooltip on a combobox selecting the request conditions */ requestConditionsLabel: 'Request conditions', /** * @description Aria announcement when a pattern was moved up */ patternMovedUp: 'URL pattern was moved up', /** * @description Aria announcemenet when a pattern was moved down */ patternMovedDown: 'URL pattern was moved down', } as const; const str_ = i18n.i18n.registerUIStrings('panels/network/RequestConditionsDrawer.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); const NETWORK_REQUEST_BLOCKING_EXPLANATION_URL = 'https://developer.chrome.com/docs/devtools/network-request-blocking' as Platform.DevToolsPath.UrlString; const {bindToAction} = UI.UIUtils; interface ViewInput { conditions: SDK.NetworkManager.RequestCondition[]; editingCondition?: SDK.NetworkManager.RequestCondition; enabled: boolean; toggleEnabled: () => void; addPattern: () => void; onToggle: (condition: SDK.NetworkManager.RequestCondition) => void; onConditionsChanged: (condition: SDK.NetworkManager.RequestCondition, conditions: SDK.NetworkManager.ThrottlingConditions) => void; onIncreasePriority: (condition: SDK.NetworkManager.RequestCondition) => void; onDecreasePriority: (condition: SDK.NetworkManager.RequestCondition) => void; onCommit: (condition: SDK.NetworkManager.RequestCondition, value: string) => void; onCancel: (condition: SDK.NetworkManager.RequestCondition) => void; onBeginEdit: (condition: SDK.NetworkManager.RequestCondition) => void; onRemove: (condition: SDK.NetworkManager.RequestCondition) => void; validator: (condition: SDK.NetworkManager.RequestCondition, value: string) => Common.UIString.LocalizedString | null; lookUpRequestCount: (condition: SDK.NetworkManager.RequestCondition) => number; } interface ViewOutput { itemRefs: Map<SDK.NetworkManager.RequestCondition, HTMLElement|undefined>; } type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void; export const DEFAULT_VIEW: View = (input, output, target) => { render( // clang-format off html` <style>${requestConditionsDrawerStyles}</style> <devtools-toolbar jslog=${VisualLogging.toolbar()}> <devtools-checkbox ?checked=${input.enabled} @click=${input.toggleEnabled} .jslogContext=${'network.enable-request-blocking'}> ${i18nString(UIStrings.enableBlockingAndThrottling)} </devtools-checkbox> <div class="toolbar-divider"></div> <devtools-button ${bindToAction('network.add-network-request-blocking-pattern')}></devtools-button> <devtools-button ${bindToAction('network.remove-all-network-request-blocking-patterns')}></devtools-button> </devtools-toolbar> ${input.conditions.length === 0 ? html` <div class="list"> <div class=empty-state> <span class=empty-state-header>${i18nString(UIStrings.noPattern)}</span> <div class=empty-state-description> ${uiI18n.getFormatLocalizedStringTemplate(str_, UIStrings.noThrottlingOrBlockingPattern, {PH1: learnMore()})} </div> <devtools-button @click=${input.addPattern} class=add-button .jslogContext=${'network.add-network-request-blocking-pattern'} title=${i18nString(UIStrings.addPatternLabel)} .variant=${Buttons.Button.Variant.TONAL}> ${i18nString(UIStrings.addRule)} </devtools-button> </div> </div> ` : html` <devtools-list class="blocked-urls list square-corners" ?deletable=${input.enabled} @edit=${(e: CustomEvent<{index: number}>) => input.onBeginEdit(input.conditions[e.detail.index])} @delete=${(e: CustomEvent<{index: number}>) => input.onRemove(input.conditions[e.detail.index])}> ${input.conditions.map((condition, index) => html` <div class="blocked-url" ${ref(e => { output.itemRefs.set(condition, e as HTMLElement | undefined); })}> ${renderItem({ condition, editing: input.editingCondition === condition, editable: input.enabled, index, onToggle: input.onToggle, onConditionsChanged: input.onConditionsChanged, onIncreasePriority: input.onIncreasePriority, onDecreasePriority: input.onDecreasePriority, onCommit: input.onCommit, onCancel: input.onCancel, onBeginEdit: input.onBeginEdit, validator: val => input.validator(condition, val), lookUpRequestCount: input.lookUpRequestCount })} </div> `)} </devtools-list> `} `, // clang-format on target, {container: {classes: (!input.enabled && input.conditions.length > 0) ? ['blocking-disabled'] : []}}); }; function renderItem({ condition, editing, editable, index, onToggle, onConditionsChanged, onIncreasePriority, onDecreasePriority, onCommit, onCancel, onBeginEdit, validator, lookUpRequestCount }: { condition: SDK.NetworkManager.RequestCondition, editing: boolean, editable: boolean, index: number, onToggle: (condition: SDK.NetworkManager.RequestCondition) => void, onConditionsChanged: (condition: SDK.NetworkManager.RequestCondition, conditions: SDK.NetworkManager.ThrottlingConditions) => void, onIncreasePriority: (condition: SDK.NetworkManager.RequestCondition) => void, onDecreasePriority: (condition: SDK.NetworkManager.RequestCondition) => void, onCommit: (condition: SDK.NetworkManager.RequestCondition, value: string) => void, onCancel: (condition: SDK.NetworkManager.RequestCondition) => void, onBeginEdit: (condition: SDK.NetworkManager.RequestCondition) => void, validator: (value: string) => Common.UIString.LocalizedString | null, lookUpRequestCount: (condition: SDK.NetworkManager.RequestCondition) => number, }): LitTemplate { const {enabled, originalOrUpgradedURLPattern, constructorStringOrWildcardURL, wildcardURL} = condition; const toggle = (e: Event): void => { e.consume(true); onToggle(condition); }; const moveUp = (e: Event): void => { e.consume(true); onIncreasePriority(condition); }; const moveDown = (e: Event): void => { e.consume(true); onDecreasePriority(condition); }; const onPromptActivate = (e: Event): void => { if (!editable || editing) { return; } onBeginEdit(condition); e.consume(true); }; const promptKeyDown = (e: Event): void => { if (!editable || editing) { return; } const keyboardEvent = e as KeyboardEvent; if (keyboardEvent.key === 'Enter') { onBeginEdit(condition); e.consume(true); } }; // clang-format off return html` <input class=blocked-url-checkbox @change=${toggle} type=checkbox title=${i18nString(UIStrings.enableThrottlingToggleLabel, {PH1: constructorStringOrWildcardURL})} .checked=${live(enabled)} .disabled=${!editable || !originalOrUpgradedURLPattern} jslog=${VisualLogging.toggle().track({ change: true })}> <devtools-button .iconName=${'arrow-up'} .variant=${Buttons.Button.Variant.ICON} .title=${i18nString(UIStrings.increasePriority, {PH1: constructorStringOrWildcardURL})} .jslogContext=${'decrease-priority'} ?disabled=${!editable || !originalOrUpgradedURLPattern} @click=${moveUp}> </devtools-button> <devtools-button .iconName=${'arrow-down'} .variant=${Buttons.Button.Variant.ICON} .title=${i18nString(UIStrings.decreasePriority, {PH1: constructorStringOrWildcardURL})} .jslogContext=${'increase-priority'} ?disabled=${!editable || !originalOrUpgradedURLPattern} @click=${moveDown}></devtools-button> ${!editing && originalOrUpgradedURLPattern ? html` <devtools-tooltip variant=rich jslogcontext=url-pattern id=url-pattern-${index}> <div>hash: ${originalOrUpgradedURLPattern.hash}</div> <div>hostname: ${originalOrUpgradedURLPattern.hostname}</div> <div>password: ${originalOrUpgradedURLPattern.password}</div> <div>pathname: ${originalOrUpgradedURLPattern.pathname}</div> <div>port: ${originalOrUpgradedURLPattern.port}</div> <div>protocol: ${originalOrUpgradedURLPattern.protocol}</div> <div>search: ${originalOrUpgradedURLPattern.search}</div> <div>username: ${originalOrUpgradedURLPattern.username}</div> <hr /> ${learnMore()} </devtools-tooltip>` : nothing} ${!editing && wildcardURL ? html` <devtools-icon name=warning-filled class="small warning" aria-details=url-pattern-warning-${index}> </devtools-icon> <devtools-tooltip variant=rich jslogcontext=url-pattern-warning id=url-pattern-warning-${index}> ${i18nString(UIStrings.patternWasUpgraded, {PH1: wildcardURL})} </devtools-tooltip> `: nothing} ${!editing && !originalOrUpgradedURLPattern ? html` <devtools-icon name=cross-circle-filled class=small aria-details=url-pattern-error-${index}> </devtools-icon> <devtools-tooltip variant=rich jslogcontext=url-pattern-warning id=url-pattern-error-${index}> ${SDK.NetworkManager.RequestURLPattern.isValidPattern(constructorStringOrWildcardURL) === SDK.NetworkManager.RequestURLPatternValidity.HAS_REGEXP_GROUPS ? i18nString(UIStrings.patternFailedWithRegExpGroups) : i18nString(UIStrings.patternFailedToParse)} ${learnMore()} </devtools-tooltip>`: nothing} <devtools-prompt @click=${onPromptActivate} @keydown=${promptKeyDown} @focus=${onPromptActivate} tabindex=${ifDefined(editing ? undefined : 0)} @commit=${(e: UI.TextPrompt.TextPromptElement.CommitEvent) => onCommit(condition, e.detail)} @cancel=${() => onCancel(condition)} ?disabled=${!editable} placeholder=${i18nString(UIStrings.textEditPattern)} value=${constructorStringOrWildcardURL} ?editing=${editable && editing} .validator=${validator} class=blocked-url-label aria-details=url-pattern-${index}> ${constructorStringOrWildcardURL} </devtools-prompt> <select class=conditions-selector title=${i18nString(UIStrings.requestConditionsLabel)} @ConditionsChanged=${(e: CustomEvent<SDK.NetworkManager.ThrottlingConditions>) => { onConditionsChanged(condition, e.detail); }} ${widget( MobileThrottling.NetworkThrottlingSelector.NetworkThrottlingSelect, { variant: MobileThrottling.NetworkThrottlingSelector.NetworkThrottlingSelect.Variant.INDIVIDUAL_REQUEST_CONDITIONS, jslogContext: 'request-conditions', disabled: !editable, currentConditions: condition.conditions, })}></select> <devtools-widget ?disabled=${!editable || !originalOrUpgradedURLPattern} ${widget(AffectedCountWidget, {condition, lookUpRequestCount})}></devtools-widget>`; // clang-format on } interface AffectedCountViewInput { count: number; } type AffectedCountView = (input: AffectedCountViewInput, output: object, target: HTMLElement) => void; export const AFFECTED_COUNT_DEFAULT_VIEW: AffectedCountView = (input, output, target) => { render(html`${i18nString(UIStrings.dAffected, {PH1: input.count})}`, target, {container: {classes: ['blocked-url-count']}}); }; function matchesUrl(conditions: SDK.NetworkManager.RequestCondition, url: string): boolean { return Boolean(conditions.originalOrUpgradedURLPattern?.test(url)); } export class AffectedCountWidget extends UI.Widget.Widget { readonly #view: AffectedCountView; #condition?: SDK.NetworkManager.RequestCondition; #lookUpRequestCount?: (condition: SDK.NetworkManager.RequestCondition) => number; constructor(target?: HTMLElement, view = AFFECTED_COUNT_DEFAULT_VIEW) { super(target); this.#view = view; } get lookUpRequestCount(): ((condition: SDK.NetworkManager.RequestCondition) => number)|undefined { return this.#lookUpRequestCount; } set lookUpRequestCount(val: (condition: SDK.NetworkManager.RequestCondition) => number) { this.#lookUpRequestCount = val; } get condition(): SDK.NetworkManager.RequestCondition|undefined { return this.#condition; } set condition(conditions: SDK.NetworkManager.RequestCondition) { this.#condition = conditions; this.requestUpdate(); } override performUpdate(): void { if (!this.#condition || !this.#lookUpRequestCount) { return; } this.#view({count: this.#lookUpRequestCount(this.#condition)}, {}, this.element); } override wasShown(): void { SDK.TargetManager.TargetManager.instance().addModelListener(SDK.NetworkManager.NetworkManager, SDK.NetworkManager.Events.RequestFinished, this.#onRequestFinished, this, {scoped: true}); Logs.NetworkLog.NetworkLog.instance().addEventListener(Logs.NetworkLog.Events.Reset, this.requestUpdate, this); super.wasShown(); } override willHide(): void { super.willHide(); SDK.TargetManager.TargetManager.instance().removeModelListener( SDK.NetworkManager.NetworkManager, SDK.NetworkManager.Events.RequestFinished, this.#onRequestFinished, this); Logs.NetworkLog.NetworkLog.instance().removeEventListener(Logs.NetworkLog.Events.Reset, this.requestUpdate, this); } #onRequestFinished(event: Common.EventTarget.EventTargetEvent<SDK.NetworkRequest.NetworkRequest>): void { if (!this.#condition) { return; } const request = event.data; if ((request.appliedNetworkConditionsId && this.#condition.ruleIds.has(request.appliedNetworkConditionsId)) || (request.wasBlocked() && matchesUrl(this.#condition, request.url()))) { this.requestUpdate(); } } } function learnMore(): LitTemplate { return html`<devtools-link href=${NETWORK_REQUEST_BLOCKING_EXPLANATION_URL} tabindex=0 class=devtools-link .jslogContext=${'learn-more'}> ${i18nString(UIStrings.learnMore)} </devtools-link>`; } export class RequestConditionsDrawer extends UI.Widget.VBox { private manager: SDK.NetworkManager.MultitargetNetworkManager; private blockedCountForUrl: Map<Platform.DevToolsPath.UrlString, number>; #throttledCount = new Map<string, number>(); #view: View; #viewOutput: ViewOutput = {itemRefs: new Map()}; #editingCondition?: SDK.NetworkManager.RequestCondition; constructor(target?: HTMLElement, view = DEFAULT_VIEW) { super(target, { jslog: `${VisualLogging.panel('network.blocked-urls').track({resize: true})}`, useShadowDom: true, }); this.#view = view; this.manager = SDK.NetworkManager.MultitargetNetworkManager.instance(); this.manager.addEventListener(SDK.NetworkManager.MultitargetNetworkManager.Events.BLOCKED_PATTERNS_CHANGED, this.requestUpdate, this); this.blockedCountForUrl = new Map(); SDK.TargetManager.TargetManager.instance().addModelListener(SDK.NetworkManager.NetworkManager, SDK.NetworkManager.Events.RequestFinished, this.onRequestFinished, this, {scoped: true}); this.requestUpdate(); Logs.NetworkLog.NetworkLog.instance().addEventListener(Logs.NetworkLog.Events.Reset, this.onNetworkLogReset, this); } override performUpdate(): void { this.#viewOutput.itemRefs.clear(); const enabled = this.manager.requestConditions.conditionsEnabled; const conditions = Array.from(this.manager.requestConditions.conditions); if (this.#editingCondition && !conditions.includes(this.#editingCondition)) { conditions.unshift(this.#editingCondition); } const input: ViewInput = { addPattern: this.addPattern.bind(this), toggleEnabled: this.toggleEnabled.bind(this), enabled, conditions, editingCondition: this.#editingCondition, onToggle: (condition: SDK.NetworkManager.RequestCondition) => { if (enabled) { condition.enabled = !condition.enabled; } }, onConditionsChanged: (condition: SDK.NetworkManager.RequestCondition, conditions: SDK.NetworkManager.ThrottlingConditions) => { if (enabled) { condition.conditions = conditions; } }, onIncreasePriority: (condition: SDK.NetworkManager.RequestCondition) => { if (enabled) { UI.ARIAUtils.LiveAnnouncer.status(i18nString(UIStrings.patternMovedUp)); this.manager.requestConditions.increasePriority(condition); } }, onDecreasePriority: (condition: SDK.NetworkManager.RequestCondition) => { if (enabled) { UI.ARIAUtils.LiveAnnouncer.status(i18nString(UIStrings.patternMovedDown)); this.manager.requestConditions.decreasePriority(condition); } }, onBeginEdit: (condition: SDK.NetworkManager.RequestCondition) => { if (this.#editingCondition) { this.#cancelEdit(this.#editingCondition); } this.#editingCondition = condition; this.requestUpdate(); }, onRemove: (condition: SDK.NetworkManager.RequestCondition) => { this.manager.requestConditions.delete(condition); UI.ARIAUtils.LiveAnnouncer.alert(UIStrings.itemDeleted); }, onCommit: this.#commitEdit.bind(this), onCancel: this.#cancelEdit.bind(this), validator: this.#validator.bind(this), lookUpRequestCount: this.#getRequestCount.bind(this), }; this.#view(input, this.#viewOutput, this.contentElement); } addPattern(): void { this.manager.requestConditions.conditionsEnabled = true; if (this.#editingCondition) { this.#cancelEdit(this.#editingCondition); } const condition = SDK.NetworkManager.RequestCondition.createFromSetting( {url: Platform.DevToolsPath.EmptyUrlString, enabled: true}); this.#editingCondition = condition; this.requestUpdate(); } removeAllPatterns(): void { this.manager.requestConditions.clear(); } #validator(condition: SDK.NetworkManager.RequestCondition, value: string): Common.UIString.LocalizedString|null { if (!value) { return i18nString(UIStrings.patternInputCannotBeEmpty); } const parsedPattern = SDK.NetworkManager.RequestURLPattern.create(value as SDK.NetworkManager.URLPatternConstructorString); const stringToCheck = parsedPattern ? parsedPattern.constructorString : value; const existingCondition = this.manager.requestConditions.findCondition(stringToCheck); if (existingCondition && existingCondition !== condition) { return i18nString(UIStrings.patternAlreadyExists); } const isValid = SDK.NetworkManager.RequestURLPattern.isValidPattern(value); switch (isValid) { case SDK.NetworkManager.RequestURLPatternValidity.FAILED_TO_PARSE: return i18nString(UIStrings.patternFailedToParse); case SDK.NetworkManager.RequestURLPatternValidity.HAS_REGEXP_GROUPS: return i18nString(UIStrings.patternFailedWithRegExpGroups); } return null; } private toggleEnabled(): void { this.manager.requestConditions.conditionsEnabled = !this.manager.requestConditions.conditionsEnabled; this.requestUpdate(); } #commitEdit(condition: SDK.NetworkManager.RequestCondition, value: string): void { if (this.#editingCondition !== condition) { return; } if (condition.constructorStringOrWildcardURL === value && Array.from(this.manager.requestConditions.conditions).includes(condition)) { this.#editingCondition = undefined; this.requestUpdate(); return; } const constructorString = value as SDK.NetworkManager.URLPatternConstructorString; const pattern = SDK.NetworkManager.RequestURLPattern.create(constructorString); if (!pattern) { return; } condition.pattern = pattern; // If it's a new item, it isn't in the manager yet. Add it. if (!Array.from(this.manager.requestConditions.conditions).includes(condition)) { this.manager.requestConditions.add(condition); } this.#editingCondition = undefined; this.requestUpdate(); } #cancelEdit(condition: SDK.NetworkManager.RequestCondition): void { if (this.#editingCondition === condition) { this.#editingCondition = undefined; this.requestUpdate(); } } #getRequestCount(condition: SDK.NetworkManager.RequestCondition): number { if (condition.isBlocking) { let result = 0; for (const blockedUrl of this.blockedCountForUrl.keys()) { if (matchesUrl(condition, blockedUrl)) { result += (this.blockedCountForUrl.get(blockedUrl) as number); } } return result; } let result = 0; for (const ruleId of condition.ruleIds) { result += this.#throttledCount.get(ruleId) ?? 0; } return result; } private onNetworkLogReset(_event: Common.EventTarget.EventTargetEvent<Logs.NetworkLog.ResetEvent>): void { this.blockedCountForUrl.clear(); this.#throttledCount.clear(); } private onRequestFinished(event: Common.EventTarget.EventTargetEvent<SDK.NetworkRequest.NetworkRequest>): void { const request = event.data; if (request.appliedNetworkConditionsId) { const count = this.#throttledCount.get(request.appliedNetworkConditionsId) ?? 0; this.#throttledCount.set(request.appliedNetworkConditionsId, count + 1); } if (request.wasBlocked()) { const count = this.blockedCountForUrl.get(request.url()) || 0; this.blockedCountForUrl.set(request.url(), count + 1); } } override wasShown(): void { UI.Context.Context.instance().setFlavor(RequestConditionsDrawer, this); super.wasShown(); } override willHide(): void { super.willHide(); UI.Context.Context.instance().setFlavor(RequestConditionsDrawer, null); } static async reveal(appliedConditions: SDK.NetworkManager.AppliedNetworkConditions): Promise<void> { await UI.ViewManager.ViewManager.instance().showView('network.blocked-urls'); const drawer = UI.Context.Context.instance().flavor(RequestConditionsDrawer); if (!drawer) { console.assert(!!drawer, 'Drawer not initialized'); return; } const condition = drawer.manager.requestConditions.conditions.find( condition => condition.ruleIds.has(appliedConditions.appliedNetworkConditionsId) && condition.constructorString && condition.constructorString === appliedConditions.urlPattern); if (condition) { const element = drawer.#viewOutput.itemRefs.get(condition); if (element) { PanelUtils.PanelUtils.highlightElement(element); } } } } export class ActionDelegate implements UI.ActionRegistration.ActionDelegate { handleAction(context: UI.Context.Context, actionId: string): boolean { const drawer = context.flavor(RequestConditionsDrawer); if (drawer === null) { return false; } switch (actionId) { case 'network.add-network-request-blocking-pattern': { drawer.addPattern(); return true; } case 'network.remove-all-network-request-blocking-patterns': { drawer.removeAllPatterns(); return true; } } return false; } } export class AppliedConditionsRevealer implements Common.Revealer.Revealer<SDK.NetworkManager.AppliedNetworkConditions> { async reveal(request: SDK.NetworkManager.AppliedNetworkConditions): Promise<void> { if (request.urlPattern) { await RequestConditionsDrawer.reveal(request); } } }