UNPKG

debug-server-next

Version:

Dev server for hippy-core.

938 lines (937 loc) 38.5 kB
// 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. /* eslint-disable rulesdir/no_underscored_properties */ import * as Common from '../common/common.js'; import * as i18n from '../i18n/i18n.js'; import { DOMModel, Events as DOMModelEvents } from './DOMModel.js'; import { RemoteObject } from './RemoteObject.js'; import { RuntimeModel } from './RuntimeModel.js'; import { Capability } from './Target.js'; import { SDKModel } from './SDKModel.js'; import { TargetManager } from './TargetManager.js'; const UIStrings = { /** *@description Title for a category of breakpoints on Trusted Type violations */ trustedTypeViolations: 'Trusted Type Violations', /** * @description Noun. Title for a checkbox that turns on breakpoints on Trusted Type sink violations. * "Trusted Types" is a Web API. A "Sink" (Noun, singular) is a special function, akin to a data sink, that expects * to receive data in a specific format. Should the data be in the wrong format, or something else * go wrong, its called a "sink violation". */ sinkViolations: 'Sink Violations', /** *@description Title for a checkbox that turns on breakpoints on Trusted Type policy violations */ policyViolations: 'Policy Violations', /** *@description Text that refers to the animation of the web page */ animation: 'Animation', /** *@description Text in DOMDebugger Model */ canvas: 'Canvas', /** *@description Title for a group of cities */ geolocation: 'Geolocation', /** *@description Text in DOMDebugger Model */ notification: 'Notification', /** *@description Text to parse something */ parse: 'Parse', /** *@description Label for a group of JavaScript files */ script: 'Script', /** *@description Text in DOMDebugger Model */ timer: 'Timer', /** *@description Text in DOMDebugger Model */ window: 'Window', /** *@description Title of the WebAudio tool */ webaudio: 'WebAudio', /** *@description Text that appears on a button for the media resource type filter. */ media: 'Media', /** *@description Text in DOMDebugger Model */ pictureinpicture: 'Picture-in-Picture', /** *@description Text in DOMDebugger Model */ clipboard: 'Clipboard', /** * @description Noun. Describes a group of DOM events (such as 'select' and 'submit') in this context. */ control: 'Control', /** *@description Text that refers to device such as a phone */ device: 'Device', /** *@description Text in DOMDebugger Model */ domMutation: 'DOM Mutation', /** *@description Text in DOMDebugger Model */ dragDrop: 'Drag / drop', /** *@description Text in DOMDebugger Model */ keyboard: 'Keyboard', /** *@description Text to load something */ load: 'Load', /** *@description Text in DOMDebugger Model */ mouse: 'Mouse', /** *@description Text in DOMDebugger Model */ pointer: 'Pointer', /** *@description Text for the touch type to simulate on a device */ touch: 'Touch', /** *@description Text that appears on a button for the xhr resource type filter. */ xhr: 'XHR', /** *@description Text in the Event Listener Breakpoints Panel of the JavaScript Debugger in the Sources Panel *@example {setTimeout} PH1 */ setTimeoutOrIntervalFired: '{PH1} fired', /** *@description Text in the Event Listener Breakpoints Panel of the JavaScript Debugger in the Sources Panel */ scriptFirstStatement: 'Script First Statement', /** *@description Text in the Event Listener Breakpoints Panel of the JavaScript Debugger in the Sources Panel */ scriptBlockedByContentSecurity: 'Script Blocked by Content Security Policy', /** *@description Text for the request animation frame event */ requestAnimationFrame: 'Request Animation Frame', /** *@description Text to cancel the animation frame */ cancelAnimationFrame: 'Cancel Animation Frame', /** *@description Text for the event that an animation frame is fired */ animationFrameFired: 'Animation Frame Fired', /** *@description Text in the Event Listener Breakpoints Panel of the JavaScript Debugger in the Sources Panel */ webglErrorFired: 'WebGL Error Fired', /** *@description Text in the Event Listener Breakpoints Panel of the JavaScript Debugger in the Sources Panel */ webglWarningFired: 'WebGL Warning Fired', /** *@description Text in the Event Listener Breakpoints Panel of the JavaScript Debugger in the Sources Panel */ setInnerhtml: 'Set `innerHTML`', /** *@description Name of a breakpoint type in the Sources Panel. */ createCanvasContext: 'Create canvas context', /** *@description Name of a breakpoint type in the Sources Panel. */ createAudiocontext: 'Create `AudioContext`', /** *@description Name of a breakpoint type in the Sources Panel. Close is a verb. */ closeAudiocontext: 'Close `AudioContext`', /** *@description Name of a breakpoint type in the Sources Panel. Resume is a verb. */ resumeAudiocontext: 'Resume `AudioContext`', /** *@description Name of a breakpoint type in the Sources Panel. */ suspendAudiocontext: 'Suspend `AudioContext`', /** *@description Error message text *@example {Snag Error} PH1 */ webglErrorFiredS: 'WebGL Error Fired ({PH1})', /** *@description Text in DOMDebugger Model *@example {"script-src 'self'"} PH1 */ scriptBlockedDueToContent: 'Script blocked due to Content Security Policy directive: {PH1}', /** *@description Text for the service worker type. */ worker: 'Worker', }; const str_ = i18n.i18n.registerUIStrings('core/sdk/DOMDebuggerModel.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); export class DOMDebuggerModel extends SDKModel { _agent; _runtimeModel; _domModel; _domBreakpoints; _domBreakpointsSetting; suspended = false; constructor(target) { super(target); this._agent = target.domdebuggerAgent(); this._runtimeModel = target.model(RuntimeModel); this._domModel = target.model(DOMModel); this._domModel.addEventListener(DOMModelEvents.DocumentUpdated, this._documentUpdated, this); this._domModel.addEventListener(DOMModelEvents.NodeRemoved, this._nodeRemoved, this); this._domBreakpoints = []; this._domBreakpointsSetting = Common.Settings.Settings.instance().createLocalSetting('domBreakpoints', []); if (this._domModel.existingDocument()) { this._documentUpdated(); } } runtimeModel() { return this._runtimeModel; } async suspendModel() { this.suspended = true; } async resumeModel() { this.suspended = false; } async eventListeners(remoteObject) { console.assert(remoteObject.runtimeModel() === this._runtimeModel); if (!remoteObject.objectId) { return []; } const listeners = await this._agent.invoke_getEventListeners({ objectId: remoteObject.objectId }); const eventListeners = []; for (const payload of listeners.listeners || []) { const location = this._runtimeModel.debuggerModel().createRawLocationByScriptId(payload.scriptId, payload.lineNumber, payload.columnNumber); if (!location) { continue; } eventListeners.push(new EventListener(this, remoteObject, payload.type, payload.useCapture, payload.passive, payload.once, payload.handler ? this._runtimeModel.createRemoteObject(payload.handler) : null, payload.originalHandler ? this._runtimeModel.createRemoteObject(payload.originalHandler) : null, location, null)); } return eventListeners; } retrieveDOMBreakpoints() { this._domModel.requestDocument(); } domBreakpoints() { return this._domBreakpoints.slice(); } hasDOMBreakpoint(node, type) { return this._domBreakpoints.some(breakpoint => (breakpoint.node === node && breakpoint.type === type)); } setDOMBreakpoint(node, type) { for (const breakpoint of this._domBreakpoints) { if (breakpoint.node === node && breakpoint.type === type) { this.toggleDOMBreakpoint(breakpoint, true); return breakpoint; } } const breakpoint = new DOMBreakpoint(this, node, type, true); this._domBreakpoints.push(breakpoint); this._saveDOMBreakpoints(); this._enableDOMBreakpoint(breakpoint); this.dispatchEventToListeners(Events.DOMBreakpointAdded, breakpoint); return breakpoint; } removeDOMBreakpoint(node, type) { this._removeDOMBreakpoints(breakpoint => breakpoint.node === node && breakpoint.type === type); } removeAllDOMBreakpoints() { this._removeDOMBreakpoints(_breakpoint => true); } toggleDOMBreakpoint(breakpoint, enabled) { if (enabled === breakpoint.enabled) { return; } breakpoint.enabled = enabled; if (enabled) { this._enableDOMBreakpoint(breakpoint); } else { this._disableDOMBreakpoint(breakpoint); } this.dispatchEventToListeners(Events.DOMBreakpointToggled, breakpoint); } _enableDOMBreakpoint(breakpoint) { if (breakpoint.node.id) { this._agent.invoke_setDOMBreakpoint({ nodeId: breakpoint.node.id, type: breakpoint.type }); breakpoint.node.setMarker(Marker, true); } } _disableDOMBreakpoint(breakpoint) { if (breakpoint.node.id) { this._agent.invoke_removeDOMBreakpoint({ nodeId: breakpoint.node.id, type: breakpoint.type }); breakpoint.node.setMarker(Marker, this._nodeHasBreakpoints(breakpoint.node) ? true : null); } } _nodeHasBreakpoints(node) { for (const breakpoint of this._domBreakpoints) { if (breakpoint.node === node && breakpoint.enabled) { return true; } } return false; } resolveDOMBreakpointData(auxData) { const type = auxData['type']; const node = this._domModel.nodeForId(auxData['nodeId']); if (!type || !node) { return null; } let targetNode = null; let insertion = false; if (type === "subtree-modified" /* SubtreeModified */) { insertion = auxData['insertion'] || false; targetNode = this._domModel.nodeForId(auxData['targetNodeId']); } return { type: type, node: node, targetNode: targetNode, insertion: insertion }; } _currentURL() { const domDocument = this._domModel.existingDocument(); return domDocument ? domDocument.documentURL : ''; } async _documentUpdated() { if (this.suspended) { return; } const removed = this._domBreakpoints; this._domBreakpoints = []; this.dispatchEventToListeners(Events.DOMBreakpointsRemoved, removed); // this._currentURL() is empty when the page is reloaded because the // new document has not been requested yet and the old one has been // removed. Therefore, we need to request the document and wait for it. // Note that requestDocument() caches the document so that it is requested // only once. const document = await this._domModel.requestDocument(); const currentURL = document ? document.documentURL : ''; for (const breakpoint of this._domBreakpointsSetting.get()) { if (breakpoint.url === currentURL) { this._domModel.pushNodeByPathToFrontend(breakpoint.path).then(appendBreakpoint.bind(this, breakpoint)); } } function appendBreakpoint(breakpoint, nodeId) { const node = nodeId ? this._domModel.nodeForId(nodeId) : null; if (!node) { return; } const domBreakpoint = new DOMBreakpoint(this, node, breakpoint.type, breakpoint.enabled); this._domBreakpoints.push(domBreakpoint); if (breakpoint.enabled) { this._enableDOMBreakpoint(domBreakpoint); } this.dispatchEventToListeners(Events.DOMBreakpointAdded, domBreakpoint); } } _removeDOMBreakpoints(filter) { const removed = []; const left = []; for (const breakpoint of this._domBreakpoints) { if (filter(breakpoint)) { removed.push(breakpoint); if (breakpoint.enabled) { breakpoint.enabled = false; this._disableDOMBreakpoint(breakpoint); } } else { left.push(breakpoint); } } if (!removed.length) { return; } this._domBreakpoints = left; this._saveDOMBreakpoints(); this.dispatchEventToListeners(Events.DOMBreakpointsRemoved, removed); } _nodeRemoved(event) { if (this.suspended) { return; } const node = event.data.node; const children = node.children() || []; this._removeDOMBreakpoints(breakpoint => breakpoint.node === node || children.indexOf(breakpoint.node) !== -1); } _saveDOMBreakpoints() { const currentURL = this._currentURL(); const breakpoints = this._domBreakpointsSetting.get().filter((breakpoint) => breakpoint.url !== currentURL); for (const breakpoint of this._domBreakpoints) { breakpoints.push({ url: currentURL, path: breakpoint.node.path(), type: breakpoint.type, enabled: breakpoint.enabled }); } this._domBreakpointsSetting.set(breakpoints); } } // TODO(crbug.com/1167717): Make this a const enum again // eslint-disable-next-line rulesdir/const_enum export var Events; (function (Events) { Events["DOMBreakpointAdded"] = "DOMBreakpointAdded"; Events["DOMBreakpointToggled"] = "DOMBreakpointToggled"; Events["DOMBreakpointsRemoved"] = "DOMBreakpointsRemoved"; })(Events || (Events = {})); const Marker = 'breakpoint-marker'; export class DOMBreakpoint { domDebuggerModel; node; type; enabled; constructor(domDebuggerModel, node, type, enabled) { this.domDebuggerModel = domDebuggerModel; this.node = node; this.type = type; this.enabled = enabled; } } export class EventListener { _domDebuggerModel; _eventTarget; _type; _useCapture; _passive; _once; _handler; _originalHandler; _location; _sourceURL; _customRemoveFunction; _origin; constructor(domDebuggerModel, eventTarget, type, useCapture, passive, once, handler, originalHandler, location, customRemoveFunction, origin) { this._domDebuggerModel = domDebuggerModel; this._eventTarget = eventTarget; this._type = type; this._useCapture = useCapture; this._passive = passive; this._once = once; this._handler = handler; this._originalHandler = originalHandler || handler; this._location = location; const script = location.script(); this._sourceURL = script ? script.contentURL() : ''; this._customRemoveFunction = customRemoveFunction; this._origin = origin || EventListener.Origin.Raw; } domDebuggerModel() { return this._domDebuggerModel; } type() { return this._type; } useCapture() { return this._useCapture; } passive() { return this._passive; } once() { return this._once; } handler() { return this._handler; } location() { return this._location; } sourceURL() { return this._sourceURL; } originalHandler() { return this._originalHandler; } canRemove() { return Boolean(this._customRemoveFunction) || this._origin !== EventListener.Origin.FrameworkUser; } remove() { if (!this.canRemove()) { return Promise.resolve(undefined); } if (this._origin !== EventListener.Origin.FrameworkUser) { function removeListener(type, listener, useCapture) { this.removeEventListener(type, listener, useCapture); // @ts-ignore: if (this['on' + type]) { // @ts-ignore: this['on' + type] = undefined; } } return this._eventTarget .callFunction( // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration // @ts-expect-error removeListener, [ RemoteObject.toCallArgument(this._type), RemoteObject.toCallArgument(this._originalHandler), RemoteObject.toCallArgument(this._useCapture), ]) .then(() => undefined); } if (this._customRemoveFunction) { function callCustomRemove(type, listener, useCapture, passive) { this.call(null, type, listener, useCapture, passive); } return this._customRemoveFunction .callFunction( // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration // @ts-expect-error callCustomRemove, [ RemoteObject.toCallArgument(this._type), RemoteObject.toCallArgument(this._originalHandler), RemoteObject.toCallArgument(this._useCapture), RemoteObject.toCallArgument(this._passive), ]) .then(() => undefined); } return Promise.resolve(undefined); } canTogglePassive() { return this._origin !== EventListener.Origin.FrameworkUser; } togglePassive() { return this._eventTarget .callFunction( // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration // @ts-expect-error callTogglePassive, [ RemoteObject.toCallArgument(this._type), RemoteObject.toCallArgument(this._originalHandler), RemoteObject.toCallArgument(this._useCapture), RemoteObject.toCallArgument(this._passive), ]) .then(() => undefined); function callTogglePassive(type, listener, useCapture, passive) { this.removeEventListener(type, listener, { capture: useCapture }); this.addEventListener(type, listener, { capture: useCapture, passive: !passive }); } } origin() { return this._origin; } markAsFramework() { this._origin = EventListener.Origin.Framework; } isScrollBlockingType() { return this._type === 'touchstart' || this._type === 'touchmove' || this._type === 'mousewheel' || this._type === 'wheel'; } } (function (EventListener) { // TODO(crbug.com/1167717): Make this a const enum again // eslint-disable-next-line rulesdir/const_enum let Origin; (function (Origin) { Origin["Raw"] = "Raw"; Origin["Framework"] = "Framework"; Origin["FrameworkUser"] = "FrameworkUser"; })(Origin = EventListener.Origin || (EventListener.Origin = {})); })(EventListener || (EventListener = {})); export class CategorizedBreakpoint { _category; _title; _enabled; constructor(category, title) { this._category = category; this._title = title; this._enabled = false; } category() { return this._category; } enabled() { return this._enabled; } setEnabled(enabled) { this._enabled = enabled; } title() { return this._title; } } export class CSPViolationBreakpoint extends CategorizedBreakpoint { _type; constructor(category, title, type) { super(category, title); this._type = type; } type() { return this._type; } } export class EventListenerBreakpoint extends CategorizedBreakpoint { _instrumentationName; _eventName; _eventTargetNames; constructor(instrumentationName, eventName, eventTargetNames, category, title) { super(category, title); this._instrumentationName = instrumentationName; this._eventName = eventName; this._eventTargetNames = eventTargetNames; } setEnabled(enabled) { if (this._enabled === enabled) { return; } super.setEnabled(enabled); for (const model of TargetManager.instance().models(DOMDebuggerModel)) { this._updateOnModel(model); } } _updateOnModel(model) { if (this._instrumentationName) { if (this._enabled) { model._agent.invoke_setInstrumentationBreakpoint({ eventName: this._instrumentationName }); } else { model._agent.invoke_removeInstrumentationBreakpoint({ eventName: this._instrumentationName }); } } else { for (const eventTargetName of this._eventTargetNames) { if (this._enabled) { model._agent.invoke_setEventListenerBreakpoint({ eventName: this._eventName, targetName: eventTargetName }); } else { model._agent.invoke_removeEventListenerBreakpoint({ eventName: this._eventName, targetName: eventTargetName }); } } } } // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration // eslint-disable-next-line @typescript-eslint/naming-convention static _listener = 'listener:'; // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration // eslint-disable-next-line @typescript-eslint/naming-convention static _instrumentation = 'instrumentation:'; } let domDebuggerManagerInstance; export class DOMDebuggerManager { _xhrBreakpointsSetting; _xhrBreakpoints; _cspViolationsToBreakOn; _eventListenerBreakpoints; constructor() { this._xhrBreakpointsSetting = Common.Settings.Settings.instance().createLocalSetting('xhrBreakpoints', []); this._xhrBreakpoints = new Map(); for (const breakpoint of this._xhrBreakpointsSetting.get()) { this._xhrBreakpoints.set(breakpoint.url, breakpoint.enabled); } this._cspViolationsToBreakOn = []; this._cspViolationsToBreakOn.push(new CSPViolationBreakpoint(i18nString(UIStrings.trustedTypeViolations), i18nString(UIStrings.sinkViolations), "trustedtype-sink-violation" /* TrustedtypeSinkViolation */)); this._cspViolationsToBreakOn.push(new CSPViolationBreakpoint(i18nString(UIStrings.trustedTypeViolations), i18nString(UIStrings.policyViolations), "trustedtype-policy-violation" /* TrustedtypePolicyViolation */)); this._eventListenerBreakpoints = []; this._createInstrumentationBreakpoints(i18nString(UIStrings.animation), ['requestAnimationFrame', 'cancelAnimationFrame', 'requestAnimationFrame.callback']); this._createInstrumentationBreakpoints(i18nString(UIStrings.canvas), ['canvasContextCreated', 'webglErrorFired', 'webglWarningFired']); this._createInstrumentationBreakpoints(i18nString(UIStrings.geolocation), ['Geolocation.getCurrentPosition', 'Geolocation.watchPosition']); this._createInstrumentationBreakpoints(i18nString(UIStrings.notification), ['Notification.requestPermission']); this._createInstrumentationBreakpoints(i18nString(UIStrings.parse), ['Element.setInnerHTML', 'Document.write']); this._createInstrumentationBreakpoints(i18nString(UIStrings.script), ['scriptFirstStatement', 'scriptBlockedByCSP']); this._createInstrumentationBreakpoints(i18nString(UIStrings.timer), ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'setTimeout.callback', 'setInterval.callback']); this._createInstrumentationBreakpoints(i18nString(UIStrings.window), ['DOMWindow.close']); this._createInstrumentationBreakpoints(i18nString(UIStrings.webaudio), ['audioContextCreated', 'audioContextClosed', 'audioContextResumed', 'audioContextSuspended']); this._createEventListenerBreakpoints(i18nString(UIStrings.media), [ 'play', 'pause', 'playing', 'canplay', 'canplaythrough', 'seeking', 'seeked', 'timeupdate', 'ended', 'ratechange', 'durationchange', 'volumechange', 'loadstart', 'progress', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'waiting', ], ['audio', 'video']); this._createEventListenerBreakpoints(i18nString(UIStrings.pictureinpicture), ['enterpictureinpicture', 'leavepictureinpicture'], ['video']); this._createEventListenerBreakpoints(i18nString(UIStrings.pictureinpicture), ['resize'], ['PictureInPictureWindow']); this._createEventListenerBreakpoints(i18nString(UIStrings.clipboard), ['copy', 'cut', 'paste', 'beforecopy', 'beforecut', 'beforepaste'], ['*']); this._createEventListenerBreakpoints(i18nString(UIStrings.control), ['resize', 'scroll', 'zoom', 'focus', 'blur', 'select', 'change', 'submit', 'reset'], ['*']); this._createEventListenerBreakpoints(i18nString(UIStrings.device), ['deviceorientation', 'devicemotion'], ['*']); this._createEventListenerBreakpoints(i18nString(UIStrings.domMutation), [ 'DOMActivate', 'DOMFocusIn', 'DOMFocusOut', 'DOMAttrModified', 'DOMCharacterDataModified', 'DOMNodeInserted', 'DOMNodeInsertedIntoDocument', 'DOMNodeRemoved', 'DOMNodeRemovedFromDocument', 'DOMSubtreeModified', 'DOMContentLoaded', ], ['*']); this._createEventListenerBreakpoints(i18nString(UIStrings.dragDrop), ['drag', 'dragstart', 'dragend', 'dragenter', 'dragover', 'dragleave', 'drop'], ['*']); this._createEventListenerBreakpoints(i18nString(UIStrings.keyboard), ['keydown', 'keyup', 'keypress', 'input'], ['*']); this._createEventListenerBreakpoints(i18nString(UIStrings.load), ['load', 'beforeunload', 'unload', 'abort', 'error', 'hashchange', 'popstate'], ['*']); this._createEventListenerBreakpoints(i18nString(UIStrings.mouse), [ 'auxclick', 'click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout', 'mouseenter', 'mouseleave', 'mousewheel', 'wheel', 'contextmenu', ], ['*']); this._createEventListenerBreakpoints(i18nString(UIStrings.pointer), [ 'pointerover', 'pointerout', 'pointerenter', 'pointerleave', 'pointerdown', 'pointerup', 'pointermove', 'pointercancel', 'gotpointercapture', 'lostpointercapture', 'pointerrawupdate', ], ['*']); this._createEventListenerBreakpoints(i18nString(UIStrings.touch), ['touchstart', 'touchmove', 'touchend', 'touchcancel'], ['*']); this._createEventListenerBreakpoints(i18nString(UIStrings.worker), ['message', 'messageerror'], ['*']); this._createEventListenerBreakpoints(i18nString(UIStrings.xhr), ['readystatechange', 'load', 'loadstart', 'loadend', 'abort', 'error', 'progress', 'timeout'], ['xmlhttprequest', 'xmlhttprequestupload']); let breakpoint; breakpoint = this._resolveEventListenerBreakpoint('instrumentation:setTimeout.callback'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.setTimeoutOrIntervalFired, { PH1: 'setTimeout' }); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:setInterval.callback'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.setTimeoutOrIntervalFired, { PH1: 'setInterval' }); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:scriptFirstStatement'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.scriptFirstStatement); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:scriptBlockedByCSP'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.scriptBlockedByContentSecurity); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:requestAnimationFrame'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.requestAnimationFrame); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:cancelAnimationFrame'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.cancelAnimationFrame); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:requestAnimationFrame.callback'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.animationFrameFired); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:webglErrorFired'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.webglErrorFired); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:webglWarningFired'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.webglWarningFired); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:Element.setInnerHTML'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.setInnerhtml); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:canvasContextCreated'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.createCanvasContext); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:Geolocation.getCurrentPosition'); if (breakpoint) { breakpoint._title = 'getCurrentPosition'; } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:Geolocation.watchPosition'); if (breakpoint) { breakpoint._title = 'watchPosition'; } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:Notification.requestPermission'); if (breakpoint) { breakpoint._title = 'requestPermission'; } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:DOMWindow.close'); if (breakpoint) { breakpoint._title = 'window.close'; } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:Document.write'); if (breakpoint) { breakpoint._title = 'document.write'; } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:audioContextCreated'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.createAudiocontext); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:audioContextClosed'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.closeAudiocontext); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:audioContextResumed'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.resumeAudiocontext); } breakpoint = this._resolveEventListenerBreakpoint('instrumentation:audioContextSuspended'); if (breakpoint) { breakpoint._title = i18nString(UIStrings.suspendAudiocontext); } TargetManager.instance().observeModels(DOMDebuggerModel, this); } static instance(opts = { forceNew: null }) { const { forceNew } = opts; if (!domDebuggerManagerInstance || forceNew) { domDebuggerManagerInstance = new DOMDebuggerManager(); } return domDebuggerManagerInstance; } cspViolationBreakpoints() { return this._cspViolationsToBreakOn.slice(); } _createInstrumentationBreakpoints(category, instrumentationNames) { for (const instrumentationName of instrumentationNames) { this._eventListenerBreakpoints.push(new EventListenerBreakpoint(instrumentationName, '', [], category, instrumentationName)); } } _createEventListenerBreakpoints(category, eventNames, eventTargetNames) { for (const eventName of eventNames) { this._eventListenerBreakpoints.push(new EventListenerBreakpoint('', eventName, eventTargetNames, category, eventName)); } } _resolveEventListenerBreakpoint(eventName, eventTargetName) { const instrumentationPrefix = 'instrumentation:'; const listenerPrefix = 'listener:'; let instrumentationName = ''; if (eventName.startsWith(instrumentationPrefix)) { instrumentationName = eventName.substring(instrumentationPrefix.length); eventName = ''; } else if (eventName.startsWith(listenerPrefix)) { eventName = eventName.substring(listenerPrefix.length); } else { return null; } eventTargetName = (eventTargetName || '*').toLowerCase(); let result = null; for (const breakpoint of this._eventListenerBreakpoints) { if (instrumentationName && breakpoint._instrumentationName === instrumentationName) { result = breakpoint; } if (eventName && breakpoint._eventName === eventName && breakpoint._eventTargetNames.indexOf(eventTargetName) !== -1) { result = breakpoint; } if (!result && eventName && breakpoint._eventName === eventName && breakpoint._eventTargetNames.indexOf('*') !== -1) { result = breakpoint; } } return result; } eventListenerBreakpoints() { return this._eventListenerBreakpoints.slice(); } resolveEventListenerBreakpointTitle(auxData) { const id = auxData['eventName']; if (id === 'instrumentation:webglErrorFired' && auxData['webglErrorName']) { let errorName = auxData['webglErrorName']; // If there is a hex code of the error, display only this. errorName = errorName.replace(/^.*(0x[0-9a-f]+).*$/i, '$1'); return i18nString(UIStrings.webglErrorFiredS, { PH1: errorName }); } if (id === 'instrumentation:scriptBlockedByCSP' && auxData['directiveText']) { return i18nString(UIStrings.scriptBlockedDueToContent, { PH1: auxData['directiveText'] }); } const breakpoint = this._resolveEventListenerBreakpoint(id, auxData['targetName']); if (!breakpoint) { return ''; } if (auxData['targetName']) { return auxData['targetName'] + '.' + breakpoint._title; } return breakpoint._title; } resolveEventListenerBreakpoint(auxData) { return this._resolveEventListenerBreakpoint(auxData['eventName'], auxData['targetName']); } updateCSPViolationBreakpoints() { const violationTypes = this._cspViolationsToBreakOn.filter(v => v.enabled()).map(v => v.type()); for (const model of TargetManager.instance().models(DOMDebuggerModel)) { this._updateCSPViolationBreakpointsForModel(model, violationTypes); } } _updateCSPViolationBreakpointsForModel(model, violationTypes) { model._agent.invoke_setBreakOnCSPViolation({ violationTypes: violationTypes }); } xhrBreakpoints() { return this._xhrBreakpoints; } _saveXHRBreakpoints() { const breakpoints = []; for (const url of this._xhrBreakpoints.keys()) { breakpoints.push({ url: url, enabled: this._xhrBreakpoints.get(url) || false }); } this._xhrBreakpointsSetting.set(breakpoints); } addXHRBreakpoint(url, enabled) { this._xhrBreakpoints.set(url, enabled); if (enabled) { for (const model of TargetManager.instance().models(DOMDebuggerModel)) { model._agent.invoke_setXHRBreakpoint({ url }); } } this._saveXHRBreakpoints(); } removeXHRBreakpoint(url) { const enabled = this._xhrBreakpoints.get(url); this._xhrBreakpoints.delete(url); if (enabled) { for (const model of TargetManager.instance().models(DOMDebuggerModel)) { model._agent.invoke_removeXHRBreakpoint({ url }); } } this._saveXHRBreakpoints(); } toggleXHRBreakpoint(url, enabled) { this._xhrBreakpoints.set(url, enabled); for (const model of TargetManager.instance().models(DOMDebuggerModel)) { if (enabled) { model._agent.invoke_setXHRBreakpoint({ url }); } else { model._agent.invoke_removeXHRBreakpoint({ url }); } } this._saveXHRBreakpoints(); } modelAdded(domDebuggerModel) { for (const url of this._xhrBreakpoints.keys()) { if (this._xhrBreakpoints.get(url)) { domDebuggerModel._agent.invoke_setXHRBreakpoint({ url: url }); } } for (const breakpoint of this._eventListenerBreakpoints) { if (breakpoint._enabled) { breakpoint._updateOnModel(domDebuggerModel); } } const violationTypes = this._cspViolationsToBreakOn.filter(v => v.enabled()).map(v => v.type()); this._updateCSPViolationBreakpointsForModel(domDebuggerModel, violationTypes); } modelRemoved(_domDebuggerModel) { } } SDKModel.register(DOMDebuggerModel, { capabilities: Capability.DOM, autostart: false });