diff --git a/lib/harAnalyzer.js b/lib/harAnalyzer.js index 6bf1784..c9a77cb 100644 --- a/lib/harAnalyzer.js +++ b/lib/harAnalyzer.js @@ -20,6 +20,47 @@ export class HarAnalyzer { this.version = this.package.version; } + getFirstPageEntries(url, harData) { + if ('log' in harData) { + harData = harData['log']; + } + + const entries = harData.entries; + if (!Array.isArray(entries)) { + return []; + } + + // A HAR can contain more than one page, for example when a concurrent + // browsertime run ends up in the same browser session (crossed DevTools + // port) and navigates to another website mid-recording. Requests made + // by other pages must not be attributed to the tested website, and if + // the recording doesn't even start with the tested website nothing in + // it can be trusted. + if (url && entries.length > 0) { + const firstUrl = entries[0].request && entries[0].request.url; + if (firstUrl) { + try { + if (new URL(firstUrl).hostname !== new URL(url).hostname) { + return []; + } + } catch { + // Unparsable URLs are handled by the entry loops as before + } + } + } + + const pages = harData.pages; + if (!Array.isArray(pages) || pages.length === 0) { + return entries; + } + const firstPageId = pages[0].id; + if (firstPageId === undefined) { + return entries; + } + return entries.filter(entry => + entry.pageref === undefined || entry.pageref === firstPageId); + } + async transform2SimplifiedData(harData, url) { const data = { 'url': url, @@ -31,10 +72,6 @@ export class HarAnalyzer { 'style-files': [] }; - if ('log' in harData) { - harData = harData['log']; - } - let reqIndex = 1; // CSS resources whose response body is missing from the HAR (served from @@ -44,7 +81,7 @@ export class HarAnalyzer { // falsely reported as "Unknown custom property" (no-unknown-custom-properties). const missingCssUrls = new Set(); - for (const entry of harData.entries) { + for (const entry of this.getFirstPageEntries(url, harData)) { const req = entry.request; const res = entry.response; const reqUrl = req.url;