RuntimeModel.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. export default class RuntimeModel extends SDK.SDKModel{constructor(target){super(target);this._agent=target.runtimeAgent();this.target().registerRuntimeDispatcher(new RuntimeDispatcher(this));this._agent.enable();this._executionContextById=new Map();this._executionContextComparator=ExecutionContext.comparator;this._hasSideEffectSupport=null;if(Common.moduleSetting('customFormatters').get()){this._agent.setCustomObjectFormatterEnabled(true);}
  2. Common.moduleSetting('customFormatters').addChangeListener(this._customFormattersStateChanged.bind(this));}
  3. static isSideEffectFailure(response){const exceptionDetails=!response[Protocol.Error]&&response.exceptionDetails;return!!(exceptionDetails&&exceptionDetails.exception&&exceptionDetails.exception.description&&exceptionDetails.exception.description.startsWith('EvalError: Possible side-effect in debug-evaluate'));}
  4. debuggerModel(){return(this.target().model(SDK.DebuggerModel));}
  5. heapProfilerModel(){return(this.target().model(SDK.HeapProfilerModel));}
  6. executionContexts(){return this._executionContextById.valuesArray().sort(this.executionContextComparator());}
  7. setExecutionContextComparator(comparator){this._executionContextComparator=comparator;}
  8. executionContextComparator(){return this._executionContextComparator;}
  9. defaultExecutionContext(){for(const context of this.executionContexts()){if(context.isDefault){return context;}}
  10. return null;}
  11. executionContext(id){return this._executionContextById.get(id)||null;}
  12. _executionContextCreated(context){const data=context.auxData||{isDefault:true};const executionContext=new ExecutionContext(this,context.id,context.name,context.origin,data['isDefault'],data['frameId']);this._executionContextById.set(executionContext.id,executionContext);this.dispatchEventToListeners(Events.ExecutionContextCreated,executionContext);}
  13. _executionContextDestroyed(executionContextId){const executionContext=this._executionContextById.get(executionContextId);if(!executionContext){return;}
  14. this.debuggerModel().executionContextDestroyed(executionContext);this._executionContextById.delete(executionContextId);this.dispatchEventToListeners(Events.ExecutionContextDestroyed,executionContext);}
  15. fireExecutionContextOrderChanged(){this.dispatchEventToListeners(Events.ExecutionContextOrderChanged,this);}
  16. _executionContextsCleared(){this.debuggerModel().globalObjectCleared();const contexts=this.executionContexts();this._executionContextById.clear();for(let i=0;i<contexts.length;++i){this.dispatchEventToListeners(Events.ExecutionContextDestroyed,contexts[i]);}}
  17. createRemoteObject(payload){console.assert(typeof payload==='object','Remote object payload should only be an object');return new SDK.RemoteObjectImpl(this,payload.objectId,payload.type,payload.subtype,payload.value,payload.unserializableValue,payload.description,payload.preview,payload.customPreview,payload.className);}
  18. createScopeRemoteObject(payload,scopeRef){return new SDK.ScopeRemoteObject(this,payload.objectId,scopeRef,payload.type,payload.subtype,payload.value,payload.unserializableValue,payload.description,payload.preview);}
  19. createRemoteObjectFromPrimitiveValue(value){const type=typeof value;let unserializableValue=undefined;const unserializableDescription=SDK.RemoteObject.unserializableDescription(value);if(unserializableDescription!==null){unserializableValue=(unserializableDescription);}
  20. if(typeof unserializableValue!=='undefined'){value=undefined;}
  21. return new SDK.RemoteObjectImpl(this,undefined,type,undefined,value,unserializableValue);}
  22. createRemotePropertyFromPrimitiveValue(name,value){return new SDK.RemoteObjectProperty(name,this.createRemoteObjectFromPrimitiveValue(value));}
  23. discardConsoleEntries(){this._agent.discardConsoleEntries();}
  24. releaseObjectGroup(objectGroupName){this._agent.releaseObjectGroup(objectGroupName);}
  25. releaseEvaluationResult(result){if(result.object){result.object.release();}
  26. if(result.exceptionDetails&&result.exceptionDetails.exception){const exception=result.exceptionDetails.exception;const exceptionObject=this.createRemoteObject({type:exception.type,objectId:exception.objectId});exceptionObject.release();}}
  27. runIfWaitingForDebugger(){this._agent.runIfWaitingForDebugger();}
  28. _customFormattersStateChanged(event){const enabled=(event.data);this._agent.setCustomObjectFormatterEnabled(enabled);}
  29. async compileScript(expression,sourceURL,persistScript,executionContextId){const response=await this._agent.invoke_compileScript({expression:String.escapeInvalidUnicodeCharacters(expression),sourceURL:sourceURL,persistScript:persistScript,executionContextId:executionContextId});if(response[Protocol.Error]){console.error(response[Protocol.Error]);return null;}
  30. return{scriptId:response.scriptId,exceptionDetails:response.exceptionDetails};}
  31. async runScript(scriptId,executionContextId,objectGroup,silent,includeCommandLineAPI,returnByValue,generatePreview,awaitPromise){const response=await this._agent.invoke_runScript({scriptId,executionContextId,objectGroup,silent,includeCommandLineAPI,returnByValue,generatePreview,awaitPromise});const error=response[Protocol.Error];if(error){console.error(error);return{error:error};}
  32. return{object:this.createRemoteObject(response.result),exceptionDetails:response.exceptionDetails};}
  33. async queryObjects(prototype){if(!prototype.objectId){return{error:'Prototype should be an Object.'};}
  34. const response=await this._agent.invoke_queryObjects({prototypeObjectId:(prototype.objectId),objectGroup:'console'});const error=response[Protocol.Error];if(error){console.error(error);return{error:error};}
  35. return{objects:this.createRemoteObject(response.objects)};}
  36. async isolateId(){return(await this._agent.getIsolateId())||this.target().id();}
  37. async heapUsage(){const result=await this._agent.invoke_getHeapUsage({});return result[Protocol.Error]?null:result;}
  38. _inspectRequested(payload,hints){const object=this.createRemoteObject(payload);if(hints.copyToClipboard){this._copyRequested(object);return;}
  39. if(hints.queryObjects){this._queryObjectsRequested(object);return;}
  40. if(object.isNode()){Common.Revealer.reveal(object).then(object.release.bind(object));return;}
  41. if(object.type==='function'){SDK.RemoteFunction.objectAsFunction(object).targetFunctionDetails().then(didGetDetails);return;}
  42. function didGetDetails(response){object.release();if(!response||!response.location){return;}
  43. Common.Revealer.reveal(response.location);}
  44. object.release();}
  45. _copyRequested(object){if(!object.objectId){Host.InspectorFrontendHost.copyText(object.unserializableValue()||(object.value));return;}
  46. object.callFunctionJSON(toStringForClipboard,[{value:object.subtype}]).then(Host.InspectorFrontendHost.copyText.bind(Host.InspectorFrontendHost));function toStringForClipboard(subtype){if(subtype==='node'){return this.outerHTML;}
  47. if(subtype&&typeof this==='undefined'){return subtype+'';}
  48. try{return JSON.stringify(this,null,' ');}catch(e){return''+this;}}}
  49. async _queryObjectsRequested(object){const result=await this.queryObjects(object);object.release();if(result.error){Common.console.error(result.error);return;}
  50. this.dispatchEventToListeners(Events.QueryObjectRequested,{objects:result.objects});}
  51. static simpleTextFromException(exceptionDetails){let text=exceptionDetails.text;if(exceptionDetails.exception&&exceptionDetails.exception.description){let description=exceptionDetails.exception.description;if(description.indexOf('\n')!==-1){description=description.substring(0,description.indexOf('\n'));}
  52. text+=' '+description;}
  53. return text;}
  54. exceptionThrown(timestamp,exceptionDetails){const exceptionWithTimestamp={timestamp:timestamp,details:exceptionDetails};this.dispatchEventToListeners(Events.ExceptionThrown,exceptionWithTimestamp);}
  55. _exceptionRevoked(exceptionId){this.dispatchEventToListeners(Events.ExceptionRevoked,exceptionId);}
  56. _consoleAPICalled(type,args,executionContextId,timestamp,stackTrace,context){const consoleAPICall={type:type,args:args,executionContextId:executionContextId,timestamp:timestamp,stackTrace:stackTrace,context:context};this.dispatchEventToListeners(Events.ConsoleAPICalled,consoleAPICall);}
  57. executionContextIdForScriptId(scriptId){const script=this.debuggerModel().scriptForId(scriptId);return script?script.executionContextId:0;}
  58. executionContextForStackTrace(stackTrace){while(stackTrace&&!stackTrace.callFrames.length){stackTrace=stackTrace.parent;}
  59. if(!stackTrace||!stackTrace.callFrames.length){return 0;}
  60. return this.executionContextIdForScriptId(stackTrace.callFrames[0].scriptId);}
  61. hasSideEffectSupport(){return this._hasSideEffectSupport;}
  62. async checkSideEffectSupport(){const testContext=this.executionContexts().peekLast();if(!testContext){return false;}
  63. const response=await this._agent.invoke_evaluate({expression:String.escapeInvalidUnicodeCharacters(_sideEffectTestExpression),contextId:testContext.id,throwOnSideEffect:true});this._hasSideEffectSupport=RuntimeModel.isSideEffectFailure(response);return this._hasSideEffectSupport;}
  64. terminateExecution(){return this._agent.invoke_terminateExecution({});}}
  65. const _sideEffectTestExpression='(async function(){ await 1; })()';export const Events={ExecutionContextCreated:Symbol('ExecutionContextCreated'),ExecutionContextDestroyed:Symbol('ExecutionContextDestroyed'),ExecutionContextChanged:Symbol('ExecutionContextChanged'),ExecutionContextOrderChanged:Symbol('ExecutionContextOrderChanged'),ExceptionThrown:Symbol('ExceptionThrown'),ExceptionRevoked:Symbol('ExceptionRevoked'),ConsoleAPICalled:Symbol('ConsoleAPICalled'),QueryObjectRequested:Symbol('QueryObjectRequested'),};class RuntimeDispatcher{constructor(runtimeModel){this._runtimeModel=runtimeModel;}
  66. executionContextCreated(context){this._runtimeModel._executionContextCreated(context);}
  67. executionContextDestroyed(executionContextId){this._runtimeModel._executionContextDestroyed(executionContextId);}
  68. executionContextsCleared(){this._runtimeModel._executionContextsCleared();}
  69. exceptionThrown(timestamp,exceptionDetails){this._runtimeModel.exceptionThrown(timestamp,exceptionDetails);}
  70. exceptionRevoked(reason,exceptionId){this._runtimeModel._exceptionRevoked(exceptionId);}
  71. consoleAPICalled(type,args,executionContextId,timestamp,stackTrace,context){this._runtimeModel._consoleAPICalled(type,args,executionContextId,timestamp,stackTrace,context);}
  72. inspectRequested(payload,hints){this._runtimeModel._inspectRequested(payload,hints);}}
  73. export class ExecutionContext{constructor(runtimeModel,id,name,origin,isDefault,frameId){this.id=id;this.name=name;this.origin=origin;this.isDefault=isDefault;this.runtimeModel=runtimeModel;this.debuggerModel=runtimeModel.debuggerModel();this.frameId=frameId;this._setLabel('');}
  74. target(){return this.runtimeModel.target();}
  75. static comparator(a,b){function targetWeight(target){if(!target.parentTarget()){return 5;}
  76. if(target.type()===SDK.Target.Type.Frame){return 4;}
  77. if(target.type()===SDK.Target.Type.ServiceWorker){return 3;}
  78. if(target.type()===SDK.Target.Type.Worker){return 2;}
  79. return 1;}
  80. function targetPath(target){let currentTarget=target;const parents=[];while(currentTarget){parents.push(currentTarget);currentTarget=currentTarget.parentTarget();}
  81. return parents.reverse();}
  82. const tagetsA=targetPath(a.target());const targetsB=targetPath(b.target());let targetA;let targetB;for(let i=0;;i++){if(!tagetsA[i]||!targetsB[i]||(tagetsA[i]!==targetsB[i])){targetA=tagetsA[i];targetB=targetsB[i];break;}}
  83. if(!targetA&&targetB){return-1;}
  84. if(!targetB&&targetA){return 1;}
  85. if(targetA&&targetB){const weightDiff=targetWeight(targetA)-targetWeight(targetB);if(weightDiff){return-weightDiff;}
  86. return targetA.id().localeCompare(targetB.id());}
  87. if(a.isDefault){return-1;}
  88. if(b.isDefault){return+1;}
  89. return a.name.localeCompare(b.name);}
  90. evaluate(options,userGesture,awaitPromise){if(this.debuggerModel.selectedCallFrame()){return this.debuggerModel.evaluateOnSelectedCallFrame(options);}
  91. const needsTerminationOptions=!!options.throwOnSideEffect||options.timeout!==undefined;if(!needsTerminationOptions||this.runtimeModel.hasSideEffectSupport()){return this._evaluateGlobal(options,userGesture,awaitPromise);}
  92. const unsupportedError={error:'Side-effect checks not supported by backend.'};if(this.runtimeModel.hasSideEffectSupport()===false){return Promise.resolve(unsupportedError);}
  93. return this.runtimeModel.checkSideEffectSupport().then(()=>{if(this.runtimeModel.hasSideEffectSupport()){return this._evaluateGlobal(options,userGesture,awaitPromise);}
  94. return Promise.resolve(unsupportedError);});}
  95. globalObject(objectGroup,generatePreview){return this._evaluateGlobal({expression:'this',objectGroup:objectGroup,includeCommandLineAPI:false,silent:true,returnByValue:false,generatePreview:generatePreview},false,false);}
  96. async _evaluateGlobal(options,userGesture,awaitPromise){if(!options.expression){options.expression='this';}
  97. const response=await this.runtimeModel._agent.invoke_evaluate({expression:String.escapeInvalidUnicodeCharacters(options.expression),objectGroup:options.objectGroup,includeCommandLineAPI:options.includeCommandLineAPI,silent:options.silent,contextId:this.id,returnByValue:options.returnByValue,generatePreview:options.generatePreview,userGesture:userGesture,awaitPromise:awaitPromise,throwOnSideEffect:options.throwOnSideEffect,timeout:options.timeout,disableBreaks:options.disableBreaks,replMode:options.replMode});const error=response[Protocol.Error];if(error){console.error(error);return{error:error};}
  98. return{object:this.runtimeModel.createRemoteObject(response.result),exceptionDetails:response.exceptionDetails};}
  99. async globalLexicalScopeNames(){const response=await this.runtimeModel._agent.invoke_globalLexicalScopeNames({executionContextId:this.id});return response[Protocol.Error]?[]:response.names;}
  100. label(){return this._label;}
  101. setLabel(label){this._setLabel(label);this.runtimeModel.dispatchEventToListeners(Events.ExecutionContextChanged,this);}
  102. _setLabel(label){if(label){this._label=label;return;}
  103. if(this.name){this._label=this.name;return;}
  104. const parsedUrl=this.origin.asParsedURL();this._label=parsedUrl?parsedUrl.lastPathComponentWithFragment():'';}}
  105. self.SDK=self.SDK||{};SDK=SDK||{};SDK.RuntimeModel=RuntimeModel;SDK.RuntimeModel.Events=Events;SDK.ExecutionContext=ExecutionContext;SDK.RuntimeModel.CompileScriptResult;SDK.RuntimeModel.EvaluationOptions;SDK.RuntimeModel.EvaluationResult;SDK.RuntimeModel.QueryObjectResult;SDK.RuntimeModel.ConsoleAPICall;SDK.RuntimeModel.ExceptionWithTimestamp;SDK.SDKModel.register(SDK.RuntimeModel,SDK.Target.Capability.JS,true);