| // Copyright 2017 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 Platform from '../../core/platform/platform.js'; | |
| import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js'; | |
| import * as Protocol from '../../generated/protocol.js'; | |
| import * as Common from '../common/common.js'; | |
| import * as i18n from '../i18n/i18n.js'; | |
| import type {CSSModel} from './CSSModel.js'; | |
| import {DebuggerModel, Events as DebuggerModelEvents} from './DebuggerModel.js'; | |
| import {DeferredDOMNode, DOMModel, type DOMNode, DOMNodeEvents, Events as DOMModelEvents} from './DOMModel.js'; | |
| import {OverlayPersistentHighlighter} from './OverlayPersistentHighlighter.js'; | |
| import type {RemoteObject} from './RemoteObject.js'; | |
| import {SDKModel} from './SDKModel.js'; | |
| import { | |
| apcaSettingDescriptor, | |
| showAdHighlightsSettingDescriptor, | |
| showDebugBordersSettingDescriptor, | |
| showFPSCounterSettingDescriptor, | |
| showLayoutShiftRegionsSettingDescriptor, | |
| showMetricsRulersSettingDescriptor, | |
| showPaintRectsSettingDescriptor, | |
| showScrollBottleneckRectsSettingDescriptor, | |
| } from './SDKSettings.js'; | |
| import {Capability, type Target} from './Target.js'; | |
| import type {TargetManager} from './TargetManager.js'; | |
| const UIStrings = { | |
| /** | |
| * @description Overlay message indicating that execution is paused in the debugger. | |
| */ | |
| pausedInDebugger: 'Paused in debugger', | |
| } as const; | |
| const str_ = i18n.i18n.registerUIStrings('core/sdk/OverlayModel.ts', UIStrings); | |
| const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); | |
| export interface HighlightColor { | |
| r: number; | |
| g: number; | |
| b: number; | |
| a: number; | |
| } | |
| export interface HighlightRect { | |
| x: number; | |
| y: number; | |
| width: number; | |
| height: number; | |
| color: HighlightColor; | |
| outlineColor: HighlightColor; | |
| } | |
| export interface Hinge { | |
| width: number; | |
| height: number; | |
| x: number; | |
| y: number; | |
| contentColor: HighlightColor; | |
| outlineColor: HighlightColor; | |
| } | |
| export interface BaseDisplayCutout { | |
| width: number; | |
| height: number; | |
| x: number; | |
| y: number; | |
| shape: Protocol.Overlay.DisplayCutoutShape; | |
| contentColor?: HighlightColor; | |
| } | |
| export type DisplayCutout = | |
| BaseDisplayCutout&({shape: Protocol.Overlay.DisplayCutoutShape.Rectangle}| | |
| {shape: Protocol.Overlay.DisplayCutoutShape.Pill, borderRadius: number}| | |
| {shape: Protocol.Overlay.DisplayCutoutShape.Notch, upperRadius: number, lowerRadius: number}| | |
| {shape: Protocol.Overlay.DisplayCutoutShape.Circle, cx: number, cy: number, radius: number}); | |
| export const enum EmulatedOSType { | |
| WINDOWS = 'Windows', | |
| MAC = 'Mac', | |
| LINUX = 'Linux', | |
| } | |
| interface PlatformOverlayDimensions { | |
| mac: {x: number, y: number, width: number, height: number}; | |
| linux: {x: number, y: number, width: number, height: number}; | |
| windows: {x: number, y: number, width: number, height: number}; | |
| } | |
| const platformOverlayDimensions: Readonly<PlatformOverlayDimensions> = { | |
| mac: {x: 85, y: 0, width: 185, height: 40}, | |
| linux: {x: 0, y: 0, width: 196, height: 34}, | |
| windows: {x: 0, y: 0, width: 238, height: 33}, | |
| }; | |
| export class OverlayModel extends SDKModel<EventTypes> implements ProtocolProxyApi.OverlayDispatcher { | |
| readonly #domModel: DOMModel; | |
| overlayAgent: ProtocolProxyApi.OverlayApi; | |
| readonly #debuggerModel: DebuggerModel|null; | |
| #inspectModeEnabled = false; | |
| #hideHighlightTimeout?: ReturnType<typeof setTimeout>; | |
| #defaultHighlighter: Highlighter; | |
| #highlighter: Highlighter; | |
| #showPaintRectsSetting: Common.Settings.Setting<boolean>; | |
| #showLayoutShiftRegionsSetting: Common.Settings.Setting<boolean>; | |
| #showAdHighlightsSetting: Common.Settings.Setting<boolean>; | |
| #showDebugBordersSetting: Common.Settings.Setting<boolean>; | |
| #showFPSCounterSetting: Common.Settings.Setting<boolean>; | |
| #showScrollBottleneckRectsSetting: Common.Settings.Setting<boolean>; | |
| #registeredListeners: Common.EventTarget.EventDescriptor[] = []; | |
| #showViewportSizeOnResize = true; | |
| #persistentHighlighter: OverlayPersistentHighlighter|null; | |
| readonly #sourceOrderHighlighter: SourceOrderHighlighter; | |
| #sourceOrderModeActive = false; | |
| #windowControls: WindowControls; | |
| constructor(target: Target) { | |
| super(target); | |
| this.#domModel = (target.model(DOMModel) as DOMModel); | |
| target.registerOverlayDispatcher(this); | |
| this.overlayAgent = target.overlayAgent(); | |
| const settings = this.target().targetManager().settings; | |
| this.#debuggerModel = target.model(DebuggerModel); | |
| if (this.#debuggerModel) { | |
| settings.moduleSetting('disable-paused-state-overlay') | |
| .addChangeListener(this.updatePausedInDebuggerMessage, this); | |
| this.#debuggerModel.addEventListener( | |
| DebuggerModelEvents.DebuggerPaused, this.updatePausedInDebuggerMessage, this); | |
| this.#debuggerModel.addEventListener( | |
| DebuggerModelEvents.DebuggerResumed, this.updatePausedInDebuggerMessage, this); | |
| // TODO(dgozman): we should get DebuggerResumed on navigations instead of listening to GlobalObjectCleared. | |
| this.#debuggerModel.addEventListener( | |
| DebuggerModelEvents.GlobalObjectCleared, this.updatePausedInDebuggerMessage, this); | |
| } | |
| this.#defaultHighlighter = new DefaultHighlighter(this); | |
| this.#highlighter = this.#defaultHighlighter; | |
| this.#showPaintRectsSetting = settings.resolve(showPaintRectsSettingDescriptor); | |
| this.#showLayoutShiftRegionsSetting = settings.resolve(showLayoutShiftRegionsSettingDescriptor); | |
| this.#showAdHighlightsSetting = settings.resolve(showAdHighlightsSettingDescriptor); | |
| this.#showDebugBordersSetting = settings.resolve(showDebugBordersSettingDescriptor); | |
| this.#showFPSCounterSetting = settings.resolve(showFPSCounterSettingDescriptor); | |
| this.#showScrollBottleneckRectsSetting = settings.resolve(showScrollBottleneckRectsSettingDescriptor); | |
| if (!target.suspended()) { | |
| void this.overlayAgent.invoke_enable(); | |
| void this.wireAgentToSettings(); | |
| } | |
| this.#persistentHighlighter = new OverlayPersistentHighlighter(this, settings, { | |
| onGridOverlayStateChanged: ({nodeId, enabled}) => { | |
| this.#domModel.nodeForId(nodeId)?.dispatchEventToListeners(DOMNodeEvents.GRID_OVERLAY_STATE_CHANGED, {enabled}); | |
| this.dispatchEventToListeners(Events.PERSISTENT_GRID_OVERLAY_STATE_CHANGED, {nodeId, enabled}); | |
| }, | |
| onFlexOverlayStateChanged: ({nodeId, enabled}) => { | |
| this.#domModel.nodeForId(nodeId)?.dispatchEventToListeners( | |
| DOMNodeEvents.FLEX_CONTAINER_OVERLAY_STATE_CHANGED, {enabled}); | |
| this.dispatchEventToListeners(Events.PERSISTENT_FLEX_CONTAINER_OVERLAY_STATE_CHANGED, {nodeId, enabled}); | |
| }, | |
| onContainerQueryOverlayStateChanged: ({nodeId, enabled}) => { | |
| this.#domModel.nodeForId(nodeId)?.dispatchEventToListeners( | |
| DOMNodeEvents.CONTAINER_QUERY_OVERLAY_STATE_CHANGED, {enabled}); | |
| this.dispatchEventToListeners(Events.PERSISTENT_CONTAINER_QUERY_OVERLAY_STATE_CHANGED, {nodeId, enabled}); | |
| }, | |
| onScrollSnapOverlayStateChanged: ({nodeId, enabled}) => { | |
| this.#domModel.nodeForId(nodeId)?.dispatchEventToListeners( | |
| DOMNodeEvents.SCROLL_SNAP_OVERLAY_STATE_CHANGED, {enabled}); | |
| this.dispatchEventToListeners(Events.PERSISTENT_SCROLL_SNAP_OVERLAY_STATE_CHANGED, {nodeId, enabled}); | |
| }, | |
| }); | |
| this.#domModel.addEventListener(DOMModelEvents.NodeRemoved, () => { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| this.#persistentHighlighter.refreshHighlights(); | |
| }); | |
| this.#domModel.addEventListener(DOMModelEvents.DocumentUpdated, () => { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| // Hide all the overlays initially after document update | |
| this.#persistentHighlighter.hideAllInOverlayWithoutSave(); | |
| if (!target.suspended()) { | |
| void this.#persistentHighlighter.restoreHighlightsForDocument(); | |
| } | |
| }); | |
| this.#sourceOrderHighlighter = new SourceOrderHighlighter(this); | |
| this.#windowControls = new WindowControls(this.#domModel.cssModel()); | |
| } | |
| static highlightObjectAsDOMNode(object: RemoteObject): void { | |
| const domModel = object.runtimeModel().target().model(DOMModel); | |
| if (domModel) { | |
| domModel.overlayModel().highlightInOverlay({object, selectorList: undefined}); | |
| } | |
| } | |
| static hideDOMNodeHighlight(targetManager: TargetManager): void { | |
| for (const overlayModel of targetManager.models(OverlayModel)) { | |
| overlayModel.delayedHideHighlight(0); | |
| } | |
| } | |
| static async muteHighlight(targetManager: TargetManager): Promise<void[]> { | |
| return await Promise.all(targetManager.models(OverlayModel).map(model => model.suspendModel())); | |
| } | |
| static async unmuteHighlight(targetManager: TargetManager): Promise<void[]> { | |
| return await Promise.all(targetManager.models(OverlayModel).map(model => model.resumeModel())); | |
| } | |
| static highlightRect(rect: HighlightRect, targetManager: TargetManager): void { | |
| for (const overlayModel of targetManager.models(OverlayModel)) { | |
| void overlayModel.highlightRect(rect); | |
| } | |
| } | |
| static clearHighlight(targetManager: TargetManager): void { | |
| for (const overlayModel of targetManager.models(OverlayModel)) { | |
| void overlayModel.clearHighlight(); | |
| } | |
| } | |
| getDOMModel(): DOMModel { | |
| return this.#domModel; | |
| } | |
| highlightRect({x, y, width, height, color, outlineColor}: HighlightRect): | |
| Promise<Protocol.ProtocolResponseWithError> { | |
| const highlightColor = color || {r: 255, g: 0, b: 255, a: 0.3}; | |
| const highlightOutlineColor = outlineColor || {r: 255, g: 0, b: 255, a: 0.5}; | |
| return this.overlayAgent.invoke_highlightRect( | |
| {x, y, width, height, color: highlightColor, outlineColor: highlightOutlineColor}); | |
| } | |
| clearHighlight(): Promise<Protocol.ProtocolResponseWithError> { | |
| return this.overlayAgent.invoke_hideHighlight(); | |
| } | |
| private async wireAgentToSettings(): Promise<void> { | |
| this.#registeredListeners = [ | |
| this.#showPaintRectsSetting.addChangeListener( | |
| () => this.overlayAgent.invoke_setShowPaintRects({result: this.#showPaintRectsSetting.get()})), | |
| this.#showLayoutShiftRegionsSetting.addChangeListener( | |
| () => | |
| this.overlayAgent.invoke_setShowLayoutShiftRegions({result: this.#showLayoutShiftRegionsSetting.get()})), | |
| this.#showAdHighlightsSetting.addChangeListener( | |
| () => this.overlayAgent.invoke_setShowAdHighlights({show: this.#showAdHighlightsSetting.get()})), | |
| this.#showDebugBordersSetting.addChangeListener( | |
| () => this.overlayAgent.invoke_setShowDebugBorders({show: this.#showDebugBordersSetting.get()})), | |
| this.#showFPSCounterSetting.addChangeListener( | |
| () => this.overlayAgent.invoke_setShowFPSCounter({show: this.#showFPSCounterSetting.get()})), | |
| this.#showScrollBottleneckRectsSetting.addChangeListener( | |
| () => this.overlayAgent.invoke_setShowScrollBottleneckRects( | |
| {show: this.#showScrollBottleneckRectsSetting.get()})), | |
| ]; | |
| if (this.#showPaintRectsSetting.get()) { | |
| void this.overlayAgent.invoke_setShowPaintRects({result: true}); | |
| } | |
| if (this.#showLayoutShiftRegionsSetting.get()) { | |
| void this.overlayAgent.invoke_setShowLayoutShiftRegions({result: true}); | |
| } | |
| if (this.#showAdHighlightsSetting.get()) { | |
| void this.overlayAgent.invoke_setShowAdHighlights({show: true}); | |
| } | |
| if (this.#showDebugBordersSetting.get()) { | |
| void this.overlayAgent.invoke_setShowDebugBorders({show: true}); | |
| } | |
| if (this.#showFPSCounterSetting.get()) { | |
| void this.overlayAgent.invoke_setShowFPSCounter({show: true}); | |
| } | |
| if (this.#showScrollBottleneckRectsSetting.get()) { | |
| void this.overlayAgent.invoke_setShowScrollBottleneckRects({show: true}); | |
| } | |
| if (this.#debuggerModel && this.#debuggerModel.isPaused()) { | |
| this.updatePausedInDebuggerMessage(); | |
| } | |
| await this.overlayAgent.invoke_setShowViewportSizeOnResize({show: this.#showViewportSizeOnResize}); | |
| this.#persistentHighlighter?.resetOverlay(); | |
| } | |
| override async suspendModel(): Promise<void> { | |
| Common.EventTarget.removeEventListeners(this.#registeredListeners); | |
| await this.overlayAgent.invoke_disable(); | |
| } | |
| override async resumeModel(): Promise<void> { | |
| await Promise.all([this.overlayAgent.invoke_enable(), this.wireAgentToSettings()]); | |
| } | |
| setShowViewportSizeOnResize(show: boolean): void { | |
| if (this.#showViewportSizeOnResize === show) { | |
| return; | |
| } | |
| this.#showViewportSizeOnResize = show; | |
| if (this.target().suspended()) { | |
| return; | |
| } | |
| void this.overlayAgent.invoke_setShowViewportSizeOnResize({show}); | |
| } | |
| private updatePausedInDebuggerMessage(): void { | |
| if (this.target().suspended()) { | |
| return; | |
| } | |
| const settings = this.target().targetManager().settings; | |
| const message = this.#debuggerModel && this.#debuggerModel.isPaused() && | |
| !settings.moduleSetting('disable-paused-state-overlay').get() ? | |
| i18nString(UIStrings.pausedInDebugger) : | |
| undefined; | |
| void this.overlayAgent.invoke_setPausedInDebuggerMessage({message}); | |
| } | |
| setHighlighter(highlighter: Highlighter|null): void { | |
| this.#highlighter = highlighter || this.#defaultHighlighter; | |
| } | |
| async setInspectMode(mode: Protocol.Overlay.InspectMode, showDetailedTooltip: boolean|undefined = true): | |
| Promise<void> { | |
| await this.#domModel.requestDocument(); | |
| this.#inspectModeEnabled = mode !== Protocol.Overlay.InspectMode.None; | |
| this.dispatchEventToListeners(Events.INSPECT_MODE_WILL_BE_TOGGLED, this); | |
| void this.#highlighter.setInspectMode(mode, this.buildHighlightConfig('all', showDetailedTooltip)); | |
| } | |
| inspectModeEnabled(): boolean { | |
| return this.#inspectModeEnabled; | |
| } | |
| highlightInOverlay(data: HighlightData, mode?: string, showInfo?: boolean): void { | |
| if (this.#sourceOrderModeActive) { | |
| // Return early if the source order is currently being shown the in the | |
| // overlay, so that it is not cleared by the highlight | |
| return; | |
| } | |
| clearTimeout(this.#hideHighlightTimeout); | |
| this.#hideHighlightTimeout = undefined; | |
| const highlightConfig = this.buildHighlightConfig(mode); | |
| if (typeof showInfo !== 'undefined') { | |
| highlightConfig.showInfo = showInfo; | |
| } | |
| this.#highlighter.highlightInOverlay(data, highlightConfig); | |
| } | |
| highlightInOverlayForTwoSeconds(data: HighlightData): void { | |
| this.highlightInOverlay(data); | |
| this.delayedHideHighlight(2000); | |
| } | |
| highlightGridInPersistentOverlay(nodeId: Protocol.DOM.NodeId): void { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| this.#persistentHighlighter.highlightGridInOverlay(nodeId); | |
| } | |
| isHighlightedGridInPersistentOverlay(nodeId: Protocol.DOM.NodeId): boolean { | |
| if (!this.#persistentHighlighter) { | |
| return false; | |
| } | |
| return this.#persistentHighlighter.isGridHighlighted(nodeId); | |
| } | |
| hideGridInPersistentOverlay(nodeId: Protocol.DOM.NodeId): void { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| this.#persistentHighlighter.hideGridInOverlay(nodeId); | |
| } | |
| highlightScrollSnapInPersistentOverlay(nodeId: Protocol.DOM.NodeId): void { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| this.#persistentHighlighter.highlightScrollSnapInOverlay(nodeId); | |
| } | |
| isHighlightedScrollSnapInPersistentOverlay(nodeId: Protocol.DOM.NodeId): boolean { | |
| if (!this.#persistentHighlighter) { | |
| return false; | |
| } | |
| return this.#persistentHighlighter.isScrollSnapHighlighted(nodeId); | |
| } | |
| hideScrollSnapInPersistentOverlay(nodeId: Protocol.DOM.NodeId): void { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| this.#persistentHighlighter.hideScrollSnapInOverlay(nodeId); | |
| } | |
| highlightFlexContainerInPersistentOverlay(nodeId: Protocol.DOM.NodeId): void { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| this.#persistentHighlighter.highlightFlexInOverlay(nodeId); | |
| } | |
| isHighlightedFlexContainerInPersistentOverlay(nodeId: Protocol.DOM.NodeId): boolean { | |
| if (!this.#persistentHighlighter) { | |
| return false; | |
| } | |
| return this.#persistentHighlighter.isFlexHighlighted(nodeId); | |
| } | |
| hideFlexContainerInPersistentOverlay(nodeId: Protocol.DOM.NodeId): void { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| this.#persistentHighlighter.hideFlexInOverlay(nodeId); | |
| } | |
| highlightContainerQueryInPersistentOverlay(nodeId: Protocol.DOM.NodeId): void { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| this.#persistentHighlighter.highlightContainerQueryInOverlay(nodeId); | |
| } | |
| isHighlightedContainerQueryInPersistentOverlay(nodeId: Protocol.DOM.NodeId): boolean { | |
| if (!this.#persistentHighlighter) { | |
| return false; | |
| } | |
| return this.#persistentHighlighter.isContainerQueryHighlighted(nodeId); | |
| } | |
| hideContainerQueryInPersistentOverlay(nodeId: Protocol.DOM.NodeId): void { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| this.#persistentHighlighter.hideContainerQueryInOverlay(nodeId); | |
| } | |
| highlightSourceOrderInOverlay(node: DOMNode): void { | |
| const sourceOrderConfig = { | |
| parentOutlineColor: Common.Color.SourceOrderHighlight.ParentOutline.toProtocolRGBA(), | |
| childOutlineColor: Common.Color.SourceOrderHighlight.ChildOutline.toProtocolRGBA(), | |
| }; | |
| this.#sourceOrderHighlighter.highlightSourceOrderInOverlay(node, sourceOrderConfig); | |
| } | |
| colorOfGridInPersistentOverlay(nodeId: Protocol.DOM.NodeId): string|null { | |
| if (!this.#persistentHighlighter) { | |
| return null; | |
| } | |
| return this.#persistentHighlighter.colorOfGrid(nodeId).asString(Common.Color.Format.HEX); | |
| } | |
| setColorOfGridInPersistentOverlay(nodeId: Protocol.DOM.NodeId, colorStr: string): void { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| const color = Common.Color.parse(colorStr); | |
| if (!color) { | |
| return; | |
| } | |
| this.#persistentHighlighter.setColorOfGrid(nodeId, color); | |
| this.#persistentHighlighter.resetOverlay(); | |
| } | |
| colorOfFlexInPersistentOverlay(nodeId: Protocol.DOM.NodeId): string|null { | |
| if (!this.#persistentHighlighter) { | |
| return null; | |
| } | |
| return this.#persistentHighlighter.colorOfFlex(nodeId).asString(Common.Color.Format.HEX); | |
| } | |
| setColorOfFlexInPersistentOverlay(nodeId: Protocol.DOM.NodeId, colorStr: string): void { | |
| if (!this.#persistentHighlighter) { | |
| return; | |
| } | |
| const color = Common.Color.parse(colorStr); | |
| if (!color) { | |
| return; | |
| } | |
| this.#persistentHighlighter.setColorOfFlex(nodeId, color); | |
| this.#persistentHighlighter.resetOverlay(); | |
| } | |
| hideSourceOrderInOverlay(): void { | |
| this.#sourceOrderHighlighter.hideSourceOrderHighlight(); | |
| } | |
| setSourceOrderActive(isActive: boolean): void { | |
| this.#sourceOrderModeActive = isActive; | |
| } | |
| private delayedHideHighlight(delay: number): void { | |
| if (this.#hideHighlightTimeout === undefined) { | |
| this.#hideHighlightTimeout = globalThis.setTimeout( | |
| () => this.highlightInOverlay({clear: true}), | |
| delay, | |
| ); | |
| } | |
| } | |
| highlightFrame(frameId: Protocol.Page.FrameId): void { | |
| clearTimeout(this.#hideHighlightTimeout); | |
| this.#hideHighlightTimeout = undefined; | |
| this.#highlighter.highlightFrame(frameId); | |
| } | |
| showHingeForDualScreen(hinge: Hinge|null): void { | |
| if (hinge) { | |
| const {x, y, width, height, contentColor, outlineColor} = hinge; | |
| void this.overlayAgent.invoke_setShowHinge({ | |
| hingeConfig: {rect: {x, y, width, height}, contentColor, outlineColor}, | |
| }); | |
| } else { | |
| void this.overlayAgent.invoke_setShowHinge({}); | |
| } | |
| } | |
| showDisplayCutout(cutout: DisplayCutout|null): void { | |
| if (cutout) { | |
| const {x, y, width, height, shape, contentColor} = cutout; | |
| const displayCutoutConfig: Protocol.Overlay.DisplayCutoutConfig = { | |
| rect: {x, y, width, height}, | |
| shape, | |
| contentColor, | |
| }; | |
| if (shape === Protocol.Overlay.DisplayCutoutShape.Pill) { | |
| displayCutoutConfig.borderRadius = cutout.borderRadius; | |
| } else if (shape === Protocol.Overlay.DisplayCutoutShape.Notch) { | |
| displayCutoutConfig.upperRadius = cutout.upperRadius; | |
| displayCutoutConfig.lowerRadius = cutout.lowerRadius; | |
| } else if (shape === Protocol.Overlay.DisplayCutoutShape.Circle) { | |
| displayCutoutConfig.cx = cutout.cx; | |
| displayCutoutConfig.cy = cutout.cy; | |
| displayCutoutConfig.radius = cutout.radius; | |
| } | |
| void this.overlayAgent.invoke_setShowDisplayCutout({ | |
| displayCutoutConfig, | |
| }); | |
| } else { | |
| void this.overlayAgent.invoke_setShowDisplayCutout({}); | |
| } | |
| } | |
| setWindowControlsPlatform(selectedPlatform: EmulatedOSType): void { | |
| this.#windowControls.selectedPlatform = selectedPlatform; | |
| } | |
| setWindowControlsThemeColor(themeColor: string): void { | |
| this.#windowControls.themeColor = themeColor; | |
| } | |
| getWindowControlsConfig(): Protocol.Overlay.WindowControlsOverlayConfig { | |
| return this.#windowControls.config; | |
| } | |
| async toggleWindowControlsToolbar(show: boolean): Promise<void> { | |
| const wcoConfigObj = show ? {windowControlsOverlayConfig: this.#windowControls.config} : {}; | |
| const setWindowControlsOverlayOperation = this.overlayAgent.invoke_setShowWindowControlsOverlay(wcoConfigObj); | |
| const toggleStylesheetOperation = this.#windowControls.toggleEmulatedOverlay(show); | |
| await Promise.all([setWindowControlsOverlayOperation, toggleStylesheetOperation]); | |
| this.setShowViewportSizeOnResize(!show); | |
| } | |
| private buildHighlightConfig(mode: string|undefined = 'all', showDetailedToolip: boolean|undefined = false): | |
| Protocol.Overlay.HighlightConfig { | |
| const settings = this.target().targetManager().settings; | |
| const showRulers = settings.resolve(showMetricsRulersSettingDescriptor).get(); | |
| const highlightConfig: Protocol.Overlay.HighlightConfig = { | |
| showInfo: mode === 'all' || mode === 'container-outline', | |
| showRulers, | |
| showStyles: showDetailedToolip, | |
| showAccessibilityInfo: showDetailedToolip, | |
| showExtensionLines: showRulers, | |
| gridHighlightConfig: {}, | |
| flexContainerHighlightConfig: {}, | |
| flexItemHighlightConfig: {}, | |
| contrastAlgorithm: settings.resolve(apcaSettingDescriptor).get() ? Protocol.Overlay.ContrastAlgorithm.Apca : | |
| Protocol.Overlay.ContrastAlgorithm.Aa, | |
| }; | |
| if (mode === 'all' || mode === 'content') { | |
| highlightConfig.contentColor = Common.Color.PageHighlight.Content.toProtocolRGBA(); | |
| } | |
| if (mode === 'all' || mode === 'padding') { | |
| highlightConfig.paddingColor = Common.Color.PageHighlight.Padding.toProtocolRGBA(); | |
| } | |
| if (mode === 'all' || mode === 'border') { | |
| highlightConfig.borderColor = Common.Color.PageHighlight.Border.toProtocolRGBA(); | |
| } | |
| if (mode === 'all' || mode === 'margin') { | |
| highlightConfig.marginColor = Common.Color.PageHighlight.Margin.toProtocolRGBA(); | |
| } | |
| if (mode === 'all') { | |
| highlightConfig.eventTargetColor = Common.Color.PageHighlight.EventTarget.toProtocolRGBA(); | |
| highlightConfig.shapeColor = Common.Color.PageHighlight.Shape.toProtocolRGBA(); | |
| highlightConfig.shapeMarginColor = Common.Color.PageHighlight.ShapeMargin.toProtocolRGBA(); | |
| highlightConfig.gridHighlightConfig = { | |
| rowGapColor: Common.Color.PageHighlight.GapBackground.toProtocolRGBA(), | |
| rowHatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| columnGapColor: Common.Color.PageHighlight.GapBackground.toProtocolRGBA(), | |
| columnHatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| rowLineColor: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| columnLineColor: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| rowLineDash: true, | |
| columnLineDash: true, | |
| }; | |
| highlightConfig.flexContainerHighlightConfig = { | |
| containerBorder: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| pattern: Protocol.Overlay.LineStylePattern.Dashed, | |
| }, | |
| itemSeparator: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| pattern: Protocol.Overlay.LineStylePattern.Dotted, | |
| }, | |
| lineSeparator: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| pattern: Protocol.Overlay.LineStylePattern.Dashed, | |
| }, | |
| mainDistributedSpace: { | |
| hatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| fillColor: Common.Color.PageHighlight.GapBackground.toProtocolRGBA(), | |
| }, | |
| crossDistributedSpace: { | |
| hatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| fillColor: Common.Color.PageHighlight.GapBackground.toProtocolRGBA(), | |
| }, | |
| rowGapSpace: { | |
| hatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| fillColor: Common.Color.PageHighlight.GapBackground.toProtocolRGBA(), | |
| }, | |
| columnGapSpace: { | |
| hatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| fillColor: Common.Color.PageHighlight.GapBackground.toProtocolRGBA(), | |
| }, | |
| }; | |
| highlightConfig.flexItemHighlightConfig = { | |
| baseSizeBox: { | |
| hatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| }, | |
| baseSizeBorder: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| pattern: Protocol.Overlay.LineStylePattern.Dotted, | |
| }, | |
| flexibilityArrow: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| }, | |
| }; | |
| } | |
| if (mode.endsWith('gap')) { | |
| highlightConfig.gridHighlightConfig = { | |
| gridBorderColor: Common.Color.PageHighlight.GridBorder.toProtocolRGBA(), | |
| gridBorderDash: true, | |
| }; | |
| if (mode === 'gap' || mode === 'row-gap') { | |
| highlightConfig.gridHighlightConfig.rowGapColor = Common.Color.PageHighlight.GapBackground.toProtocolRGBA(); | |
| highlightConfig.gridHighlightConfig.rowHatchColor = Common.Color.PageHighlight.GapHatch.toProtocolRGBA(); | |
| } | |
| if (mode === 'gap' || mode === 'column-gap') { | |
| highlightConfig.gridHighlightConfig.columnGapColor = Common.Color.PageHighlight.GapBackground.toProtocolRGBA(); | |
| highlightConfig.gridHighlightConfig.columnHatchColor = Common.Color.PageHighlight.GapHatch.toProtocolRGBA(); | |
| } | |
| } | |
| if (mode.endsWith('gap')) { | |
| highlightConfig.flexContainerHighlightConfig = { | |
| containerBorder: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| pattern: Protocol.Overlay.LineStylePattern.Dashed, | |
| }, | |
| }; | |
| if (mode === 'gap' || mode === 'row-gap') { | |
| highlightConfig.flexContainerHighlightConfig.rowGapSpace = { | |
| hatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| fillColor: Common.Color.PageHighlight.GapBackground.toProtocolRGBA(), | |
| }; | |
| } | |
| if (mode === 'gap' || mode === 'column-gap') { | |
| highlightConfig.flexContainerHighlightConfig.columnGapSpace = { | |
| hatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| fillColor: Common.Color.PageHighlight.GapBackground.toProtocolRGBA(), | |
| }; | |
| } | |
| } | |
| if (mode === 'grid-areas') { | |
| highlightConfig.gridHighlightConfig = { | |
| rowLineColor: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| columnLineColor: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| rowLineDash: true, | |
| columnLineDash: true, | |
| showAreaNames: true, | |
| areaBorderColor: Common.Color.PageHighlight.GridAreaBorder.toProtocolRGBA(), | |
| }; | |
| } | |
| if (mode === 'grid-template-columns') { | |
| highlightConfig.contentColor = Common.Color.PageHighlight.Content.toProtocolRGBA(); | |
| highlightConfig.gridHighlightConfig = { | |
| columnLineColor: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| columnLineDash: true, | |
| }; | |
| } | |
| if (mode === 'grid-template-rows') { | |
| highlightConfig.contentColor = Common.Color.PageHighlight.Content.toProtocolRGBA(); | |
| highlightConfig.gridHighlightConfig = { | |
| rowLineColor: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| rowLineDash: true, | |
| }; | |
| } | |
| if (mode === 'justify-content') { | |
| highlightConfig.flexContainerHighlightConfig = { | |
| containerBorder: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| pattern: Protocol.Overlay.LineStylePattern.Dashed, | |
| }, | |
| mainDistributedSpace: { | |
| hatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| fillColor: Common.Color.PageHighlight.GapBackground.toProtocolRGBA(), | |
| }, | |
| }; | |
| } | |
| if (mode === 'align-content') { | |
| highlightConfig.flexContainerHighlightConfig = { | |
| containerBorder: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| pattern: Protocol.Overlay.LineStylePattern.Dashed, | |
| }, | |
| crossDistributedSpace: { | |
| hatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| fillColor: Common.Color.PageHighlight.GapBackground.toProtocolRGBA(), | |
| }, | |
| }; | |
| } | |
| if (mode === 'align-items') { | |
| highlightConfig.flexContainerHighlightConfig = { | |
| containerBorder: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| pattern: Protocol.Overlay.LineStylePattern.Dashed, | |
| }, | |
| lineSeparator: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| pattern: Protocol.Overlay.LineStylePattern.Dashed, | |
| }, | |
| crossAlignment: {color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA()}, | |
| }; | |
| } | |
| if (mode === 'flexibility') { | |
| highlightConfig.flexItemHighlightConfig = { | |
| baseSizeBox: { | |
| hatchColor: Common.Color.PageHighlight.GapHatch.toProtocolRGBA(), | |
| }, | |
| baseSizeBorder: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| pattern: Protocol.Overlay.LineStylePattern.Dotted, | |
| }, | |
| flexibilityArrow: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| }, | |
| }; | |
| } | |
| if (mode === 'container-outline') { | |
| highlightConfig.containerQueryContainerHighlightConfig = { | |
| containerBorder: { | |
| color: Common.Color.PageHighlight.LayoutLine.toProtocolRGBA(), | |
| pattern: Protocol.Overlay.LineStylePattern.Dashed, | |
| }, | |
| }; | |
| } | |
| return highlightConfig; | |
| } | |
| nodeHighlightRequested({nodeId}: Protocol.Overlay.NodeHighlightRequestedEvent): void { | |
| const node = this.#domModel.nodeForId(nodeId); | |
| if (node) { | |
| this.dispatchEventToListeners(Events.HIGHLIGHT_NODE_REQUESTED, node); | |
| } | |
| } | |
| static setInspectNodeHandler(handler: (arg0: DOMNode) => Promise<void>): void { | |
| OverlayModel.inspectNodeHandler = handler; | |
| } | |
| inspectNodeRequested({backendNodeId}: Protocol.Overlay.InspectNodeRequestedEvent): void { | |
| const deferredNode = new DeferredDOMNode(this.target(), backendNodeId); | |
| if (OverlayModel.inspectNodeHandler) { | |
| void deferredNode.resolvePromise().then(node => { | |
| if (node && OverlayModel.inspectNodeHandler) { | |
| void OverlayModel.inspectNodeHandler(node); | |
| } | |
| }); | |
| } else { | |
| void Common.Revealer.reveal(deferredNode); | |
| } | |
| this.dispatchEventToListeners(Events.EXITED_INSPECT_MODE); | |
| } | |
| screenshotRequested({viewport}: Protocol.Overlay.ScreenshotRequestedEvent): void { | |
| this.dispatchEventToListeners(Events.SCREENSHOT_REQUESTED, viewport); | |
| this.dispatchEventToListeners(Events.EXITED_INSPECT_MODE); | |
| } | |
| inspectModeCanceled(): void { | |
| this.dispatchEventToListeners(Events.EXITED_INSPECT_MODE); | |
| } | |
| static inspectNodeHandler: ((node: DOMNode) => Promise<void>)|null = null; | |
| getOverlayAgent(): ProtocolProxyApi.OverlayApi { | |
| return this.overlayAgent; | |
| } | |
| async hasStyleSheetText(url: Platform.DevToolsPath.UrlString): Promise<boolean> { | |
| return await this.#windowControls.initializeStyleSheetText(url); | |
| } | |
| inspectPanelShowRequested({backendNodeId}: Protocol.Overlay.InspectPanelShowRequestedEvent): void { | |
| this.dispatchEventToListeners(Events.INSPECT_PANEL_SHOW_REQUESTED, backendNodeId); | |
| } | |
| inspectedElementWindowRestored({backendNodeId}: Protocol.Overlay.InspectedElementWindowRestoredEvent): void { | |
| this.dispatchEventToListeners(Events.INSPECTED_ELEMENT_WINDOW_RESTORED, backendNodeId); | |
| } | |
| } | |
| export class WindowControls { | |
| readonly #cssModel: CSSModel; | |
| #originalStylesheetText: string|undefined; | |
| #stylesheetId?: Protocol.DOM.StyleSheetId; | |
| #currentUrl: Platform.DevToolsPath.UrlString|undefined; | |
| #config: Protocol.Overlay.WindowControlsOverlayConfig = { | |
| showCSS: false, | |
| selectedPlatform: EmulatedOSType.WINDOWS, | |
| themeColor: '#ffffff', | |
| }; | |
| constructor(cssModel: CSSModel) { | |
| this.#cssModel = cssModel; | |
| } | |
| get selectedPlatform(): string { | |
| return this.#config.selectedPlatform; | |
| } | |
| set selectedPlatform(osType: EmulatedOSType) { | |
| this.#config.selectedPlatform = osType; | |
| } | |
| get themeColor(): string { | |
| return this.#config.themeColor; | |
| } | |
| set themeColor(color: string) { | |
| this.#config.themeColor = color; | |
| } | |
| get config(): Protocol.Overlay.WindowControlsOverlayConfig { | |
| return this.#config; | |
| } | |
| async initializeStyleSheetText(url: Platform.DevToolsPath.UrlString): Promise<boolean> { | |
| if (this.#originalStylesheetText && url === this.#currentUrl) { | |
| return true; | |
| } | |
| const cssSourceUrl = this.#fetchCssSourceUrl(url); | |
| if (!cssSourceUrl) { | |
| return false; | |
| } | |
| this.#stylesheetId = this.#fetchCurrentStyleSheet(cssSourceUrl); | |
| if (!this.#stylesheetId) { | |
| return false; | |
| } | |
| const stylesheetText = await this.#cssModel.getStyleSheetText(this.#stylesheetId); | |
| if (!stylesheetText) { | |
| return false; | |
| } | |
| this.#originalStylesheetText = stylesheetText; | |
| this.#currentUrl = url; | |
| return true; | |
| } | |
| async toggleEmulatedOverlay(showOverlay: boolean): Promise<void> { | |
| if (!this.#stylesheetId || !this.#originalStylesheetText) { | |
| return; | |
| } | |
| if (showOverlay) { | |
| const styleSheetText = WindowControls.#getStyleSheetForPlatform( | |
| this.#config.selectedPlatform.toLowerCase(), this.#originalStylesheetText); | |
| if (styleSheetText) { | |
| await this.#cssModel.setStyleSheetText(this.#stylesheetId, styleSheetText, false); | |
| } | |
| } else { | |
| // Restore the original stylesheet | |
| await this.#cssModel.setStyleSheetText(this.#stylesheetId, this.#originalStylesheetText, false); | |
| } | |
| } | |
| static #getStyleSheetForPlatform(platform: string, originalStyleSheet: string|undefined): string|undefined { | |
| const overlayDimensions = platformOverlayDimensions[platform as keyof PlatformOverlayDimensions]; | |
| return WindowControls.#transformStyleSheet( | |
| overlayDimensions.x, overlayDimensions.y, overlayDimensions.width, overlayDimensions.height, | |
| originalStyleSheet); | |
| } | |
| #fetchCssSourceUrl(url: Platform.DevToolsPath.UrlString): Platform.DevToolsPath.UrlString|undefined { | |
| const parentURL = Common.ParsedURL.ParsedURL.extractOrigin(url); | |
| const cssHeaders = this.#cssModel.styleSheetHeaders(); | |
| const header = cssHeaders.find(header => header.sourceURL && header.sourceURL.includes(parentURL)); | |
| return header?.sourceURL; | |
| } | |
| #fetchCurrentStyleSheet(cssSourceUrl: Platform.DevToolsPath.UrlString): Protocol.DOM.StyleSheetId|undefined { | |
| const stylesheetIds = this.#cssModel.getStyleSheetIdsForURL(cssSourceUrl); | |
| return stylesheetIds.length > 0 ? stylesheetIds[0] : undefined; | |
| } | |
| // The primary objective of this function is to adjust certain CSS environment variables within the existing stylesheet | |
| // and provide it as the style sheet for the emulated overlay. | |
| static #transformStyleSheet( | |
| x: number, y: number, width: number, height: number, originalStyleSheet: string|undefined): string|undefined { | |
| if (!originalStyleSheet) { | |
| return undefined; | |
| } | |
| const stylesheetText = originalStyleSheet; | |
| const updatedStylesheet = | |
| stylesheetText.replace(/: env\(titlebar-area-x(?:,[^)]*)?\);/g, `: env(titlebar-area-x, ${x}px);`) | |
| .replace(/: env\(titlebar-area-y(?:,[^)]*)?\);/g, `: env(titlebar-area-y, ${y}px);`) | |
| .replace( | |
| /: env\(titlebar-area-width(?:,[^)]*)?\);/g, `: env(titlebar-area-width, calc(100% - ${width}px));`) | |
| .replace(/: env\(titlebar-area-height(?:,[^)]*)?\);/g, `: env(titlebar-area-height, ${height}px);`); | |
| return updatedStylesheet; | |
| } | |
| transformStyleSheetforTesting( | |
| x: number, y: number, width: number, height: number, originalStyleSheet: string|undefined): string|undefined { | |
| return WindowControls.#transformStyleSheet(x, y, width, height, originalStyleSheet); | |
| } | |
| } | |
| export const enum Events { | |
| INSPECT_MODE_WILL_BE_TOGGLED = 'InspectModeWillBeToggled', | |
| EXITED_INSPECT_MODE = 'InspectModeExited', | |
| HIGHLIGHT_NODE_REQUESTED = 'HighlightNodeRequested', | |
| SCREENSHOT_REQUESTED = 'ScreenshotRequested', | |
| PERSISTENT_GRID_OVERLAY_STATE_CHANGED = 'PersistentGridOverlayStateChanged', | |
| PERSISTENT_FLEX_CONTAINER_OVERLAY_STATE_CHANGED = 'PersistentFlexContainerOverlayStateChanged', | |
| PERSISTENT_SCROLL_SNAP_OVERLAY_STATE_CHANGED = 'PersistentScrollSnapOverlayStateChanged', | |
| PERSISTENT_CONTAINER_QUERY_OVERLAY_STATE_CHANGED = 'PersistentContainerQueryOverlayStateChanged', | |
| INSPECT_PANEL_SHOW_REQUESTED = 'InspectPanelShowRequested', | |
| INSPECTED_ELEMENT_WINDOW_RESTORED = 'InspectedElementWindowRestored', | |
| } | |
| export interface ChangedNodeId { | |
| nodeId: number; | |
| enabled: boolean; | |
| } | |
| export interface EventTypes { | |
| [Events.INSPECT_MODE_WILL_BE_TOGGLED]: OverlayModel; | |
| [Events.EXITED_INSPECT_MODE]: void; | |
| [Events.HIGHLIGHT_NODE_REQUESTED]: DOMNode; | |
| [Events.SCREENSHOT_REQUESTED]: Protocol.Page.Viewport; | |
| [Events.PERSISTENT_GRID_OVERLAY_STATE_CHANGED]: ChangedNodeId; | |
| [Events.PERSISTENT_FLEX_CONTAINER_OVERLAY_STATE_CHANGED]: ChangedNodeId; | |
| [Events.PERSISTENT_SCROLL_SNAP_OVERLAY_STATE_CHANGED]: ChangedNodeId; | |
| [Events.PERSISTENT_CONTAINER_QUERY_OVERLAY_STATE_CHANGED]: ChangedNodeId; | |
| [Events.INSPECT_PANEL_SHOW_REQUESTED]: number; | |
| [Events.INSPECTED_ELEMENT_WINDOW_RESTORED]: number; | |
| } | |
| export interface Highlighter { | |
| highlightInOverlay(data: HighlightData, config: Protocol.Overlay.HighlightConfig): void; | |
| setInspectMode(mode: Protocol.Overlay.InspectMode, config: Protocol.Overlay.HighlightConfig): Promise<void>; | |
| highlightFrame(frameId: Protocol.Page.FrameId): void; | |
| } | |
| class DefaultHighlighter implements Highlighter { | |
| readonly #model: OverlayModel; | |
| constructor(model: OverlayModel) { | |
| this.#model = model; | |
| } | |
| highlightInOverlay(data: HighlightData, highlightConfig: Protocol.Overlay.HighlightConfig): void { | |
| const {node, deferredNode, object, selectorList} = | |
| {node: undefined, deferredNode: undefined, object: undefined, selectorList: undefined, ...data}; | |
| const nodeId = node ? node.id : undefined; | |
| const backendNodeId = deferredNode ? deferredNode.backendNodeId() : undefined; | |
| const objectId = object ? object.objectId : undefined; | |
| if (nodeId || backendNodeId || objectId) { | |
| void this.#model.target().overlayAgent().invoke_highlightNode( | |
| {highlightConfig, nodeId, backendNodeId, objectId, selector: selectorList}); | |
| } else { | |
| void this.#model.target().overlayAgent().invoke_hideHighlight(); | |
| } | |
| } | |
| async setInspectMode(mode: Protocol.Overlay.InspectMode, highlightConfig: Protocol.Overlay.HighlightConfig): | |
| Promise<void> { | |
| await this.#model.target().overlayAgent().invoke_setInspectMode({mode, highlightConfig}); | |
| } | |
| highlightFrame(frameId: Protocol.Page.FrameId): void { | |
| void this.#model.target().overlayAgent().invoke_highlightFrame({ | |
| frameId, | |
| contentColor: Common.Color.PageHighlight.Content.toProtocolRGBA(), | |
| contentOutlineColor: Common.Color.PageHighlight.ContentOutline.toProtocolRGBA(), | |
| }); | |
| } | |
| } | |
| export class SourceOrderHighlighter { | |
| readonly #model: OverlayModel; | |
| constructor(model: OverlayModel) { | |
| this.#model = model; | |
| } | |
| highlightSourceOrderInOverlay(node: DOMNode, sourceOrderConfig: Protocol.Overlay.SourceOrderConfig): void { | |
| this.#model.setSourceOrderActive(true); | |
| this.#model.setShowViewportSizeOnResize(false); | |
| void this.#model.getOverlayAgent().invoke_highlightSourceOrder({sourceOrderConfig, nodeId: node.id}); | |
| } | |
| hideSourceOrderHighlight(): void { | |
| this.#model.setSourceOrderActive(false); | |
| this.#model.setShowViewportSizeOnResize(true); | |
| void this.#model.clearHighlight(); | |
| } | |
| } | |
| SDKModel.register(OverlayModel, {capabilities: Capability.DOM, autostart: true}); | |
| export interface HighlightNodeData { | |
| node: DOMNode; | |
| selectorList?: string; | |
| } | |
| export interface HighlightDeferredNode { | |
| deferredNode: DeferredDOMNode; | |
| } | |
| export interface HighlightObjectData { | |
| object: RemoteObject; | |
| selectorList?: string; | |
| } | |
| export type HighlightData = HighlightNodeData|HighlightDeferredNode|HighlightObjectData|{clear: boolean}; |