blob: 486f823ebb08ad624a0c5e752e1003ca04a2d47f [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @implements {Protocol.OverlayDispatcher}
*/
export default class OverlayModel extends SDK.SDKModel {
/**
* @param {!SDK.Target} target
*/
constructor(target) {
super(target);
this._domModel = /** @type {!SDK.DOMModel} */ (target.model(SDK.DOMModel));
target.registerOverlayDispatcher(this);
this._overlayAgent = target.overlayAgent();
this._debuggerModel = target.model(SDK.DebuggerModel);
if (this._debuggerModel) {
Common.moduleSetting('disablePausedStateOverlay').addChangeListener(this._updatePausedInDebuggerMessage, this);
this._debuggerModel.addEventListener(
SDK.DebuggerModel.Events.DebuggerPaused, this._updatePausedInDebuggerMessage, this);
this._debuggerModel.addEventListener(
SDK.DebuggerModel.Events.DebuggerResumed, this._updatePausedInDebuggerMessage, this);
// TODO(dgozman): we should get DebuggerResumed on navigations instead of listening to GlobalObjectCleared.
this._debuggerModel.addEventListener(
SDK.DebuggerModel.Events.GlobalObjectCleared, this._updatePausedInDebuggerMessage, this);
}
this._inspectModeEnabled = false;
this._hideHighlightTimeout = null;
this._defaultHighlighter = new DefaultHighlighter(this);
this._highlighter = this._defaultHighlighter;
this._showPaintRectsSetting = Common.moduleSetting('showPaintRects');
this._showLayoutShiftRegionsSetting = Common.moduleSetting('showLayoutShiftRegions');
this._showAdHighlightsSetting = Common.moduleSetting('showAdHighlights');
this._showDebugBordersSetting = Common.moduleSetting('showDebugBorders');
this._showFPSCounterSetting = Common.moduleSetting('showFPSCounter');
this._showScrollBottleneckRectsSetting = Common.moduleSetting('showScrollBottleneckRects');
this._showHitTestBordersSetting = Common.moduleSetting('showHitTestBorders');
this._registeredListeners = [];
this._showViewportSizeOnResize = true;
if (!target.suspended()) {
this._overlayAgent.enable();
this._wireAgentToSettings();
}
}
/**
* @param {!SDK.RemoteObject} object
*/
static highlightObjectAsDOMNode(object) {
const domModel = object.runtimeModel().target().model(SDK.DOMModel);
if (domModel) {
domModel.overlayModel().highlightInOverlay({object});
}
}
static hideDOMNodeHighlight() {
for (const overlayModel of SDK.targetManager.models(OverlayModel)) {
overlayModel._delayedHideHighlight(0);
}
}
static async muteHighlight() {
return Promise.all(SDK.targetManager.models(OverlayModel).map(model => model.suspendModel()));
}
static async unmuteHighlight() {
return Promise.all(SDK.targetManager.models(OverlayModel).map(model => model.resumeModel()));
}
/**
* @return {!Promise}
*/
_wireAgentToSettings() {
this._registeredListeners = [
this._showPaintRectsSetting.addChangeListener(
() => this._overlayAgent.setShowPaintRects(this._showPaintRectsSetting.get())),
this._showLayoutShiftRegionsSetting.addChangeListener(
() => this._overlayAgent.setShowLayoutShiftRegions(this._showLayoutShiftRegionsSetting.get())),
this._showAdHighlightsSetting.addChangeListener(
() => this._overlayAgent.setShowAdHighlights(this._showAdHighlightsSetting.get())),
this._showDebugBordersSetting.addChangeListener(
() => this._overlayAgent.setShowDebugBorders(this._showDebugBordersSetting.get())),
this._showFPSCounterSetting.addChangeListener(
() => this._overlayAgent.setShowFPSCounter(this._showFPSCounterSetting.get())),
this._showScrollBottleneckRectsSetting.addChangeListener(
() => this._overlayAgent.setShowScrollBottleneckRects(this._showScrollBottleneckRectsSetting.get())),
this._showHitTestBordersSetting.addChangeListener(
() => this._overlayAgent.setShowHitTestBorders(this._showHitTestBordersSetting.get()))
];
if (this._showPaintRectsSetting.get()) {
this._overlayAgent.setShowPaintRects(true);
}
if (this._showLayoutShiftRegionsSetting.get()) {
this._overlayAgent.setShowLayoutShiftRegions(true);
}
if (this._showAdHighlightsSetting.get()) {
this._overlayAgent.setShowAdHighlights(true);
}
if (this._showDebugBordersSetting.get()) {
this._overlayAgent.setShowDebugBorders(true);
}
if (this._showFPSCounterSetting.get()) {
this._overlayAgent.setShowFPSCounter(true);
}
if (this._showScrollBottleneckRectsSetting.get()) {
this._overlayAgent.setShowScrollBottleneckRects(true);
}
if (this._showHitTestBordersSetting.get()) {
this._overlayAgent.setShowHitTestBorders(true);
}
if (this._debuggerModel.isPaused()) {
this._updatePausedInDebuggerMessage();
}
return this._overlayAgent.setShowViewportSizeOnResize(this._showViewportSizeOnResize);
}
/**
* @override
* @return {!Promise}
*/
suspendModel() {
Common.EventTarget.removeEventListeners(this._registeredListeners);
return this._overlayAgent.disable();
}
/**
* @override
* @return {!Promise}
*/
resumeModel() {
this._overlayAgent.enable();
return this._wireAgentToSettings();
}
/**
* @param {boolean} show
*/
setShowViewportSizeOnResize(show) {
this._showViewportSizeOnResize = show;
if (this.target().suspended()) {
return;
}
this._overlayAgent.setShowViewportSizeOnResize(show);
}
/**
* @return {!Promise}
*/
_updatePausedInDebuggerMessage() {
if (this.target().suspended()) {
return Promise.resolve();
}
const message = this._debuggerModel.isPaused() && !Common.moduleSetting('disablePausedStateOverlay').get() ?
Common.UIString('Paused in debugger') :
undefined;
return this._overlayAgent.setPausedInDebuggerMessage(message);
}
/**
* @param {?Highlighter} highlighter
*/
setHighlighter(highlighter) {
this._highlighter = highlighter || this._defaultHighlighter;
}
/**
* @param {!Protocol.Overlay.InspectMode} mode
* @param {boolean=} showStyles
* @return {!Promise}
*/
async setInspectMode(mode, showStyles = true) {
await this._domModel.requestDocument();
this._inspectModeEnabled = mode !== Protocol.Overlay.InspectMode.None;
this.dispatchEventToListeners(Events.InspectModeWillBeToggled, this);
this._highlighter.setInspectMode(mode, this._buildHighlightConfig('all', showStyles));
}
/**
* @return {boolean}
*/
inspectModeEnabled() {
return this._inspectModeEnabled;
}
/**
* @param {!SDK.OverlayModel.HighlightData} data
* @param {string=} mode
* @param {boolean=} showInfo
*/
highlightInOverlay(data, mode, showInfo) {
if (this._hideHighlightTimeout) {
clearTimeout(this._hideHighlightTimeout);
this._hideHighlightTimeout = null;
}
const highlightConfig = this._buildHighlightConfig(mode);
if (typeof showInfo !== 'undefined') {
highlightConfig.showInfo = showInfo;
}
this._highlighter.highlightInOverlay(data, highlightConfig);
}
/**
* @param {!SDK.OverlayModel.HighlightData} data
*/
highlightInOverlayForTwoSeconds(data) {
this.highlightInOverlay(data);
this._delayedHideHighlight(2000);
}
/**
* @param {number} delay
*/
_delayedHideHighlight(delay) {
if (this._hideHighlightTimeout === null) {
this._hideHighlightTimeout = setTimeout(() => this.highlightInOverlay({}), delay);
}
}
/**
* @param {!Protocol.Page.FrameId} frameId
*/
highlightFrame(frameId) {
if (this._hideHighlightTimeout) {
clearTimeout(this._hideHighlightTimeout);
this._hideHighlightTimeout = null;
}
this._highlighter.highlightFrame(frameId);
}
/**
* @param {string=} mode
* @param {boolean=} showStyles
* @return {!Protocol.Overlay.HighlightConfig}
*/
_buildHighlightConfig(mode = 'all', showStyles = false) {
const showRulers = Common.moduleSetting('showMetricsRulers').get();
const highlightConfig =
{showInfo: mode === 'all', showRulers: showRulers, showStyles, showExtensionLines: showRulers};
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();
}
if (mode === 'all') {
highlightConfig.cssGridColor = Common.Color.PageHighlight.CssGrid.toProtocolRGBA();
}
return highlightConfig;
}
/**
* @override
* @param {!Protocol.DOM.NodeId} nodeId
*/
nodeHighlightRequested(nodeId) {
const node = this._domModel.nodeForId(nodeId);
if (node) {
this.dispatchEventToListeners(Events.HighlightNodeRequested, node);
}
}
/**
* @param {function(!SDK.DOMNode)} handler
*/
static setInspectNodeHandler(handler) {
OverlayModel._inspectNodeHandler = handler;
}
/**
* @override
* @param {!Protocol.DOM.BackendNodeId} backendNodeId
*/
inspectNodeRequested(backendNodeId) {
const deferredNode = new SDK.DeferredDOMNode(this.target(), backendNodeId);
if (OverlayModel._inspectNodeHandler) {
deferredNode.resolvePromise().then(node => {
if (node) {
OverlayModel._inspectNodeHandler(node);
}
});
} else {
Common.Revealer.reveal(deferredNode);
}
this.dispatchEventToListeners(Events.ExitedInspectMode);
}
/**
* @override
* @param {!Protocol.Page.Viewport} viewport
*/
screenshotRequested(viewport) {
this.dispatchEventToListeners(Events.ScreenshotRequested, viewport);
this.dispatchEventToListeners(Events.ExitedInspectMode);
}
/**
* @override
*/
inspectModeCanceled() {
this.dispatchEventToListeners(Events.ExitedInspectMode);
}
}
/** @enum {symbol} */
export const Events = {
InspectModeWillBeToggled: Symbol('InspectModeWillBeToggled'),
ExitedInspectMode: Symbol('InspectModeExited'),
HighlightNodeRequested: Symbol('HighlightNodeRequested'),
ScreenshotRequested: Symbol('ScreenshotRequested'),
};
/**
* @interface
*/
export class Highlighter {
/**
* @param {!SDK.OverlayModel.HighlightData} data
* @param {!Protocol.Overlay.HighlightConfig} config
*/
highlightInOverlay(data, config) {
}
/**
* @param {!Protocol.Overlay.InspectMode} mode
* @param {!Protocol.Overlay.HighlightConfig} config
* @return {!Promise}
*/
setInspectMode(mode, config) {
}
/**
* @param {!Protocol.Page.FrameId} frameId
*/
highlightFrame(frameId) {}
}
/**
* @implements {Highlighter}
*/
class DefaultHighlighter {
/**
* @param {!OverlayModel} model
*/
constructor(model) {
this._model = model;
}
/**
* @override
* @param {!SDK.OverlayModel.HighlightData} data
* @param {!Protocol.Overlay.HighlightConfig} config
*/
highlightInOverlay(data, config) {
const {node, deferredNode, object, selectorList} = data;
const nodeId = node ? node.id : undefined;
const backendNodeId = deferredNode ? deferredNode.backendNodeId() : undefined;
const objectId = object ? object.objectId : undefined;
if (nodeId || backendNodeId || objectId) {
this._model._overlayAgent.highlightNode(config, nodeId, backendNodeId, objectId, selectorList);
} else {
this._model._overlayAgent.hideHighlight();
}
}
/**
* @override
* @param {!Protocol.Overlay.InspectMode} mode
* @param {!Protocol.Overlay.HighlightConfig} config
* @return {!Promise}
*/
setInspectMode(mode, config) {
return this._model._overlayAgent.setInspectMode(mode, config);
}
/**
* @override
* @param {!Protocol.Page.FrameId} frameId
*/
highlightFrame(frameId) {
this._model._overlayAgent.highlightFrame(
frameId, Common.Color.PageHighlight.Content.toProtocolRGBA(),
Common.Color.PageHighlight.ContentOutline.toProtocolRGBA());
}
}
/* Legacy exported object */
self.SDK = self.SDK || {};
/* Legacy exported object */
SDK = SDK || {};
/** @constructor */
SDK.OverlayModel = OverlayModel;
/** @enum {symbol} */
SDK.OverlayModel.Events = Events;
/**
* @interface
*/
SDK.OverlayModel.Highlighter = Highlighter;
SDK.SDKModel.register(SDK.OverlayModel, SDK.Target.Capability.DOM, true);
/** @typedef {{node: (!SDK.DOMNode|undefined),
deferredNode: (!SDK.DeferredDOMNode|undefined),
selectorList: (string|undefined),
object:(!SDK.RemoteObject|undefined)}} */
SDK.OverlayModel.HighlightData;