DOMDebuggerModel.js 20 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. export default class DOMDebuggerModel extends SDK.SDKModel{constructor(target){super(target);this._agent=target.domdebuggerAgent();this._runtimeModel=(target.model(SDK.RuntimeModel));this._domModel=(target.model(SDK.DOMModel));this._domModel.addEventListener(SDK.DOMModel.Events.DocumentUpdated,this._documentUpdated,this);this._domModel.addEventListener(SDK.DOMModel.Events.NodeRemoved,this._nodeRemoved,this);this._domBreakpoints=[];this._domBreakpointsSetting=Common.settings.createLocalSetting('domBreakpoints',[]);if(this._domModel.existingDocument()){this._documentUpdated();}}
  2. runtimeModel(){return this._runtimeModel;}
  3. async eventListeners(remoteObject){console.assert(remoteObject.runtimeModel()===this._runtimeModel);if(!remoteObject.objectId){return[];}
  4. const payloads=await this._agent.getEventListeners((remoteObject.objectId));const eventListeners=[];for(const payload of payloads||[]){const location=this._runtimeModel.debuggerModel().createRawLocationByScriptId(payload.scriptId,payload.lineNumber,payload.columnNumber);if(!location){continue;}
  5. eventListeners.push(new SDK.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));}
  6. return eventListeners;}
  7. retrieveDOMBreakpoints(){this._domModel.requestDocument();}
  8. domBreakpoints(){return this._domBreakpoints.slice();}
  9. hasDOMBreakpoint(node,type){return this._domBreakpoints.some(breakpoint=>(breakpoint.node===node&&breakpoint.type===type));}
  10. setDOMBreakpoint(node,type){for(const breakpoint of this._domBreakpoints){if(breakpoint.node===node&&breakpoint.type===type){this.toggleDOMBreakpoint(breakpoint,true);return breakpoint;}}
  11. const breakpoint=new DOMBreakpoint(this,node,type,true);this._domBreakpoints.push(breakpoint);this._saveDOMBreakpoints();this._enableDOMBreakpoint(breakpoint);this.dispatchEventToListeners(Events.DOMBreakpointAdded,breakpoint);return breakpoint;}
  12. removeDOMBreakpoint(node,type){this._removeDOMBreakpoints(breakpoint=>breakpoint.node===node&&breakpoint.type===type);}
  13. removeAllDOMBreakpoints(){this._removeDOMBreakpoints(breakpoint=>true);}
  14. toggleDOMBreakpoint(breakpoint,enabled){if(enabled===breakpoint.enabled){return;}
  15. breakpoint.enabled=enabled;if(enabled){this._enableDOMBreakpoint(breakpoint);}else{this._disableDOMBreakpoint(breakpoint);}
  16. this.dispatchEventToListeners(Events.DOMBreakpointToggled,breakpoint);}
  17. _enableDOMBreakpoint(breakpoint){this._agent.setDOMBreakpoint(breakpoint.node.id,breakpoint.type);breakpoint.node.setMarker(Marker,true);}
  18. _disableDOMBreakpoint(breakpoint){this._agent.removeDOMBreakpoint(breakpoint.node.id,breakpoint.type);breakpoint.node.setMarker(Marker,this._nodeHasBreakpoints(breakpoint.node)?true:null);}
  19. _nodeHasBreakpoints(node){for(const breakpoint of this._domBreakpoints){if(breakpoint.node===node&&breakpoint.enabled){return true;}}
  20. return false;}
  21. resolveDOMBreakpointData(auxData){const type=auxData['type'];const node=this._domModel.nodeForId(auxData['nodeId']);if(!type||!node){return null;}
  22. let targetNode=null;let insertion=false;if(type===SDK.DOMDebuggerModel.DOMBreakpoint.Type.SubtreeModified){insertion=auxData['insertion']||false;targetNode=this._domModel.nodeForId(auxData['targetNodeId']);}
  23. return{type:type,node:node,targetNode:targetNode,insertion:insertion};}
  24. _currentURL(){const domDocument=this._domModel.existingDocument();return domDocument?domDocument.documentURL:'';}
  25. _documentUpdated(){const removed=this._domBreakpoints;this._domBreakpoints=[];this.dispatchEventToListeners(Events.DOMBreakpointsRemoved,removed);const currentURL=this._currentURL();for(const breakpoint of this._domBreakpointsSetting.get()){if(breakpoint.url===currentURL){this._domModel.pushNodeByPathToFrontend(breakpoint.path).then(appendBreakpoint.bind(this,breakpoint));}}
  26. function appendBreakpoint(breakpoint,nodeId){const node=nodeId?this._domModel.nodeForId(nodeId):null;if(!node){return;}
  27. const domBreakpoint=new DOMBreakpoint(this,node,breakpoint.type,breakpoint.enabled);this._domBreakpoints.push(domBreakpoint);if(breakpoint.enabled){this._enableDOMBreakpoint(domBreakpoint);}
  28. this.dispatchEventToListeners(Events.DOMBreakpointAdded,domBreakpoint);}}
  29. _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);}}
  30. if(!removed.length){return;}
  31. this._domBreakpoints=left;this._saveDOMBreakpoints();this.dispatchEventToListeners(Events.DOMBreakpointsRemoved,removed);}
  32. _nodeRemoved(event){const node=(event.data.node);const children=node.children()||[];this._removeDOMBreakpoints(breakpoint=>breakpoint.node===node||children.indexOf(breakpoint.node)!==-1);}
  33. _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});}
  34. this._domBreakpointsSetting.set(breakpoints);}}
  35. export const Events={DOMBreakpointAdded:Symbol('DOMBreakpointAdded'),DOMBreakpointToggled:Symbol('DOMBreakpointToggled'),DOMBreakpointsRemoved:Symbol('DOMBreakpointsRemoved'),};const Marker='breakpoint-marker';export class DOMBreakpoint{constructor(domDebuggerModel,node,type,enabled){this.domDebuggerModel=domDebuggerModel;this.node=node;this.type=type;this.enabled=enabled;}}
  36. export class EventListener{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;}
  37. domDebuggerModel(){return this._domDebuggerModel;}
  38. type(){return this._type;}
  39. useCapture(){return this._useCapture;}
  40. passive(){return this._passive;}
  41. once(){return this._once;}
  42. handler(){return this._handler;}
  43. location(){return this._location;}
  44. sourceURL(){return this._sourceURL;}
  45. originalHandler(){return this._originalHandler;}
  46. canRemove(){return!!this._customRemoveFunction||this._origin!==EventListener.Origin.FrameworkUser;}
  47. remove(){if(!this.canRemove()){return Promise.resolve();}
  48. if(this._origin!==EventListener.Origin.FrameworkUser){function removeListener(type,listener,useCapture){this.removeEventListener(type,listener,useCapture);if(this['on'+type]){this['on'+type]=undefined;}}
  49. return(this._eventTarget.callFunction(removeListener,[SDK.RemoteObject.toCallArgument(this._type),SDK.RemoteObject.toCallArgument(this._originalHandler),SDK.RemoteObject.toCallArgument(this._useCapture)]));}
  50. return this._customRemoveFunction.callFunction(callCustomRemove,[SDK.RemoteObject.toCallArgument(this._type),SDK.RemoteObject.toCallArgument(this._originalHandler),SDK.RemoteObject.toCallArgument(this._useCapture),SDK.RemoteObject.toCallArgument(this._passive),]).then(()=>undefined);function callCustomRemove(type,listener,useCapture,passive){this.call(null,type,listener,useCapture,passive);}}
  51. canTogglePassive(){return this._origin!==EventListener.Origin.FrameworkUser;}
  52. togglePassive(){return(this._eventTarget.callFunction(callTogglePassive,[SDK.RemoteObject.toCallArgument(this._type),SDK.RemoteObject.toCallArgument(this._originalHandler),SDK.RemoteObject.toCallArgument(this._useCapture),SDK.RemoteObject.toCallArgument(this._passive),]));function callTogglePassive(type,listener,useCapture,passive){this.removeEventListener(type,listener,{capture:useCapture});this.addEventListener(type,listener,{capture:useCapture,passive:!passive});}}
  53. origin(){return this._origin;}
  54. markAsFramework(){this._origin=EventListener.Origin.Framework;}
  55. isScrollBlockingType(){return this._type==='touchstart'||this._type==='touchmove'||this._type==='mousewheel'||this._type==='wheel';}}
  56. EventListener.Origin={Raw:'Raw',Framework:'Framework',FrameworkUser:'FrameworkUser'};export class EventListenerBreakpoint{constructor(instrumentationName,eventName,eventTargetNames,category,title){this._instrumentationName=instrumentationName;this._eventName=eventName;this._eventTargetNames=eventTargetNames;this._category=category;this._title=title;this._enabled=false;}
  57. category(){return this._category;}
  58. enabled(){return this._enabled;}
  59. setEnabled(enabled){if(this._enabled===enabled){return;}
  60. this._enabled=enabled;for(const model of SDK.targetManager.models(DOMDebuggerModel)){this._updateOnModel(model);}}
  61. _updateOnModel(model){if(this._instrumentationName){if(this._enabled){model._agent.setInstrumentationBreakpoint(this._instrumentationName);}else{model._agent.removeInstrumentationBreakpoint(this._instrumentationName);}}else{for(const eventTargetName of this._eventTargetNames){if(this._enabled){model._agent.setEventListenerBreakpoint(this._eventName,eventTargetName);}else{model._agent.removeEventListenerBreakpoint(this._eventName,eventTargetName);}}}}
  62. title(){return this._title;}}
  63. EventListenerBreakpoint._listener='listener:';EventListenerBreakpoint._instrumentation='instrumentation:';export class DOMDebuggerManager{constructor(){this._xhrBreakpointsSetting=Common.settings.createLocalSetting('xhrBreakpoints',[]);this._xhrBreakpoints=new Map();for(const breakpoint of this._xhrBreakpointsSetting.get()){this._xhrBreakpoints.set(breakpoint.url,breakpoint.enabled);}
  64. this._eventListenerBreakpoints=[];this._createInstrumentationBreakpoints(Common.UIString('Animation'),['requestAnimationFrame','cancelAnimationFrame','requestAnimationFrame.callback']);this._createInstrumentationBreakpoints(Common.UIString('Canvas'),['canvasContextCreated','webglErrorFired','webglWarningFired']);this._createInstrumentationBreakpoints(Common.UIString('Geolocation'),['Geolocation.getCurrentPosition','Geolocation.watchPosition']);this._createInstrumentationBreakpoints(Common.UIString('Notification'),['Notification.requestPermission']);this._createInstrumentationBreakpoints(Common.UIString('Parse'),['Element.setInnerHTML','Document.write']);this._createInstrumentationBreakpoints(Common.UIString('Script'),['scriptFirstStatement','scriptBlockedByCSP']);this._createInstrumentationBreakpoints(Common.UIString('Timer'),['setTimeout','clearTimeout','setInterval','clearInterval','setTimeout.callback','setInterval.callback']);this._createInstrumentationBreakpoints(Common.UIString('Window'),['DOMWindow.close']);this._createInstrumentationBreakpoints(Common.UIString('WebAudio'),['audioContextCreated','audioContextClosed','audioContextResumed','audioContextSuspended']);this._createEventListenerBreakpoints(Common.UIString('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(Common.UIString('Picture-in-Picture'),['enterpictureinpicture','leavepictureinpicture'],['video']);this._createEventListenerBreakpoints(Common.UIString('Picture-in-Picture'),['resize'],['PictureInPictureWindow']);this._createEventListenerBreakpoints(Common.UIString('Clipboard'),['copy','cut','paste','beforecopy','beforecut','beforepaste'],['*']);this._createEventListenerBreakpoints(Common.UIString('Control'),['resize','scroll','zoom','focus','blur','select','change','submit','reset'],['*']);this._createEventListenerBreakpoints(Common.UIString('Device'),['deviceorientation','devicemotion'],['*']);this._createEventListenerBreakpoints(Common.UIString('DOM Mutation'),['DOMActivate','DOMFocusIn','DOMFocusOut','DOMAttrModified','DOMCharacterDataModified','DOMNodeInserted','DOMNodeInsertedIntoDocument','DOMNodeRemoved','DOMNodeRemovedFromDocument','DOMSubtreeModified','DOMContentLoaded'],['*']);this._createEventListenerBreakpoints(Common.UIString('Drag / drop'),['drag','dragstart','dragend','dragenter','dragover','dragleave','drop'],['*']);this._createEventListenerBreakpoints(Common.UIString('Keyboard'),['keydown','keyup','keypress','input'],['*']);this._createEventListenerBreakpoints(Common.UIString('Load'),['load','beforeunload','unload','abort','error','hashchange','popstate'],['*']);this._createEventListenerBreakpoints(Common.UIString('Mouse'),['auxclick','click','dblclick','mousedown','mouseup','mouseover','mousemove','mouseout','mouseenter','mouseleave','mousewheel','wheel','contextmenu'],['*']);this._createEventListenerBreakpoints(Common.UIString('Pointer'),['pointerover','pointerout','pointerenter','pointerleave','pointerdown','pointerup','pointermove','pointercancel','gotpointercapture','lostpointercapture'],['*']);this._createEventListenerBreakpoints(Common.UIString('Touch'),['touchstart','touchmove','touchend','touchcancel'],['*']);this._createEventListenerBreakpoints(Common.UIString('Worker'),['message','messageerror'],['*']);this._createEventListenerBreakpoints(Common.UIString('XHR'),['readystatechange','load','loadstart','loadend','abort','error','progress','timeout'],['xmlhttprequest','xmlhttprequestupload']);this._resolveEventListenerBreakpoint('instrumentation:setTimeout.callback')._title=Common.UIString('setTimeout fired');this._resolveEventListenerBreakpoint('instrumentation:setInterval.callback')._title=Common.UIString('setInterval fired');this._resolveEventListenerBreakpoint('instrumentation:scriptFirstStatement')._title=Common.UIString('Script First Statement');this._resolveEventListenerBreakpoint('instrumentation:scriptBlockedByCSP')._title=Common.UIString('Script Blocked by Content Security Policy');this._resolveEventListenerBreakpoint('instrumentation:requestAnimationFrame')._title=Common.UIString('Request Animation Frame');this._resolveEventListenerBreakpoint('instrumentation:cancelAnimationFrame')._title=Common.UIString('Cancel Animation Frame');this._resolveEventListenerBreakpoint('instrumentation:requestAnimationFrame.callback')._title=Common.UIString('Animation Frame Fired');this._resolveEventListenerBreakpoint('instrumentation:webglErrorFired')._title=Common.UIString('WebGL Error Fired');this._resolveEventListenerBreakpoint('instrumentation:webglWarningFired')._title=Common.UIString('WebGL Warning Fired');this._resolveEventListenerBreakpoint('instrumentation:Element.setInnerHTML')._title=Common.UIString('Set innerHTML');this._resolveEventListenerBreakpoint('instrumentation:canvasContextCreated')._title=Common.UIString('Create canvas context');this._resolveEventListenerBreakpoint('instrumentation:Geolocation.getCurrentPosition')._title='getCurrentPosition';this._resolveEventListenerBreakpoint('instrumentation:Geolocation.watchPosition')._title='watchPosition';this._resolveEventListenerBreakpoint('instrumentation:Notification.requestPermission')._title='requestPermission';this._resolveEventListenerBreakpoint('instrumentation:DOMWindow.close')._title='window.close';this._resolveEventListenerBreakpoint('instrumentation:Document.write')._title='document.write';this._resolveEventListenerBreakpoint('instrumentation:audioContextCreated')._title=Common.UIString('Create AudioContext');this._resolveEventListenerBreakpoint('instrumentation:audioContextClosed')._title=Common.UIString('Close AudioContext');this._resolveEventListenerBreakpoint('instrumentation:audioContextResumed')._title=Common.UIString('Resume AudioContext');this._resolveEventListenerBreakpoint('instrumentation:audioContextSuspended')._title=Common.UIString('Suspend AudioContext');SDK.targetManager.observeModels(SDK.DOMDebuggerModel,this);}
  65. _createInstrumentationBreakpoints(category,instrumentationNames){for(const instrumentationName of instrumentationNames){this._eventListenerBreakpoints.push(new EventListenerBreakpoint(instrumentationName,'',[],category,instrumentationName));}}
  66. _createEventListenerBreakpoints(category,eventNames,eventTargetNames){for(const eventName of eventNames){this._eventListenerBreakpoints.push(new EventListenerBreakpoint('',eventName,eventTargetNames,category,eventName));}}
  67. _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;}
  68. eventTargetName=(eventTargetName||'*').toLowerCase();let result=null;for(const breakpoint of this._eventListenerBreakpoints){if(instrumentationName&&breakpoint._instrumentationName===instrumentationName){result=breakpoint;}
  69. if(eventName&&breakpoint._eventName===eventName&&breakpoint._eventTargetNames.indexOf(eventTargetName)!==-1){result=breakpoint;}
  70. if(!result&&eventName&&breakpoint._eventName===eventName&&breakpoint._eventTargetNames.indexOf('*')!==-1){result=breakpoint;}}
  71. return result;}
  72. eventListenerBreakpoints(){return this._eventListenerBreakpoints.slice();}
  73. resolveEventListenerBreakpointTitle(auxData){const id=auxData['eventName'];if(id==='instrumentation:webglErrorFired'&&auxData['webglErrorName']){let errorName=auxData['webglErrorName'];errorName=errorName.replace(/^.*(0x[0-9a-f]+).*$/i,'$1');return Common.UIString('WebGL Error Fired (%s)',errorName);}
  74. if(id==='instrumentation:scriptBlockedByCSP'&&auxData['directiveText']){return Common.UIString('Script blocked due to Content Security Policy directive: %s',auxData['directiveText']);}
  75. const breakpoint=this._resolveEventListenerBreakpoint(id,auxData['targetName']);if(!breakpoint){return'';}
  76. if(auxData['targetName']){return auxData['targetName']+'.'+breakpoint._title;}
  77. return breakpoint._title;}
  78. resolveEventListenerBreakpoint(auxData){return this._resolveEventListenerBreakpoint(auxData['eventName'],auxData['targetName']);}
  79. xhrBreakpoints(){return this._xhrBreakpoints;}
  80. _saveXHRBreakpoints(){const breakpoints=[];for(const url of this._xhrBreakpoints.keys()){breakpoints.push({url:url,enabled:this._xhrBreakpoints.get(url)});}
  81. this._xhrBreakpointsSetting.set(breakpoints);}
  82. addXHRBreakpoint(url,enabled){this._xhrBreakpoints.set(url,enabled);if(enabled){for(const model of SDK.targetManager.models(DOMDebuggerModel)){model._agent.setXHRBreakpoint(url);}}
  83. this._saveXHRBreakpoints();}
  84. removeXHRBreakpoint(url){const enabled=this._xhrBreakpoints.get(url);this._xhrBreakpoints.delete(url);if(enabled){for(const model of SDK.targetManager.models(DOMDebuggerModel)){model._agent.removeXHRBreakpoint(url);}}
  85. this._saveXHRBreakpoints();}
  86. toggleXHRBreakpoint(url,enabled){this._xhrBreakpoints.set(url,enabled);for(const model of SDK.targetManager.models(DOMDebuggerModel)){if(enabled){model._agent.setXHRBreakpoint(url);}else{model._agent.removeXHRBreakpoint(url);}}
  87. this._saveXHRBreakpoints();}
  88. modelAdded(domDebuggerModel){for(const url of this._xhrBreakpoints.keys()){if(this._xhrBreakpoints.get(url)){domDebuggerModel._agent.setXHRBreakpoint(url);}}
  89. for(const breakpoint of this._eventListenerBreakpoints){if(breakpoint._enabled){breakpoint._updateOnModel(domDebuggerModel);}}}
  90. modelRemoved(domDebuggerModel){}}
  91. self.SDK=self.SDK||{};SDK=SDK||{};SDK.DOMDebuggerModel=DOMDebuggerModel;SDK.DOMDebuggerModel.Events=Events;SDK.DOMDebuggerModel.DOMBreakpoint=DOMBreakpoint;SDK.DOMDebuggerModel.EventListenerBreakpoint=EventListenerBreakpoint;SDK.EventListener=EventListener;SDK.DOMDebuggerManager=DOMDebuggerManager;SDK.SDKModel.register(SDK.DOMDebuggerModel,SDK.Target.Capability.DOM,false);SDK.DOMDebuggerModel.DOMBreakpoint.Type=Protocol.DOMDebugger.DOMBreakpointType;SDK.domDebuggerManager;