UNPKG

chrome-devtools-frontend

Version:
182 lines (160 loc) 7.41 kB
// Copyright 2021 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js'; import {CategorizedBreakpoint, Category} from './CategorizedBreakpoint.js'; import type {EventListenerPausedDetailsAuxData} from './DebuggerModel.js'; import {SDKModel} from './SDKModel.js'; import {Capability, type Target} from './Target.js'; import {type SDKModelObserver, TargetManager} from './TargetManager.js'; export const enum InstrumentationNames { BEFORE_BIDDER_WORKLET_BIDDING_START = 'beforeBidderWorkletBiddingStart', BEFORE_BIDDER_WORKLET_REPORTING_START = 'beforeBidderWorkletReportingStart', BEFORE_SELLER_WORKLET_SCORING_START = 'beforeSellerWorkletScoringStart', BEFORE_SELLER_WORKLET_REPORTING_START = 'beforeSellerWorkletReportingStart', SET_TIMEOUT = 'setTimeout', CLEAR_TIMEOUT = 'clearTimeout', SET_TIMEOUT_CALLBACK = 'setTimeout.callback', SET_INTERVAL = 'setInterval', CLEAR_INTERVAL = 'clearInterval', SET_INTERVAL_CALLBACK = 'setInterval.callback', SCRIPT_FIRST_STATEMENT = 'scriptFirstStatement', SCRIPT_BLOCKED_BY_CSP = 'scriptBlockedByCSP', SHARED_STORAGE_WORKLET_SCRIPT_FIRST_STATEMENT = 'sharedStorageWorkletScriptFirstStatement', REQUEST_ANIMATION_FRAME = 'requestAnimationFrame', CANCEL_ANIMATION_FRAME = 'cancelAnimationFrame', REQUEST_ANIMATION_FRAME_CALLBACK = 'requestAnimationFrame.callback', WEBGL_ERROR_FIRED = 'webglErrorFired', WEBGL_WARNING_FIRED = 'webglWarningFired', ELEMENT_SET_INNER_HTML = 'Element.setInnerHTML', CANVAS_CONTEXT_CREATED = 'canvasContextCreated', GEOLOCATION_GET_CURRENT_POSITION = 'Geolocation.getCurrentPosition', GEOLOCATION_WATCH_POSITION = 'Geolocation.watchPosition', NOTIFICATION_REQUEST_PERMISSION = 'Notification.requestPermission', DOM_WINDOW_CLOSE = 'DOMWindow.close', DOCUMENT_WRITE = 'Document.write', AUDIO_CONTEXT_CREATED = 'audioContextCreated', AUDIO_CONTEXT_CLOSED = 'audioContextClosed', AUDIO_CONTEXT_RESUMED = 'audioContextResumed', AUDIO_CONTEXT_SUSPENDED = 'audioContextSuspended', } export class EventBreakpointsModel extends SDKModel<void> { readonly agent: ProtocolProxyApi.EventBreakpointsApi; constructor(target: Target) { super(target); this.agent = target.eventBreakpointsAgent(); } } // This implementation (as opposed to similar class in DOMDebuggerModel) is for // instrumentation breakpoints in targets that run JS but do not have a DOM. class EventListenerBreakpoint extends CategorizedBreakpoint { override setEnabled(enabled: boolean): void { if (this.enabled() === enabled) { return; } super.setEnabled(enabled); for (const model of TargetManager.instance().models(EventBreakpointsModel)) { this.updateOnModel(model); } } updateOnModel(model: EventBreakpointsModel): void { if (this.enabled()) { void model.agent.invoke_setInstrumentationBreakpoint({eventName: this.name}); } else { void model.agent.invoke_removeInstrumentationBreakpoint({eventName: this.name}); } } static readonly instrumentationPrefix = 'instrumentation:'; } let eventBreakpointManagerInstance: EventBreakpointsManager; export class EventBreakpointsManager implements SDKModelObserver<EventBreakpointsModel> { readonly #eventListenerBreakpoints: EventListenerBreakpoint[] = []; constructor() { this.createInstrumentationBreakpoints(Category.AUCTION_WORKLET, [ InstrumentationNames.BEFORE_BIDDER_WORKLET_BIDDING_START, InstrumentationNames.BEFORE_BIDDER_WORKLET_REPORTING_START, InstrumentationNames.BEFORE_SELLER_WORKLET_SCORING_START, InstrumentationNames.BEFORE_SELLER_WORKLET_REPORTING_START, ]); this.createInstrumentationBreakpoints(Category.ANIMATION, [ InstrumentationNames.REQUEST_ANIMATION_FRAME, InstrumentationNames.CANCEL_ANIMATION_FRAME, InstrumentationNames.REQUEST_ANIMATION_FRAME_CALLBACK, ]); this.createInstrumentationBreakpoints(Category.CANVAS, [ InstrumentationNames.CANVAS_CONTEXT_CREATED, InstrumentationNames.WEBGL_ERROR_FIRED, InstrumentationNames.WEBGL_WARNING_FIRED, ]); this.createInstrumentationBreakpoints(Category.GEOLOCATION, [ InstrumentationNames.GEOLOCATION_GET_CURRENT_POSITION, InstrumentationNames.GEOLOCATION_WATCH_POSITION, ]); this.createInstrumentationBreakpoints(Category.NOTIFICATION, [ InstrumentationNames.NOTIFICATION_REQUEST_PERMISSION, ]); this.createInstrumentationBreakpoints(Category.PARSE, [ InstrumentationNames.ELEMENT_SET_INNER_HTML, InstrumentationNames.DOCUMENT_WRITE, ]); this.createInstrumentationBreakpoints(Category.SCRIPT, [ InstrumentationNames.SCRIPT_FIRST_STATEMENT, InstrumentationNames.SCRIPT_BLOCKED_BY_CSP, ]); this.createInstrumentationBreakpoints(Category.SHARED_STORAGE_WORKLET, [ InstrumentationNames.SHARED_STORAGE_WORKLET_SCRIPT_FIRST_STATEMENT, ]); this.createInstrumentationBreakpoints(Category.TIMER, [ InstrumentationNames.SET_TIMEOUT, InstrumentationNames.CLEAR_TIMEOUT, InstrumentationNames.SET_TIMEOUT_CALLBACK, InstrumentationNames.SET_INTERVAL, InstrumentationNames.CLEAR_INTERVAL, InstrumentationNames.SET_INTERVAL_CALLBACK, ]); this.createInstrumentationBreakpoints(Category.WINDOW, [ InstrumentationNames.DOM_WINDOW_CLOSE, ]); this.createInstrumentationBreakpoints(Category.WEB_AUDIO, [ InstrumentationNames.AUDIO_CONTEXT_CREATED, InstrumentationNames.AUDIO_CONTEXT_CLOSED, InstrumentationNames.AUDIO_CONTEXT_RESUMED, InstrumentationNames.AUDIO_CONTEXT_SUSPENDED, ]); TargetManager.instance().observeModels(EventBreakpointsModel, this); } static instance(opts: { forceNew: boolean|null, } = {forceNew: null}): EventBreakpointsManager { const {forceNew} = opts; if (!eventBreakpointManagerInstance || forceNew) { eventBreakpointManagerInstance = new EventBreakpointsManager(); } return eventBreakpointManagerInstance; } private createInstrumentationBreakpoints(category: Category, instrumentationNames: InstrumentationNames[]): void { for (const instrumentationName of instrumentationNames) { this.#eventListenerBreakpoints.push(new EventListenerBreakpoint(category, instrumentationName)); } } eventListenerBreakpoints(): EventListenerBreakpoint[] { return this.#eventListenerBreakpoints.slice(); } resolveEventListenerBreakpoint({eventName}: EventListenerPausedDetailsAuxData): EventListenerBreakpoint|null { if (!eventName.startsWith(EventListenerBreakpoint.instrumentationPrefix)) { return null; } const instrumentationName = eventName.substring(EventListenerBreakpoint.instrumentationPrefix.length); return this.#eventListenerBreakpoints.find(b => b.name === instrumentationName) || null; } modelAdded(eventBreakpointModel: EventBreakpointsModel): void { for (const breakpoint of this.#eventListenerBreakpoints) { if (breakpoint.enabled()) { breakpoint.updateOnModel(eventBreakpointModel); } } } modelRemoved(_eventBreakpointModel: EventBreakpointsModel): void { } } SDKModel.register(EventBreakpointsModel, {capabilities: Capability.EVENT_BREAKPOINTS, autostart: false});