UNPKG

chrome-devtools-frontend

Version:
730 lines (671 loc) • 29.5 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. /* eslint-disable @devtools/no-lit-render-outside-of-view */ /* eslint-disable @devtools/no-imperative-dom-api */ import '../../ui/legacy/legacy.js'; import '../../ui/components/tooltips/tooltips.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 Root from '../../core/root/root.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} = Directives; const {widgetConfig} = UI.Widget; const UIStrings = { /** * @description Text to enable blocking of network requests */ enableNetworkRequestBlocking: 'Enable network request blocking', /** * @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 */ addPattern: 'Add pattern', /** * @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 */ addNetworkRequestBlockingPattern: 'Add network request blocking pattern', /** * @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. */ noNetworkRequestsBlocked: 'No blocked network requests', /** * @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 that shows in the network request blocking panel if no pattern has yet been added. * @example {Add pattern} PH1 */ addPatternToBlock: 'Add a pattern by clicking on the "{PH1}" button.', /** * @description Text in Blocked URLs Pane of the Network panel * @example {4} PH1 */ dBlocked: '{PH1} blocked', /** * @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 */ textPatternToBlockMatching: 'Text pattern to block matching requests; use * for wildcard', /** * @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 Accessibility label on a `Learn more` link */ learnMoreLabel: 'Learn more about URL pattern syntax', /** * @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', /** * @description Text on a button to start editing text * @example {*://example.com} PH1 */ editPattern: 'Edit {PH1}', /** * @description Label for an item to remove something * @example {*://example.com} PH1 */ removePattern: 'Remove {PH1}', } 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 PATTERN_API_DOCS_URL = 'https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API' as Platform.DevToolsPath.UrlString; const {bindToAction} = UI.UIUtils; interface ViewInput { list: UI.ListWidget.ListWidget<SDK.NetworkManager.RequestCondition>; enabled: boolean; toggleEnabled: () => void; addPattern: () => void; } type View = (input: ViewInput, output: object, target: HTMLElement) => void; export const DEFAULT_VIEW: View = (input, output, target) => { const individualThrottlingEnabled = Boolean(Root.Runtime.hostConfig.devToolsIndividualRequestThrottling?.enabled); render( // clang-format off html` <style>${RequestConditionsDrawer}</style> <devtools-toolbar jslog=${VisualLogging.toolbar()}> <devtools-checkbox ?checked=${input.enabled} @click=${input.toggleEnabled} .jslogContext=${'network.enable-request-blocking'}> ${individualThrottlingEnabled ? i18nString(UIStrings.enableBlockingAndThrottling) : i18nString(UIStrings.enableNetworkRequestBlocking)} </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> <div class=empty-state ${ref(e => input.list.setEmptyPlaceholder(e ?? null))}> <span class=empty-state-header>${individualThrottlingEnabled ? i18nString(UIStrings.noPattern) : i18nString(UIStrings.noNetworkRequestsBlocked)}</span> <div class=empty-state-description> ${individualThrottlingEnabled ? uiI18n.getFormatLocalizedStringTemplate(str_, UIStrings.noThrottlingOrBlockingPattern, {PH1: learnMore()}) : html`<span>${i18nString(UIStrings.addPatternToBlock, {PH1: i18nString(UIStrings.addPattern)})}</span>${learnMore()}`} </div> <devtools-button @click=${input.addPattern} class=add-button .jslogContext=${'network.add-network-request-blocking-pattern'} title=${individualThrottlingEnabled ? i18nString(UIStrings.addPatternLabel) : i18nString(UIStrings.addNetworkRequestBlockingPattern)} .variant=${Buttons.Button.Variant.TONAL}> ${individualThrottlingEnabled ? i18nString(UIStrings.addRule) : i18nString(UIStrings.addPattern)} </devtools-button> </div> <devtools-widget .widgetConfig=${UI.Widget.widgetConfig(UI.Widget.VBox)}> ${input.list.element} </devtools-widget> `, // clang-format on target); }; interface AffectedCountViewInput { count: number; } type AffectedCountView = (input: AffectedCountViewInput, output: object, target: HTMLElement) => void; export const AFFECTED_COUNT_DEFAULT_VIEW: AffectedCountView = (input, output, target) => { if (Root.Runtime.hostConfig.devToolsIndividualRequestThrottling?.enabled) { render(html`${i18nString(UIStrings.dAffected, {PH1: input.count})}`, target); } else { render(html`${i18nString(UIStrings.dBlocked, {PH1: input.count})}`, target); } }; function matchesUrl(conditions: SDK.NetworkManager.RequestCondition, url: string): boolean { function matchesPattern(pattern: string, url: string): boolean { let pos = 0; const parts = pattern.split('*'); for (let index = 0; index < parts.length; index++) { const part = parts[index]; if (!part.length) { continue; } pos = url.indexOf(part, pos); if (pos === -1) { return false; } pos += part.length; } return true; } return Boolean( Root.Runtime.hostConfig.devToolsIndividualRequestThrottling?.enabled ? conditions.originalOrUpgradedURLPattern?.test(url) : (conditions.wildcardURL && matchesPattern(conditions.wildcardURL, url))); } export class AffectedCountWidget extends UI.Widget.Widget { readonly #view: AffectedCountView; #condition?: SDK.NetworkManager.RequestCondition; #drawer?: RequestConditionsDrawer; constructor(target?: HTMLElement, view = AFFECTED_COUNT_DEFAULT_VIEW) { super(target, {classes: ['blocked-url-count']}); this.#view = view; } get condition(): SDK.NetworkManager.RequestCondition|undefined { return this.#condition; } set condition(conditions: SDK.NetworkManager.RequestCondition) { this.#condition = conditions; this.requestUpdate(); } get drawer(): RequestConditionsDrawer|undefined { return this.#drawer; } set drawer(drawer: RequestConditionsDrawer) { this.#drawer = drawer; this.requestUpdate(); } override performUpdate(): void { if (!this.#condition || !this.#drawer) { return; } const count = !Root.Runtime.hostConfig.devToolsIndividualRequestThrottling?.enabled || this.#condition.isBlocking ? this.#drawer.blockedRequestsCount(this.#condition) : this.#drawer.throttledRequestsCount(this.#condition); this.#view({count}, {}, 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`<x-link href=${NETWORK_REQUEST_BLOCKING_EXPLANATION_URL} tabindex=0 class=devtools-link jslog=${VisualLogging.link().track({click: true, keydown: 'Enter|Space'}).context('learn-more')}> ${i18nString(UIStrings.learnMore)} </x-link>`; } export class RequestConditionsDrawer extends UI.Widget.VBox implements UI.ListWidget.Delegate<SDK.NetworkManager.RequestCondition> { private manager: SDK.NetworkManager.MultitargetNetworkManager; private readonly list: UI.ListWidget.ListWidget<SDK.NetworkManager.RequestCondition>; private editor: UI.ListWidget.Editor<SDK.NetworkManager.RequestCondition>|null; private blockedCountForUrl: Map<Platform.DevToolsPath.UrlString, number>; #throttledCount = new Map<string, number>(); #view: View; #listElements = new WeakMap<SDK.NetworkManager.RequestCondition, HTMLElement>(); 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.update, this); this.list = new UI.ListWidget.ListWidget(this); this.list.registerRequiredCSS(requestConditionsDrawerStyles); this.list.element.classList.add('blocked-urls'); this.editor = null; this.blockedCountForUrl = new Map(); SDK.TargetManager.TargetManager.instance().addModelListener( SDK.NetworkManager.NetworkManager, SDK.NetworkManager.Events.RequestFinished, this.onRequestFinished, this, {scoped: true}); this.update(); Logs.NetworkLog.NetworkLog.instance().addEventListener(Logs.NetworkLog.Events.Reset, this.onNetworkLogReset, this); } override performUpdate(): void { const enabled = this.manager.requestConditions.conditionsEnabled; this.list.element.classList.toggle('blocking-disabled', !enabled && Boolean(this.manager.requestConditions.count)); const input: ViewInput = { addPattern: this.addPattern.bind(this), toggleEnabled: this.toggleEnabled.bind(this), enabled, list: this.list, }; this.#view(input, {}, this.contentElement); } addPattern(): void { this.manager.requestConditions.conditionsEnabled = true; this.list.addNewItem( 0, SDK.NetworkManager.RequestCondition.createFromSetting( {url: Platform.DevToolsPath.EmptyUrlString, enabled: true})); } removeAllPatterns(): void { this.manager.requestConditions.clear(); } renderItem(condition: SDK.NetworkManager.RequestCondition, editable: boolean, index: number): Element { const element = document.createElement('div'); this.#listElements.set(condition, element); element.classList.add('blocked-url'); this.updateItem(element, condition, editable, index); return element; } updateItem(element: HTMLElement, condition: SDK.NetworkManager.RequestCondition, editable: boolean, index: number): void { const toggle = (e: Event): void => { if (editable) { e.consume(true); condition.enabled = !condition.enabled; } }; const onConditionsChanged = (conditions: SDK.NetworkManager.ThrottlingConditions): void => { if (editable) { condition.conditions = conditions; } }; const {enabled, originalOrUpgradedURLPattern, constructorStringOrWildcardURL, wildcardURL} = condition; if (Root.Runtime.hostConfig.devToolsIndividualRequestThrottling?.enabled) { const moveUp = (e: Event): void => { if (this.manager.requestConditions.conditionsEnabled) { UI.ARIAUtils.LiveAnnouncer.status(i18nString(UIStrings.patternMovedUp)); e.consume(true); this.manager.requestConditions.increasePriority(condition); } }; const moveDown = (e: Event): void => { if (this.manager.requestConditions.conditionsEnabled) { UI.ARIAUtils.LiveAnnouncer.status(i18nString(UIStrings.patternMovedDown)); e.consume(true); this.manager.requestConditions.decreasePriority(condition); } }; render( // clang-format off html` <input class=blocked-url-checkbox @click=${toggle} type=checkbox title=${i18nString(UIStrings.enableThrottlingToggleLabel, {PH1: constructorStringOrWildcardURL})} .checked=${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> ${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} ${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} ${!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} <div @click=${toggle} ?disabled=${!editable || !originalOrUpgradedURLPattern} class=blocked-url-label aria-details=url-pattern-${index}> ${constructorStringOrWildcardURL} </div> <devtools-widget class=conditions-selector title=${i18nString(UIStrings.requestConditionsLabel)} .widgetConfig=${UI.Widget.widgetConfig( MobileThrottling.NetworkThrottlingSelector.NetworkThrottlingSelectorWidget, { variant: MobileThrottling.NetworkThrottlingSelector.NetworkThrottlingSelect.Variant.INDIVIDUAL_REQUEST_CONDITIONS, jslogContext: 'request-conditions', disabled: !editable, onConditionsChanged, currentConditions: condition.conditions, })}></devtools-widget> <devtools-widget ?disabled=${!editable || !originalOrUpgradedURLPattern} .widgetConfig=${widgetConfig(AffectedCountWidget, {condition, drawer: this})}></devtools-widget>`, // clang-format on element); } else { render( // clang-format off html` <input class=blocked-url-checkbox @click=${toggle} type=checkbox .checked=${condition.enabled} .disabled=${!editable} jslog=${VisualLogging.toggle().track({ change: true })}> <div @click=${toggle} class=blocked-url-label>${wildcardURL}</div> <devtools-widget .widgetConfig=${widgetConfig(AffectedCountWidget, {condition, drawer: this})}></devtools-widget>`, // clang-format on element); } } private toggleEnabled(): void { this.manager.requestConditions.conditionsEnabled = !this.manager.requestConditions.conditionsEnabled; this.update(); } removeItemRequested(condition: SDK.NetworkManager.RequestCondition): void { this.manager.requestConditions.delete(condition); UI.ARIAUtils.LiveAnnouncer.alert(UIStrings.itemDeleted); } beginEdit(pattern: SDK.NetworkManager.RequestCondition): UI.ListWidget.Editor<SDK.NetworkManager.RequestCondition> { this.editor = this.createEditor(); this.editor.control('url').value = Root.Runtime.hostConfig.devToolsIndividualRequestThrottling?.enabled ? pattern.constructorStringOrWildcardURL : pattern.wildcardURL ?? ''; return this.editor; } commitEdit( item: SDK.NetworkManager.RequestCondition, editor: UI.ListWidget.Editor<SDK.NetworkManager.RequestCondition>, isNew: boolean): void { const constructorString = editor.control('url').value as SDK.NetworkManager.URLPatternConstructorString; const pattern = Root.Runtime.hostConfig.devToolsIndividualRequestThrottling?.enabled ? SDK.NetworkManager.RequestURLPattern.create(constructorString) : constructorString; if (!pattern) { throw new Error('Failed to parse pattern'); } item.pattern = pattern; if (isNew) { this.manager.requestConditions.add(item); } } private createEditor(): UI.ListWidget.Editor<SDK.NetworkManager.RequestCondition> { if (this.editor) { return this.editor; } const editor = new UI.ListWidget.Editor<SDK.NetworkManager.RequestCondition>(); const content = editor.contentElement(); const titles = content.createChild('div', 'blocked-url-edit-row'); const label = titles.createChild('label'); if (Root.Runtime.hostConfig.devToolsIndividualRequestThrottling?.enabled) { const learnMore = UI.XLink.XLink.create( PATTERN_API_DOCS_URL, i18nString(UIStrings.learnMore), undefined, undefined, 'learn-more'); learnMore.title = i18nString(UIStrings.learnMoreLabel); titles.append('\xA0', learnMore); label.textContent = i18nString(UIStrings.textEditPattern); } else { label.textContent = i18nString(UIStrings.textPatternToBlockMatching); } const fields = content.createChild('div', 'blocked-url-edit-row'); const validator = (_item: SDK.NetworkManager.RequestCondition, _index: number, input: UI.ListWidget.EditorControl): { valid: boolean, errorMessage: Common.UIString.LocalizedString|undefined, } => { if (!input.value) { return {errorMessage: i18nString(UIStrings.patternInputCannotBeEmpty), valid: false}; } if (this.manager.requestConditions.has(input.value)) { return {errorMessage: i18nString(UIStrings.patternAlreadyExists), valid: false}; } if (Root.Runtime.hostConfig.devToolsIndividualRequestThrottling?.enabled) { const isValid = SDK.NetworkManager.RequestURLPattern.isValidPattern(input.value); switch (isValid) { case SDK.NetworkManager.RequestURLPatternValidity.FAILED_TO_PARSE: return {errorMessage: i18nString(UIStrings.patternFailedToParse), valid: false}; case SDK.NetworkManager.RequestURLPatternValidity.HAS_REGEXP_GROUPS: return {errorMessage: i18nString(UIStrings.patternFailedWithRegExpGroups), valid: false}; } } return {valid: true, errorMessage: undefined}; }; const urlInput = editor.createInput('url', 'text', '', validator); label.htmlFor = urlInput.id = 'editor-url-input'; fields.createChild('div', 'blocked-url-edit-value').appendChild(urlInput); return editor; } update(): void { const enabled = this.manager.requestConditions.conditionsEnabled; const newItems = Array.from(this.manager.requestConditions.conditions.filter( pattern => Root.Runtime.hostConfig.devToolsIndividualRequestThrottling?.enabled || pattern.wildcardURL)); let oldIndex = 0; for (; oldIndex < newItems.length; ++oldIndex) { const pattern = newItems[oldIndex]; this.list.updateItem( oldIndex, pattern, enabled, /* focusable=*/ false, { edit: i18nString(UIStrings.editPattern, {PH1: pattern.constructorStringOrWildcardURL}), delete: i18nString(UIStrings.removePattern, {PH1: pattern.constructorStringOrWildcardURL}) }, ); } while (oldIndex < this.list.items.length) { this.list.removeItem(oldIndex); } this.requestUpdate(); } blockedRequestsCount(condition: SDK.NetworkManager.RequestCondition): number { let result = 0; for (const blockedUrl of this.blockedCountForUrl.keys()) { if (matchesUrl(condition, blockedUrl)) { result += (this.blockedCountForUrl.get(blockedUrl) as number); } } return result; } throttledRequestsCount(condition: SDK.NetworkManager.RequestCondition): number { 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 conditions = drawer.manager.requestConditions.conditions.find( condition => condition.ruleIds.has(appliedConditions.appliedNetworkConditionsId) && condition.constructorString && condition.constructorString === appliedConditions.urlPattern); const element = (conditions && drawer.#listElements.get(conditions)); 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); } } }