har-importer.js (4949B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { 8 TIMING_KEYS, 9 } = require("resource://devtools/client/netmonitor/src/constants.js"); 10 const { 11 getUrlDetails, 12 } = require("resource://devtools/client/netmonitor/src/utils/request-utils.js"); 13 14 var guid = 0; 15 16 /** 17 * This object is responsible for importing HAR file. See HAR spec: 18 * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html 19 * http://www.softwareishard.com/blog/har-12-spec/ 20 */ 21 class HarImporter { 22 constructor(actions) { 23 this.actions = actions; 24 } 25 /** 26 * This is the main method used to import HAR data. 27 */ 28 import(har) { 29 const json = JSON.parse(har); 30 this.doImport(json); 31 } 32 33 doImport(har) { 34 this.actions.clearRequests({ isExplicitClear: true }); 35 36 // Helper map for pages. 37 const pages = new Map(); 38 har.log.pages.forEach(page => { 39 pages.set(page.id, page); 40 }); 41 42 // Iterate all entries/requests and generate state. 43 har.log.entries.forEach(entry => { 44 const requestId = String(++guid); 45 const startedMs = Date.parse(entry.startedDateTime); 46 47 // Add request 48 this.actions.addRequest( 49 requestId, 50 { 51 startedMs, 52 method: entry.request.method, 53 url: entry.request.url, 54 urlDetails: getUrlDetails(entry.request.url), 55 isXHR: false, 56 cause: { 57 loadingDocumentUri: "", 58 stackTraceAvailable: false, 59 type: "", 60 }, 61 fromCache: false, 62 fromServiceWorker: false, 63 }, 64 false 65 ); 66 67 // Update request 68 const data = { 69 requestHeaders: { 70 headers: entry.request.headers, 71 headersSize: entry.request.headersSize, 72 rawHeaders: "", 73 }, 74 responseHeaders: { 75 headers: entry.response.headers, 76 headersSize: entry.response.headersSize, 77 rawHeaders: "", 78 }, 79 requestCookies: entry.request.cookies, 80 responseCookies: entry.response.cookies, 81 requestPostData: { 82 postData: entry.request.postData || {}, 83 postDataDiscarded: false, 84 }, 85 responseContent: { 86 content: entry.response.content, 87 contentDiscarded: false, 88 }, 89 eventTimings: { 90 timings: entry.timings, 91 }, 92 totalTime: TIMING_KEYS.reduce((sum, type) => { 93 const time = entry.timings[type]; 94 return typeof time != "undefined" && time != -1 ? sum + time : sum; 95 }, 0), 96 97 httpVersion: entry.request.httpVersion, 98 contentSize: entry.response.content.size, 99 mimeType: entry.response.content.mimeType, 100 remoteAddress: entry.serverIPAddress, 101 remotePort: entry.connection, 102 status: entry.response.status, 103 statusText: entry.response.statusText, 104 transferredSize: entry.response.bodySize, 105 securityState: entry._securityState, 106 107 // Avoid auto-fetching data from the backend 108 eventTimingsAvailable: false, 109 requestCookiesAvailable: false, 110 requestHeadersAvailable: false, 111 responseContentAvailable: false, 112 responseStartAvailable: false, 113 responseCookiesAvailable: false, 114 responseHeadersAvailable: false, 115 securityInfoAvailable: false, 116 requestPostDataAvailable: false, 117 }; 118 119 if (entry.cache.afterRequest) { 120 const { afterRequest } = entry.cache; 121 data.responseCache = { 122 cache: { 123 expires: afterRequest.expires, 124 fetchCount: afterRequest.fetchCount, 125 lastFetched: afterRequest.lastFetched, 126 // TODO: eTag support, see Bug 1799844. 127 // eTag: afterRequest.eTag, 128 _dataSize: afterRequest._dataSize, 129 _lastModified: afterRequest._lastModified, 130 _device: afterRequest._device, 131 }, 132 }; 133 } 134 135 this.actions.updateRequest(requestId, data, false); 136 137 // Page timing markers 138 const pageTimings = pages.get(entry.pageref)?.pageTimings; 139 let onContentLoad = (pageTimings && pageTimings.onContentLoad) || 0; 140 let onLoad = (pageTimings && pageTimings.onLoad) || 0; 141 142 // Set 0 as the default value 143 onContentLoad = onContentLoad != -1 ? onContentLoad : 0; 144 onLoad = onLoad != -1 ? onLoad : 0; 145 146 // Add timing markers 147 if (onContentLoad > 0) { 148 this.actions.addTimingMarker({ 149 name: "dom-interactive", 150 time: startedMs + onContentLoad, 151 }); 152 } 153 154 if (onLoad > 0) { 155 this.actions.addTimingMarker({ 156 name: "dom-complete", 157 time: startedMs + onLoad, 158 }); 159 } 160 }); 161 } 162 } 163 164 // Exports from this module 165 exports.HarImporter = HarImporter;