UNPKG

@react-native/debugger-frontend

Version:
2 lines (1 loc) • 12.4 kB
import*as e from"../core/core.js";class t{static types={NETWORK:"network",CPU:"cpu"};_id;_isMainDocument;_dependents;_dependencies;constructor(e){this._id=e,this._isMainDocument=!1,this._dependents=[],this._dependencies=[]}get id(){return this._id}get type(){throw new e.LanternError("Unimplemented")}get startTime(){throw new e.LanternError("Unimplemented")}get endTime(){throw new e.LanternError("Unimplemented")}setIsMainDocument(e){this._isMainDocument=e}isMainDocument(){return this._isMainDocument}getDependents(){return this._dependents.slice()}getNumberOfDependents(){return this._dependents.length}getDependencies(){return this._dependencies.slice()}getNumberOfDependencies(){return this._dependencies.length}getRootNode(){let e=this;for(;e._dependencies.length;)e=e._dependencies[0];return e}addDependent(e){e.addDependency(this)}addDependency(t){if(t===this)throw new e.LanternError("Cannot add dependency on itself");this._dependencies.includes(t)||(t._dependents.push(this),this._dependencies.push(t))}removeDependent(e){e.removeDependency(this)}removeDependency(e){if(!this._dependencies.includes(e))return;const t=e._dependents.indexOf(this);e._dependents.splice(t,1),this._dependencies.splice(this._dependencies.indexOf(e),1)}removeAllDependencies(){for(const e of this._dependencies.slice())this.removeDependency(e)}isDependentOn(e){let t=!1;return this.traverse((r=>{t||(t=r===e)}),(e=>t?[]:e.getDependencies())),t}cloneWithoutRelationships(){const e=new t(this.id);return e.setIsMainDocument(this._isMainDocument),e}cloneWithRelationships(t){const r=this.getRootNode(),n=new Map;r.traverse((e=>{n.has(e.id)||(void 0!==t?t(e)&&e.traverse((e=>n.set(e.id,e.cloneWithoutRelationships())),(e=>e._dependencies.filter((e=>!n.has(e.id))))):n.set(e.id,e.cloneWithoutRelationships()))})),r.traverse((t=>{const r=n.get(t.id);if(r)for(const s of t._dependencies){const t=n.get(s.id);if(!t)throw new e.LanternError("Dependency somehow not cloned");r.addDependency(t)}}));const s=n.get(this.id);if(!s)throw new e.LanternError("Cloned graph missing node");return s}traverse(e,t){for(const{node:r,traversalPath:n}of this.traverseGenerator(t))e(r,n)}*traverseGenerator(e){e||(e=e=>e.getDependents());const t=[[this]],r=new Set([this.id]);for(;t.length;){const n=t.shift(),s=n[0];yield{node:s,traversalPath:n};for(const i of e(s))r.has(i.id)||(r.add(i.id),t.push([i,...n]))}}static hasCycle(e,r="both"){if("both"===r)return t.hasCycle(e,"dependents")||t.hasCycle(e,"dependencies");const n=new Set,s=[],i=[e],o=new Map([[e,0]]);for(;i.length;){const e=i.pop();if(s.includes(e))return!0;if(n.has(e))continue;for(;s.length>o.get(e);)s.pop();n.add(e),s.push(e);const t="dependents"===r?e._dependents:e._dependencies;for(const e of t)i.includes(e)||(i.push(e),o.set(e,s.length))}return!1}canDependOn(e){return e.startTime<=this.startTime}}class r extends t{_event;_childEvents;_correctedEndTs;constructor(e,t=[],r){super(`${e.tid}.${e.ts}`),this._event=e,this._childEvents=t,this._correctedEndTs=r}get type(){return t.types.CPU}get startTime(){return this._event.ts}get endTime(){return this._correctedEndTs?this._correctedEndTs:this._event.ts+this._event.dur}get duration(){return this.endTime-this.startTime}get event(){return this._event}get childEvents(){return this._childEvents}didPerformLayout(){return this._childEvents.some((e=>"Layout"===e.name))}getEvaluateScriptURLs(){const e=new Set;for(const t of this._childEvents)"EvaluateScript"===t.name&&t.args.data&&t.args.data.url&&e.add(t.args.data.url);return e}cloneWithoutRelationships(){return new r(this._event,this._childEvents,this._correctedEndTs)}}const n=["blob","data","intent","file","filesystem","chrome-extension"];function s(e){const t=e.includes(":")?e.slice(0,e.indexOf(":")):e;return n.includes(t)}class i extends t{_request;constructor(e){super(e.requestId),this._request=e}get type(){return t.types.NETWORK}get startTime(){return 1e3*this._request.rendererStartTime}get endTime(){return 1e3*this._request.networkEndTime}get rawRequest(){return this._request.rawRequest}get request(){return this._request}get initiatorType(){return this._request.initiator&&this._request.initiator.type}get fromDiskCache(){return Boolean(this._request.fromDiskCache)}get isNonNetworkProtocol(){return s(this.request.protocol)||s(this.request.parsedURL.scheme)}get isConnectionless(){return this.fromDiskCache||this.isNonNetworkProtocol}hasRenderBlockingPriority(){const e=this._request.priority,t="Script"===this._request.resourceType,r="Document"===this._request.resourceType;return"VeryHigh"===e||"High"===e&&t||"High"===e&&r}cloneWithoutRelationships(){const e=new i(this._request);return e.setIsMainDocument(this._isMainDocument),e}}const o=/^video/;class d{static getNetworkInitiators(e){if(!e.initiator)return[];if(e.initiator.url)return[e.initiator.url];if("script"===e.initiator.type){const t=new Set;let r=e.initiator.stack;for(;r;){const e=r.callFrames||[];for(const r of e)r.url&&t.add(r.url);r=r.parent}return Array.from(t)}return[]}static getNetworkNodeOutput(e){const t=[],r=new Map,n=new Map,s=new Map;return e.forEach((e=>{if(o.test(e.mimeType))return;if(e.fromWorker)return;for(;r.has(e.requestId);)e.requestId+=":duplicate";const d=new i(e);t.push(d);const a=n.get(e.url)||[];if(a.push(d),r.set(e.requestId,d),n.set(e.url,a),e.frameId&&"Document"===e.resourceType&&e.documentURL===e.url){const t=s.has(e.frameId)?null:d;s.set(e.frameId,t)}})),{nodes:t,idToNodeMap:r,urlToNodeMap:n,frameIdToNodeMap:s}}static isScheduleableTask(e){return"RunTask"===e.name||"ThreadControllerImpl::RunTask"===e.name||"ThreadControllerImpl::DoWork"===e.name||"TaskQueueManager::ProcessTaskFromWorkQueue"===e.name}static assertHasToplevelEvents(t){if(!t.some(this.isScheduleableTask))throw new e.LanternError("Could not find any top level events")}static getCPUNodes(e){const t=[];let n=0;for(d.assertHasToplevelEvents(e);n<e.length;){const s=e[n];if(n++,!d.isScheduleableTask(s)||!s.dur)continue;let i;const o=[];for(const t=s.ts+s.dur;n<e.length&&e[n].ts<t;n++){const t=e[n];if(d.isScheduleableTask(t)&&t.dur){i=t.ts-1;break}o.push(t)}t.push(new r(s,o,i))}return t}static linkNetworkNodes(e,t){t.nodes.forEach((r=>{const n=r.request.initiatorRequest||e.request,s=t.idToNodeMap.get(n.requestId)||e,i=!s.isDependentOn(r)&&r.canDependOn(s),o=d.getNetworkInitiators(r.request);if(o.length?o.forEach((e=>{const n=t.urlToNodeMap.get(e)||[];1===n.length&&n[0].startTime<=r.startTime&&!n[0].isDependentOn(r)?r.addDependency(n[0]):i&&s.addDependent(r)})):i&&s.addDependent(r),r!==e&&0===r.getDependencies().length&&r.canDependOn(e)&&r.addDependency(e),!r.request.redirects)return;const a=[...r.request.redirects,r.request];for(let e=1;e<a.length;e++){const r=t.idToNodeMap.get(a[e-1].requestId),n=t.idToNodeMap.get(a[e].requestId);n&&r&&n.addDependency(r)}}))}static linkCPUNodes(e,t,r){const n=new Set(["XHR","Fetch","Script"]);function s(e,r){const s=t.idToNodeMap.get(r);if(!s||s.startTime<=e.startTime)return;const{request:i}=s,o=i.resourceType||i.redirectDestination?.resourceType;n.has(o)&&e.addDependent(s)}function i(e,r){if(!r)return;const n=t.frameIdToNodeMap.get(r);n&&(n.startTime>=e.startTime||e.addDependency(n))}function o(e,r){if(!r)return;const n=t.urlToNodeMap.get(r)||[];let s=null,i=1/0;for(const t of n){if(e.startTime<=t.startTime)return;const r=e.startTime-t.endTime;r>=-1e5&&r<i&&(s=t,i=r)}s&&e.addDependency(s)}const a=new Map;for(const t of r){for(const e of t.childEvents){if(!e.args.data)continue;const r=e.args.data.url,n=(e.args.data.stackTrace||[]).map((e=>e.url)).filter(Boolean);switch(e.name){case"TimerInstall":a.set(e.args.data.timerId,t),n.forEach((e=>o(t,e)));break;case"TimerFire":{const r=a.get(e.args.data.timerId);if(!r||r.endTime>t.startTime)break;r.addDependent(t);break}case"InvalidateLayout":case"ScheduleStyleRecalculation":i(t,e.args.data.frame),n.forEach((e=>o(t,e)));break;case"EvaluateScript":i(t,e.args.data.frame),o(t,r),n.forEach((e=>o(t,e)));break;case"XHRReadyStateChange":if(4!==e.args.data.readyState)break;o(t,r),n.forEach((e=>o(t,e)));break;case"FunctionCall":case"v8.compile":i(t,e.args.data.frame),o(t,r);break;case"ParseAuthorStyleSheet":i(t,e.args.data.frame),o(t,e.args.data.styleSheetUrl);break;case"ResourceSendRequest":i(t,e.args.data.frame),s(t,e.args.data.requestId),n.forEach((e=>o(t,e)))}}0===t.getNumberOfDependencies()&&t.canDependOn(e)&&t.addDependency(e)}let c=!1,u=!1,h=!1;for(const e of r){let t=!1;!c&&e.childEvents.some((e=>"Layout"===e.name))&&(t=c=!0),!u&&e.childEvents.some((e=>"Paint"===e.name))&&(t=u=!0),!h&&e.childEvents.some((e=>"ParseHTML"===e.name))&&(t=h=!0),t||e.duration>=1e4||(1===e.getNumberOfDependencies()||e.getNumberOfDependents()<=1)&&d._pruneNode(e)}}static _pruneNode(e){const t=e.getDependencies(),r=e.getDependents();for(const n of t){e.removeDependency(n);for(const e of r)n.addDependent(e)}for(const t of r)e.removeDependent(t)}static _debugNormalizeRequests(e){for(const t of e)t.rendererStartTime=Math.round(1e3*t.rendererStartTime)/1e3,t.networkRequestTime=Math.round(1e3*t.networkRequestTime)/1e3,t.responseHeadersEndTime=Math.round(1e3*t.responseHeadersEndTime)/1e3,t.networkEndTime=Math.round(1e3*t.networkEndTime)/1e3;for(const t of e)delete t.rawRequest,t.initiatorRequest&&(t.initiatorRequest={id:t.initiatorRequest.requestId}),t.redirectDestination&&(t.redirectDestination={id:t.redirectDestination.requestId}),t.redirectSource&&(t.redirectSource={id:t.redirectSource.requestId}),t.redirects&&(t.redirects=t.redirects.map((e=>e.requestId)));const t=e.map((e=>({requestId:e.requestId,connectionId:e.connectionId,connectionReused:e.connectionReused,url:e.url,protocol:e.protocol,parsedURL:e.parsedURL,documentURL:e.documentURL,rendererStartTime:e.rendererStartTime,networkRequestTime:e.networkRequestTime,responseHeadersEndTime:e.responseHeadersEndTime,networkEndTime:e.networkEndTime,transferSize:e.transferSize,resourceSize:e.resourceSize,fromDiskCache:e.fromDiskCache,fromMemoryCache:e.fromMemoryCache,finished:e.finished,statusCode:e.statusCode,redirectSource:e.redirectSource,redirectDestination:e.redirectDestination,redirects:e.redirects,failed:e.failed,initiator:e.initiator,timing:e.timing?{requestTime:e.timing.requestTime,proxyStart:e.timing.proxyStart,proxyEnd:e.timing.proxyEnd,dnsStart:e.timing.dnsStart,dnsEnd:e.timing.dnsEnd,connectStart:e.timing.connectStart,connectEnd:e.timing.connectEnd,sslStart:e.timing.sslStart,sslEnd:e.timing.sslEnd,workerStart:e.timing.workerStart,workerReady:e.timing.workerReady,workerFetchStart:e.timing.workerFetchStart,workerRespondWithSettled:e.timing.workerRespondWithSettled,sendStart:e.timing.sendStart,sendEnd:e.timing.sendEnd,pushStart:e.timing.pushStart,pushEnd:e.timing.pushEnd,receiveHeadersStart:e.timing.receiveHeadersStart,receiveHeadersEnd:e.timing.receiveHeadersEnd}:e.timing,resourceType:e.resourceType,mimeType:e.mimeType,priority:e.priority,initiatorRequest:e.initiatorRequest,frameId:e.frameId,fromWorker:e.fromWorker,isLinkPreload:e.isLinkPreload,serverResponseTime:e.serverResponseTime}))).filter((e=>!e.fromWorker));console.log(t)}static createGraph(t,r,n){const s=d.getNetworkNodeOutput(r),o=d.getCPUNodes(t),{requestedUrl:a,mainDocumentUrl:c}=n;if(!a)throw new e.LanternError("requestedUrl is required to get the root request");if(!c)throw new e.LanternError("mainDocumentUrl is required to get the main resource");const u=e.NetworkAnalyzer.findResourceForUrl(r,a);if(!u)throw new e.LanternError("rootRequest not found");const h=s.idToNodeMap.get(u.requestId);if(!h)throw new e.LanternError("rootNode not found");const p=e.NetworkAnalyzer.findLastDocumentForUrl(r,c);if(!p)throw new e.LanternError("mainDocumentRequest not found");const m=s.idToNodeMap.get(p.requestId);if(!m)throw new e.LanternError("mainDocumentNode not found");if(d.linkNetworkNodes(h,s),d.linkCPUNodes(h,s,o),m.setIsMainDocument(!0),i.hasCycle(h))throw new e.LanternError("Invalid dependency graph created, cycle detected");return h}static printGraph(e,t=100){function r(e,t,r=" "){return e+r.repeat(Math.max(t-e.length,0))}const n=[];e.traverse((e=>n.push(e))),n.sort(((e,t)=>e.startTime-t.startTime));const s=n[0].startTime,i=(n.reduce(((e,t)=>Math.max(e,t.endTime)),0)-s)/t;n.forEach((e=>{const n=Math.round((e.startTime-s)/i),o=Math.ceil((e.endTime-e.startTime)/i),d=r("",n)+r("",o,"="),a=e.request?e.request.url:e.type;console.log(r(d,t),`| ${a.slice(0,30)}`)}))}}export{t as BaseNode,r as CPUNode,i as NetworkNode,d as PageDependencyGraph};