lighthouse 9.5.0-dev.20230126 → 9.5.0-dev.20230128

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,7 +41,7 @@ class o{constructor(e,t){this._document=e,this._lighthouseChannel="unknown",this
41
41
  * @license Copyright 2023 The Lighthouse Authors. All Rights Reserved.
42
42
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
43
43
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
44
- */const l="data:image/jpeg;base64,",s=r.RATINGS;class c{static prepareReportResult(e){const t=JSON.parse(JSON.stringify(e));t.configSettings.locale||(t.configSettings.locale="en"),t.configSettings.formFactor||(t.configSettings.formFactor=t.configSettings.emulatedFormFactor),t.finalDisplayedUrl=r.getFinalDisplayedUrl(t),t.mainDocumentUrl=r.getMainDocumentUrl(t);for(const e of Object.values(t.audits))if("not_applicable"!==e.scoreDisplayMode&&"not-applicable"!==e.scoreDisplayMode||(e.scoreDisplayMode="notApplicable"),e.details){if(void 0!==e.details.type&&"diagnostic"!==e.details.type||(e.details.type="debugdata"),"filmstrip"===e.details.type)for(const t of e.details.items)t.data.startsWith(l)||(t.data=l+t.data);if("table"===e.details.type)for(const t of e.details.headings){const{itemType:e,text:n}=t;void 0!==e&&(t.valueType=e,delete t.itemType),void 0!==n&&(t.label=n,delete t.text);const r=t.subItemsHeading?.itemType;t.subItemsHeading&&void 0!==r&&(t.subItemsHeading.valueType=r,delete t.subItemsHeading.itemType)}}if("object"!=typeof t.categories)throw new Error("No categories provided.");const n=new Map,[o]=t.lighthouseVersion.split(".").map(Number),i=t.categories.performance;if(o<9&&i){t.categoryGroups||(t.categoryGroups={}),t.categoryGroups.hidden={title:""};for(const e of i.auditRefs)e.group?["load-opportunities","diagnostics"].includes(e.group)&&delete e.group:e.group="hidden"}for(const e of Object.values(t.categories))e.auditRefs.forEach((e=>{e.relevantAudits&&e.relevantAudits.forEach((t=>{const r=n.get(t)||[];r.push(e),n.set(t,r)}))})),e.auditRefs.forEach((e=>{const r=t.audits[e.id];e.result=r,n.has(e.id)&&(e.relevantMetrics=n.get(e.id)),t.stackPacks&&t.stackPacks.forEach((t=>{t.descriptions[e.id]&&(e.stackPacks=e.stackPacks||[],e.stackPacks.push({title:t.title,iconDataURL:t.iconDataURL,description:t.descriptions[e.id]}))}))}));if(t.environment||(t.environment={benchmarkIndex:0}),t.configSettings.screenEmulation||(t.configSettings.screenEmulation={}),t.i18n||(t.i18n={}),t.audits["full-page-screenshot"]){const e=t.audits["full-page-screenshot"].details;t.fullPageScreenshot=e?{screenshot:e.screenshot,nodes:e.nodes}:null,delete t.audits["full-page-screenshot"]}return t}static getEmulationDescriptions(e){let t,n,r;const o=e.throttling,i=a.i18n,l=a.strings;switch(e.throttlingMethod){case"provided":r=n=t=l.throttlingProvided;break;case"devtools":{const{cpuSlowdownMultiplier:e,requestLatencyMs:a}=o;t=`${i.formatNumber(e)}x slowdown (DevTools)`,n=`${i.formatMilliseconds(a)} HTTP RTT, ${i.formatKbps(o.downloadThroughputKbps)} down, ${i.formatKbps(o.uploadThroughputKbps)} up (DevTools)`;r=(()=>562.5===a&&o.downloadThroughputKbps===1638.4*.9&&675===o.uploadThroughputKbps)()?l.runtimeSlow4g:l.runtimeCustom;break}case"simulate":{const{cpuSlowdownMultiplier:e,rttMs:a,throughputKbps:s}=o;t=`${i.formatNumber(e)}x slowdown (Simulated)`,n=`${i.formatMilliseconds(a)} TCP RTT, ${i.formatKbps(s)} throughput (Simulated)`;r=(()=>150===a&&1638.4===s)()?l.runtimeSlow4g:l.runtimeCustom;break}default:r=t=n=l.runtimeUnknown}const s="devtools"!==e.channel&&e.screenEmulation.disabled,c="devtools"===e.channel?"mobile"===e.formFactor:e.screenEmulation.mobile;let d=l.runtimeMobileEmulation;s?d=l.runtimeNoEmulation:c||(d=l.runtimeDesktopEmulation);return{deviceEmulation:d,screenEmulation:s?void 0:`${e.screenEmulation.width}x${e.screenEmulation.height}, DPR ${e.screenEmulation.deviceScaleFactor}`,cpuThrottling:t,networkThrottling:n,summary:r}}static showAsPassed(e){switch(e.scoreDisplayMode){case"manual":case"notApplicable":return!0;case"error":case"informative":return!1;case"numeric":case"binary":default:return Number(e.score)>=s.PASS.minScore}}static calculateRating(e,t){if("manual"===t||"notApplicable"===t)return s.PASS.label;if("error"===t)return s.ERROR.label;if(null===e)return s.FAIL.label;let n=s.FAIL.label;return e>=s.PASS.minScore?n=s.PASS.label:e>=s.AVERAGE.minScore&&(n=s.AVERAGE.label),n}static calculateCategoryFraction(e){let t=0,n=0,r=0,o=0;for(const i of e.auditRefs){const e=c.showAsPassed(i.result);"hidden"!==i.group&&"manual"!==i.result.scoreDisplayMode&&"notApplicable"!==i.result.scoreDisplayMode&&("informative"!==i.result.scoreDisplayMode?(++t,o+=i.weight,e&&n++):e||++r)}return{numPassed:n,numPassableAudits:t,numInformative:r,totalWeight:o}}static isPluginCategory(e){return e.startsWith("lighthouse-plugin-")}static shouldDisplayAsFraction(e){return"timespan"===e||"snapshot"===e}}const d={varianceDisclaimer:"Values are estimated and may vary. The [performance score is calculated](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/) directly from these metrics.",calculatorLink:"See calculator.",showRelevantAudits:"Show audits relevant to:",opportunityResourceColumnLabel:"Opportunity",opportunitySavingsColumnLabel:"Estimated Savings",errorMissingAuditInfo:"Report error: no audit information",errorLabel:"Error!",warningHeader:"Warnings: ",warningAuditsGroupTitle:"Passed audits but with warnings",passedAuditsGroupTitle:"Passed audits",notApplicableAuditsGroupTitle:"Not applicable",manualAuditsGroupTitle:"Additional items to manually check",toplevelWarningsMessage:"There were issues affecting this run of Lighthouse:",crcInitialNavigation:"Initial Navigation",crcLongestDurationLabel:"Maximum critical path latency:",snippetExpandButtonLabel:"Expand snippet",snippetCollapseButtonLabel:"Collapse snippet",lsPerformanceCategoryDescription:"[Lighthouse](https://developers.google.com/web/tools/lighthouse/) analysis of the current page on an emulated mobile network. Values are estimated and may vary.",labDataTitle:"Lab Data",thirdPartyResourcesLabel:"Show 3rd-party resources",viewTreemapLabel:"View Treemap",viewTraceLabel:"View Trace",viewOriginalTraceLabel:"View Original Trace",dropdownPrintSummary:"Print Summary",dropdownPrintExpanded:"Print Expanded",dropdownCopyJSON:"Copy JSON",dropdownSaveHTML:"Save as HTML",dropdownSaveJSON:"Save as JSON",dropdownViewer:"Open in Viewer",dropdownSaveGist:"Save as Gist",dropdownDarkTheme:"Toggle Dark Theme",runtimeSettingsDevice:"Device",runtimeSettingsNetworkThrottling:"Network throttling",runtimeSettingsCPUThrottling:"CPU throttling",runtimeSettingsUANetwork:"User agent (network)",runtimeSettingsBenchmark:"Unthrottled CPU/Memory Power",runtimeSettingsAxeVersion:"Axe version",runtimeSettingsScreenEmulation:"Screen emulation",footerIssue:"File an issue",runtimeNoEmulation:"No emulation",runtimeMobileEmulation:"Emulated Moto G4",runtimeDesktopEmulation:"Emulated Desktop",runtimeUnknown:"Unknown",runtimeSingleLoad:"Single page load",runtimeAnalysisWindow:"Initial page load",runtimeSingleLoadTooltip:"This data is taken from a single page load, as opposed to field data summarizing many sessions.",throttlingProvided:"Provided by environment",show:"Show",hide:"Hide",expandView:"Expand view",collapseView:"Collapse view",runtimeSlow4g:"Slow 4G throttling",runtimeCustom:"Custom throttling"};
44
+ */const l="data:image/jpeg;base64,",s=r.RATINGS;class c{static prepareReportResult(e){const t=JSON.parse(JSON.stringify(e));t.configSettings.locale||(t.configSettings.locale="en"),t.configSettings.formFactor||(t.configSettings.formFactor=t.configSettings.emulatedFormFactor),t.finalDisplayedUrl=r.getFinalDisplayedUrl(t),t.mainDocumentUrl=r.getMainDocumentUrl(t);for(const n of Object.values(t.audits))if("not_applicable"!==n.scoreDisplayMode&&"not-applicable"!==n.scoreDisplayMode||(n.scoreDisplayMode="notApplicable"),n.details){if(void 0!==n.details.type&&"diagnostic"!==n.details.type||(n.details.type="debugdata"),"filmstrip"===n.details.type)for(const e of n.details.items)e.data.startsWith(l)||(e.data=l+e.data);if("table"===n.details.type)for(const e of n.details.headings){const{itemType:t,text:n}=e;void 0!==t&&(e.valueType=t,delete e.itemType),void 0!==n&&(e.label=n,delete e.text);const r=e.subItemsHeading?.itemType;e.subItemsHeading&&void 0!==r&&(e.subItemsHeading.valueType=r,delete e.subItemsHeading.itemType)}c.classifyEntities(e.entities,n)}if("object"!=typeof t.categories)throw new Error("No categories provided.");const n=new Map,[o]=t.lighthouseVersion.split(".").map(Number),i=t.categories.performance;if(o<9&&i){t.categoryGroups||(t.categoryGroups={}),t.categoryGroups.hidden={title:""};for(const e of i.auditRefs)e.group?["load-opportunities","diagnostics"].includes(e.group)&&delete e.group:e.group="hidden"}for(const e of Object.values(t.categories))e.auditRefs.forEach((e=>{e.relevantAudits&&e.relevantAudits.forEach((t=>{const r=n.get(t)||[];r.push(e),n.set(t,r)}))})),e.auditRefs.forEach((e=>{const r=t.audits[e.id];e.result=r,n.has(e.id)&&(e.relevantMetrics=n.get(e.id)),t.stackPacks&&t.stackPacks.forEach((t=>{t.descriptions[e.id]&&(e.stackPacks=e.stackPacks||[],e.stackPacks.push({title:t.title,iconDataURL:t.iconDataURL,description:t.descriptions[e.id]}))}))}));if(t.environment||(t.environment={benchmarkIndex:0}),t.configSettings.screenEmulation||(t.configSettings.screenEmulation={}),t.i18n||(t.i18n={}),t.audits["full-page-screenshot"]){const e=t.audits["full-page-screenshot"].details;t.fullPageScreenshot=e?{screenshot:e.screenshot,nodes:e.nodes}:null,delete t.audits["full-page-screenshot"]}return t}static getUrlLocatorFn(e){const t=e.find((e=>"url"===e.valueType))?.key;if(t&&"string"==typeof t)return e=>{const n=e[t];if("string"==typeof n)return n};const n=e.find((e=>"source-location"===e.valueType))?.key;return n?e=>{const t=e[n];if("object"==typeof t&&"source-location"===t.type)return t.url}:void 0}static classifyEntities(e,t){if(!e)return;if("opportunity"!==t.details?.type&&"table"!==t.details?.type)return;const{items:n,headings:o}=t.details;if(!n.length||n.some((e=>e.entity)))return;const i=c.getUrlLocatorFn(o);if(i)for(const t of n){const n=i(t);if(!n)continue;let o;try{o=r.parseURL(n).origin}catch{}if(!o)continue;const a=e.entityIndexByOrigin[o];if(void 0===a)return;const l=e.list[a];t.entity=l.name}}static getEmulationDescriptions(e){let t,n,r;const o=e.throttling,i=a.i18n,l=a.strings;switch(e.throttlingMethod){case"provided":r=n=t=l.throttlingProvided;break;case"devtools":{const{cpuSlowdownMultiplier:e,requestLatencyMs:a}=o;t=`${i.formatNumber(e)}x slowdown (DevTools)`,n=`${i.formatMilliseconds(a)} HTTP RTT, ${i.formatKbps(o.downloadThroughputKbps)} down, ${i.formatKbps(o.uploadThroughputKbps)} up (DevTools)`;r=(()=>562.5===a&&o.downloadThroughputKbps===1638.4*.9&&675===o.uploadThroughputKbps)()?l.runtimeSlow4g:l.runtimeCustom;break}case"simulate":{const{cpuSlowdownMultiplier:e,rttMs:a,throughputKbps:s}=o;t=`${i.formatNumber(e)}x slowdown (Simulated)`,n=`${i.formatMilliseconds(a)} TCP RTT, ${i.formatKbps(s)} throughput (Simulated)`;r=(()=>150===a&&1638.4===s)()?l.runtimeSlow4g:l.runtimeCustom;break}default:r=t=n=l.runtimeUnknown}const s="devtools"!==e.channel&&e.screenEmulation.disabled,c="devtools"===e.channel?"mobile"===e.formFactor:e.screenEmulation.mobile;let d=l.runtimeMobileEmulation;s?d=l.runtimeNoEmulation:c||(d=l.runtimeDesktopEmulation);return{deviceEmulation:d,screenEmulation:s?void 0:`${e.screenEmulation.width}x${e.screenEmulation.height}, DPR ${e.screenEmulation.deviceScaleFactor}`,cpuThrottling:t,networkThrottling:n,summary:r}}static showAsPassed(e){switch(e.scoreDisplayMode){case"manual":case"notApplicable":return!0;case"error":case"informative":return!1;case"numeric":case"binary":default:return Number(e.score)>=s.PASS.minScore}}static calculateRating(e,t){if("manual"===t||"notApplicable"===t)return s.PASS.label;if("error"===t)return s.ERROR.label;if(null===e)return s.FAIL.label;let n=s.FAIL.label;return e>=s.PASS.minScore?n=s.PASS.label:e>=s.AVERAGE.minScore&&(n=s.AVERAGE.label),n}static calculateCategoryFraction(e){let t=0,n=0,r=0,o=0;for(const i of e.auditRefs){const e=c.showAsPassed(i.result);"hidden"!==i.group&&"manual"!==i.result.scoreDisplayMode&&"notApplicable"!==i.result.scoreDisplayMode&&("informative"!==i.result.scoreDisplayMode?(++t,o+=i.weight,e&&n++):e||++r)}return{numPassed:n,numPassableAudits:t,numInformative:r,totalWeight:o}}static isPluginCategory(e){return e.startsWith("lighthouse-plugin-")}static shouldDisplayAsFraction(e){return"timespan"===e||"snapshot"===e}}const d={varianceDisclaimer:"Values are estimated and may vary. The [performance score is calculated](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/) directly from these metrics.",calculatorLink:"See calculator.",showRelevantAudits:"Show audits relevant to:",opportunityResourceColumnLabel:"Opportunity",opportunitySavingsColumnLabel:"Estimated Savings",errorMissingAuditInfo:"Report error: no audit information",errorLabel:"Error!",warningHeader:"Warnings: ",warningAuditsGroupTitle:"Passed audits but with warnings",passedAuditsGroupTitle:"Passed audits",notApplicableAuditsGroupTitle:"Not applicable",manualAuditsGroupTitle:"Additional items to manually check",toplevelWarningsMessage:"There were issues affecting this run of Lighthouse:",crcInitialNavigation:"Initial Navigation",crcLongestDurationLabel:"Maximum critical path latency:",snippetExpandButtonLabel:"Expand snippet",snippetCollapseButtonLabel:"Collapse snippet",lsPerformanceCategoryDescription:"[Lighthouse](https://developers.google.com/web/tools/lighthouse/) analysis of the current page on an emulated mobile network. Values are estimated and may vary.",labDataTitle:"Lab Data",thirdPartyResourcesLabel:"Show 3rd-party resources",viewTreemapLabel:"View Treemap",viewTraceLabel:"View Trace",viewOriginalTraceLabel:"View Original Trace",dropdownPrintSummary:"Print Summary",dropdownPrintExpanded:"Print Expanded",dropdownCopyJSON:"Copy JSON",dropdownSaveHTML:"Save as HTML",dropdownSaveJSON:"Save as JSON",dropdownViewer:"Open in Viewer",dropdownSaveGist:"Save as Gist",dropdownDarkTheme:"Toggle Dark Theme",runtimeSettingsDevice:"Device",runtimeSettingsNetworkThrottling:"Network throttling",runtimeSettingsCPUThrottling:"CPU throttling",runtimeSettingsUANetwork:"User agent (network)",runtimeSettingsBenchmark:"Unthrottled CPU/Memory Power",runtimeSettingsAxeVersion:"Axe version",runtimeSettingsScreenEmulation:"Screen emulation",footerIssue:"File an issue",runtimeNoEmulation:"No emulation",runtimeMobileEmulation:"Emulated Moto G4",runtimeDesktopEmulation:"Emulated Desktop",runtimeUnknown:"Unknown",runtimeSingleLoad:"Single page load",runtimeAnalysisWindow:"Initial page load",runtimeSingleLoadTooltip:"This data is taken from a single page load, as opposed to field data summarizing many sessions.",throttlingProvided:"Provided by environment",show:"Show",hide:"Hide",expandView:"Expand view",collapseView:"Collapse view",runtimeSlow4g:"Slow 4G throttling",runtimeCustom:"Custom throttling"};
45
45
  /**
46
46
  * @license
47
47
  * Copyright 2017 The Lighthouse Authors. All Rights Reserved.
@@ -94,7 +94,7 @@ class o{constructor(e,t){this._document=e,this._lighthouseChannel="unknown",this
94
94
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95
95
  * See the License for the specific language governing permissions and
96
96
  * limitations under the License.
97
- */const f=["http://","https://","data:"];class v{constructor(e,t={}){this._dom=e,this._fullPageScreenshot=t.fullPageScreenshot}render(e){switch(e.type){case"filmstrip":return this._renderFilmstrip(e);case"list":return this._renderList(e);case"table":case"opportunity":return this._renderTable(e);case"criticalrequestchain":return p.render(this._dom,e,this);case"screenshot":case"debugdata":case"treemap-data":return null;default:return this._renderUnknown(e.type,e)}}_renderBytes(e){const t=a.i18n.formatBytesToKiB(e.value,e.granularity||.1),n=this._renderText(t);return n.title=a.i18n.formatBytes(e.value),n}_renderMilliseconds(e){let t;return t="duration"===e.displayUnit?a.i18n.formatDuration(e.value):a.i18n.formatMilliseconds(e.value,e.granularity||10),this._renderText(t)}renderTextURL(e){const t=e;let n,o,i;try{const e=r.parseURL(t);n="/"===e.file?e.origin:e.file,o="/"===e.file||""===e.hostname?"":`(${e.hostname})`,i=t}catch(e){n=t}const a=this._dom.createElement("div","lh-text__url");if(a.append(this._renderLink({text:n,url:t})),o){const e=this._renderText(o);e.classList.add("lh-text__url-host"),a.append(e)}return i&&(a.title=t,a.dataset.url=t),a}_renderLink(e){const t=this._dom.createElement("a");if(this._dom.safelySetHref(t,e.url),!t.href){const t=this._renderText(e.text);return t.classList.add("lh-link"),t}return t.rel="noopener",t.target="_blank",t.textContent=e.text,t.classList.add("lh-link"),t}_renderText(e){const t=this._dom.createElement("div","lh-text");return t.textContent=e,t}_renderNumeric(e){const t=a.i18n.formatNumber(e.value,e.granularity||.1),n=this._dom.createElement("div","lh-numeric");return n.textContent=t,n}_renderThumbnail(e){const t=this._dom.createElement("img","lh-thumbnail"),n=e;return t.src=n,t.title=n,t.alt="",t}_renderUnknown(e,t){console.error(`Unknown details type: ${e}`,t);const n=this._dom.createElement("details","lh-unknown");return this._dom.createChildOf(n,"summary").textContent=`We don't know how to render audit details of type \`${e}\`. The Lighthouse version that collected this data is likely newer than the Lighthouse version of the report renderer. Expand for the raw JSON.`,this._dom.createChildOf(n,"pre").textContent=JSON.stringify(t,null,2),n}_renderTableValue(e,t){if(null==e)return null;if("object"==typeof e)switch(e.type){case"code":return this._renderCode(e.value);case"link":return this._renderLink(e);case"node":return this.renderNode(e);case"numeric":return this._renderNumeric(e);case"source-location":return this.renderSourceLocation(e);case"url":return this.renderTextURL(e.value);default:return this._renderUnknown(e.type,e)}switch(t.valueType){case"bytes":{const n=Number(e);return this._renderBytes({value:n,granularity:t.granularity})}case"code":{const t=String(e);return this._renderCode(t)}case"ms":{const n={value:Number(e),granularity:t.granularity,displayUnit:t.displayUnit};return this._renderMilliseconds(n)}case"numeric":{const n=Number(e);return this._renderNumeric({value:n,granularity:t.granularity})}case"text":{const t=String(e);return this._renderText(t)}case"thumbnail":{const t=String(e);return this._renderThumbnail(t)}case"timespanMs":{const t=Number(e);return this._renderMilliseconds({value:t})}case"url":{const t=String(e);return f.some((e=>t.startsWith(e)))?this.renderTextURL(t):this._renderCode(t)}default:return this._renderUnknown(t.valueType,e)}}_getDerivedSubItemsHeading(e){return e.subItemsHeading?{key:e.subItemsHeading.key||"",valueType:e.subItemsHeading.valueType||e.valueType,granularity:e.subItemsHeading.granularity||e.granularity,displayUnit:e.subItemsHeading.displayUnit||e.displayUnit,label:""}:null}_renderTableRow(e,t){const n=this._dom.createElement("tr");for(const r of t){if(!r||!r.key){this._dom.createChildOf(n,"td","lh-table-column--empty");continue}const t=e[r.key];let o;if(null!=t&&(o=this._renderTableValue(t,r)),o){const e=`lh-table-column--${r.valueType}`;this._dom.createChildOf(n,"td",e).append(o)}else this._dom.createChildOf(n,"td","lh-table-column--empty")}return n}_renderTableRowsFromItem(e,t){const n=this._dom.createFragment();if(n.append(this._renderTableRow(e,t)),!e.subItems)return n;const r=t.map(this._getDerivedSubItemsHeading);if(!r.some(Boolean))return n;for(const t of e.subItems.items){const e=this._renderTableRow(t,r);e.classList.add("lh-sub-item-row"),n.append(e)}return n}_renderTable(e){if(!e.items.length)return this._dom.createElement("span");const t=this._dom.createElement("table","lh-table"),n=this._dom.createChildOf(t,"thead"),r=this._dom.createChildOf(n,"tr");for(const t of e.headings){const e=`lh-table-column--${t.valueType||"text"}`,n=this._dom.createElement("div","lh-text");n.textContent=t.label,this._dom.createChildOf(r,"th",e).append(n)}const o=this._dom.createChildOf(t,"tbody");let i=!0;for(const t of e.items){const n=this._renderTableRowsFromItem(t,e.headings);for(const e of this._dom.findAll("tr",n))e.classList.add(i?"lh-row--even":"lh-row--odd");i=!i,o.append(n)}return t}_renderList(e){const t=this._dom.createElement("div","lh-list");return e.items.forEach((e=>{const n=this.render(e);n&&t.append(n)})),t}renderNode(e){const t=this._dom.createElement("span","lh-node");if(e.nodeLabel){const n=this._dom.createElement("div");n.textContent=e.nodeLabel,t.append(n)}if(e.snippet){const n=this._dom.createElement("div");n.classList.add("lh-node__snippet"),n.textContent=e.snippet,t.append(n)}if(e.selector&&(t.title=e.selector),e.path&&t.setAttribute("data-path",e.path),e.selector&&t.setAttribute("data-selector",e.selector),e.snippet&&t.setAttribute("data-snippet",e.snippet),!this._fullPageScreenshot)return t;const n=e.lhId&&this._fullPageScreenshot.nodes[e.lhId];if(!n||0===n.width||0===n.height)return t;const r=m.render(this._dom,this._fullPageScreenshot.screenshot,n,{width:147,height:100});return r&&t.prepend(r),t}renderSourceLocation(e){if(!e.url)return null;const t=`${e.url}:${e.line+1}:${e.column}`;let n,r;if(e.original){n=`${e.original.file||"<unmapped>"}:${e.original.line+1}:${e.original.column}`}if("network"===e.urlProvider&&n)r=this._renderLink({url:e.url,text:n}),r.title=`maps to generated location ${t}`;else if("network"!==e.urlProvider||n)if("comment"===e.urlProvider&&n)r=this._renderText(`${n} (from source map)`),r.title=`${t} (from sourceURL)`;else{if("comment"!==e.urlProvider||n)return null;r=this._renderText(`${t} (from sourceURL)`)}else r=this.renderTextURL(e.url),this._dom.find(".lh-link",r).textContent+=`:${e.line+1}:${e.column}`;return r.classList.add("lh-source-location"),r.setAttribute("data-source-url",e.url),r.setAttribute("data-source-line",String(e.line)),r.setAttribute("data-source-column",String(e.column)),r}_renderFilmstrip(e){const t=this._dom.createElement("div","lh-filmstrip");for(const n of e.items){const e=this._dom.createChildOf(t,"div","lh-filmstrip__frame"),r=this._dom.createChildOf(e,"img","lh-filmstrip__thumbnail");r.src=n.data,r.alt="Screenshot"}return t}_renderCode(e){const t=this._dom.createElement("pre","lh-code");return t.textContent=e,t}}
97
+ */const f=["http://","https://","data:"];class v{constructor(e,t={}){this._dom=e,this._fullPageScreenshot=t.fullPageScreenshot}render(e){switch(e.type){case"filmstrip":return this._renderFilmstrip(e);case"list":return this._renderList(e);case"table":case"opportunity":return this._renderTable(e);case"criticalrequestchain":return p.render(this._dom,e,this);case"screenshot":case"debugdata":case"treemap-data":return null;default:return this._renderUnknown(e.type,e)}}_renderBytes(e){const t=a.i18n.formatBytesToKiB(e.value,e.granularity||.1),n=this._renderText(t);return n.title=a.i18n.formatBytes(e.value),n}_renderMilliseconds(e){let t;return t="duration"===e.displayUnit?a.i18n.formatDuration(e.value):a.i18n.formatMilliseconds(e.value,e.granularity||10),this._renderText(t)}renderTextURL(e){const t=e;let n,o,i;try{const e=r.parseURL(t);n="/"===e.file?e.origin:e.file,o="/"===e.file||""===e.hostname?"":`(${e.hostname})`,i=t}catch(e){n=t}const a=this._dom.createElement("div","lh-text__url");if(a.append(this._renderLink({text:n,url:t})),o){const e=this._renderText(o);e.classList.add("lh-text__url-host"),a.append(e)}return i&&(a.title=t,a.dataset.url=t),a}_renderLink(e){const t=this._dom.createElement("a");if(this._dom.safelySetHref(t,e.url),!t.href){const t=this._renderText(e.text);return t.classList.add("lh-link"),t}return t.rel="noopener",t.target="_blank",t.textContent=e.text,t.classList.add("lh-link"),t}_renderText(e){const t=this._dom.createElement("div","lh-text");return t.textContent=e,t}_renderNumeric(e){const t=a.i18n.formatNumber(e.value,e.granularity||.1),n=this._dom.createElement("div","lh-numeric");return n.textContent=t,n}_renderThumbnail(e){const t=this._dom.createElement("img","lh-thumbnail"),n=e;return t.src=n,t.title=n,t.alt="",t}_renderUnknown(e,t){console.error(`Unknown details type: ${e}`,t);const n=this._dom.createElement("details","lh-unknown");return this._dom.createChildOf(n,"summary").textContent=`We don't know how to render audit details of type \`${e}\`. The Lighthouse version that collected this data is likely newer than the Lighthouse version of the report renderer. Expand for the raw JSON.`,this._dom.createChildOf(n,"pre").textContent=JSON.stringify(t,null,2),n}_renderTableValue(e,t){if(null==e)return null;if("object"==typeof e)switch(e.type){case"code":return this._renderCode(e.value);case"link":return this._renderLink(e);case"node":return this.renderNode(e);case"numeric":return this._renderNumeric(e);case"source-location":return this.renderSourceLocation(e);case"url":return this.renderTextURL(e.value);default:return this._renderUnknown(e.type,e)}switch(t.valueType){case"bytes":{const n=Number(e);return this._renderBytes({value:n,granularity:t.granularity})}case"code":{const t=String(e);return this._renderCode(t)}case"ms":{const n={value:Number(e),granularity:t.granularity,displayUnit:t.displayUnit};return this._renderMilliseconds(n)}case"numeric":{const n=Number(e);return this._renderNumeric({value:n,granularity:t.granularity})}case"text":{const t=String(e);return this._renderText(t)}case"thumbnail":{const t=String(e);return this._renderThumbnail(t)}case"timespanMs":{const t=Number(e);return this._renderMilliseconds({value:t})}case"url":{const t=String(e);return f.some((e=>t.startsWith(e)))?this.renderTextURL(t):this._renderCode(t)}default:return this._renderUnknown(t.valueType,e)}}_getDerivedSubItemsHeading(e){return e.subItemsHeading?{key:e.subItemsHeading.key||"",valueType:e.subItemsHeading.valueType||e.valueType,granularity:e.subItemsHeading.granularity||e.granularity,displayUnit:e.subItemsHeading.displayUnit||e.displayUnit,label:""}:null}_renderTableRow(e,t){const n=this._dom.createElement("tr");for(const r of t){if(!r||!r.key){this._dom.createChildOf(n,"td","lh-table-column--empty");continue}const t=e[r.key];let o;if(null!=t&&(o=this._renderTableValue(t,r)),o){const e=`lh-table-column--${r.valueType}`;this._dom.createChildOf(n,"td",e).append(o)}else this._dom.createChildOf(n,"td","lh-table-column--empty")}return n}_renderTableRowsFromItem(e,t){const n=this._dom.createFragment();if(n.append(this._renderTableRow(e,t)),!e.subItems)return n;const r=t.map(this._getDerivedSubItemsHeading);if(!r.some(Boolean))return n;for(const t of e.subItems.items){const e=this._renderTableRow(t,r);e.classList.add("lh-sub-item-row"),n.append(e)}return n}_renderTable(e){if(!e.items.length)return this._dom.createElement("span");const t=this._dom.createElement("table","lh-table"),n=this._dom.createChildOf(t,"thead"),r=this._dom.createChildOf(n,"tr");for(const t of e.headings){const e=`lh-table-column--${t.valueType||"text"}`,n=this._dom.createElement("div","lh-text");n.textContent=t.label,this._dom.createChildOf(r,"th",e).append(n)}const o=this._dom.createChildOf(t,"tbody");let i=!0;for(const t of e.items){const n=this._renderTableRowsFromItem(t,e.headings);let r;"object"==typeof t.entity&&"link"===t.entity.type?r=t.entity.text:"string"==typeof t.entity&&(r=t.entity);for(const e of this._dom.findAll("tr",n))e.classList.add(i?"lh-row--even":"lh-row--odd"),r&&!e.classList.contains("lh-sub-item-row")&&(e.dataset.entity=r);i=!i,o.append(n)}return t}_renderList(e){const t=this._dom.createElement("div","lh-list");return e.items.forEach((e=>{const n=this.render(e);n&&t.append(n)})),t}renderNode(e){const t=this._dom.createElement("span","lh-node");if(e.nodeLabel){const n=this._dom.createElement("div");n.textContent=e.nodeLabel,t.append(n)}if(e.snippet){const n=this._dom.createElement("div");n.classList.add("lh-node__snippet"),n.textContent=e.snippet,t.append(n)}if(e.selector&&(t.title=e.selector),e.path&&t.setAttribute("data-path",e.path),e.selector&&t.setAttribute("data-selector",e.selector),e.snippet&&t.setAttribute("data-snippet",e.snippet),!this._fullPageScreenshot)return t;const n=e.lhId&&this._fullPageScreenshot.nodes[e.lhId];if(!n||0===n.width||0===n.height)return t;const r=m.render(this._dom,this._fullPageScreenshot.screenshot,n,{width:147,height:100});return r&&t.prepend(r),t}renderSourceLocation(e){if(!e.url)return null;const t=`${e.url}:${e.line+1}:${e.column}`;let n,r;if(e.original){n=`${e.original.file||"<unmapped>"}:${e.original.line+1}:${e.original.column}`}if("network"===e.urlProvider&&n)r=this._renderLink({url:e.url,text:n}),r.title=`maps to generated location ${t}`;else if("network"!==e.urlProvider||n)if("comment"===e.urlProvider&&n)r=this._renderText(`${n} (from source map)`),r.title=`${t} (from sourceURL)`;else{if("comment"!==e.urlProvider||n)return null;r=this._renderText(`${t} (from sourceURL)`)}else r=this.renderTextURL(e.url),this._dom.find(".lh-link",r).textContent+=`:${e.line+1}:${e.column}`;return r.classList.add("lh-source-location"),r.setAttribute("data-source-url",e.url),r.setAttribute("data-source-line",String(e.line)),r.setAttribute("data-source-column",String(e.column)),r}_renderFilmstrip(e){const t=this._dom.createElement("div","lh-filmstrip");for(const n of e.items){const e=this._dom.createChildOf(t,"div","lh-filmstrip__frame"),r=this._dom.createChildOf(e,"img","lh-filmstrip__thumbnail");r.src=n.data,r.alt="Screenshot"}return t}_renderCode(e){const t=this._dom.createElement("pre","lh-code");return t.textContent=e,t}}
98
98
  /**
99
99
  * @license Copyright 2020 The Lighthouse Authors. All Rights Reserved.
100
100
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -182,12 +182,12 @@ class o{constructor(e,t){this._document=e,this._lighthouseChannel="unknown",this
182
182
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
183
183
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
184
184
  */
185
- class D{constructor(e){this._dom=e,this._toggleEl,this._menuEl,this.onDocumentKeyDown=this.onDocumentKeyDown.bind(this),this.onToggleClick=this.onToggleClick.bind(this),this.onToggleKeydown=this.onToggleKeydown.bind(this),this.onMenuFocusOut=this.onMenuFocusOut.bind(this),this.onMenuKeydown=this.onMenuKeydown.bind(this),this._getNextMenuItem=this._getNextMenuItem.bind(this),this._getNextSelectableNode=this._getNextSelectableNode.bind(this),this._getPreviousMenuItem=this._getPreviousMenuItem.bind(this)}setup(e){this._toggleEl=this._dom.find(".lh-topbar button.lh-tools__button",this._dom.rootEl),this._toggleEl.addEventListener("click",this.onToggleClick),this._toggleEl.addEventListener("keydown",this.onToggleKeydown),this._menuEl=this._dom.find(".lh-topbar div.lh-tools__dropdown",this._dom.rootEl),this._menuEl.addEventListener("keydown",this.onMenuKeydown),this._menuEl.addEventListener("click",e)}close(){this._toggleEl.classList.remove("lh-active"),this._toggleEl.setAttribute("aria-expanded","false"),this._menuEl.contains(this._dom.document().activeElement)&&this._toggleEl.focus(),this._menuEl.removeEventListener("focusout",this.onMenuFocusOut),this._dom.document().removeEventListener("keydown",this.onDocumentKeyDown)}open(e){this._toggleEl.classList.contains("lh-active")?e.focus():this._menuEl.addEventListener("transitionend",(()=>{e.focus()}),{once:!0}),this._toggleEl.classList.add("lh-active"),this._toggleEl.setAttribute("aria-expanded","true"),this._menuEl.addEventListener("focusout",this.onMenuFocusOut),this._dom.document().addEventListener("keydown",this.onDocumentKeyDown)}onToggleClick(e){e.preventDefault(),e.stopImmediatePropagation(),this._toggleEl.classList.contains("lh-active")?this.close():this.open(this._getNextMenuItem())}onToggleKeydown(e){switch(e.code){case"ArrowUp":e.preventDefault(),this.open(this._getPreviousMenuItem());break;case"ArrowDown":case"Enter":case" ":e.preventDefault(),this.open(this._getNextMenuItem())}}onMenuKeydown(e){const t=e.target;switch(e.code){case"ArrowUp":e.preventDefault(),this._getPreviousMenuItem(t).focus();break;case"ArrowDown":e.preventDefault(),this._getNextMenuItem(t).focus();break;case"Home":e.preventDefault(),this._getNextMenuItem().focus();break;case"End":e.preventDefault(),this._getPreviousMenuItem().focus()}}onDocumentKeyDown(e){27===e.keyCode&&this.close()}onMenuFocusOut(e){const t=e.relatedTarget;this._menuEl.contains(t)||this.close()}_getNextSelectableNode(e,t){const n=e.filter((e=>e instanceof HTMLElement&&(!e.hasAttribute("disabled")&&"none"!==window.getComputedStyle(e).display)));let r=t?n.indexOf(t)+1:0;return r>=n.length&&(r=0),n[r]}_getNextMenuItem(e){const t=Array.from(this._menuEl.childNodes);return this._getNextSelectableNode(t,e)}_getPreviousMenuItem(e){const t=Array.from(this._menuEl.childNodes).reverse();return this._getNextSelectableNode(t,e)}}
185
+ class T{constructor(e){this._dom=e,this._toggleEl,this._menuEl,this.onDocumentKeyDown=this.onDocumentKeyDown.bind(this),this.onToggleClick=this.onToggleClick.bind(this),this.onToggleKeydown=this.onToggleKeydown.bind(this),this.onMenuFocusOut=this.onMenuFocusOut.bind(this),this.onMenuKeydown=this.onMenuKeydown.bind(this),this._getNextMenuItem=this._getNextMenuItem.bind(this),this._getNextSelectableNode=this._getNextSelectableNode.bind(this),this._getPreviousMenuItem=this._getPreviousMenuItem.bind(this)}setup(e){this._toggleEl=this._dom.find(".lh-topbar button.lh-tools__button",this._dom.rootEl),this._toggleEl.addEventListener("click",this.onToggleClick),this._toggleEl.addEventListener("keydown",this.onToggleKeydown),this._menuEl=this._dom.find(".lh-topbar div.lh-tools__dropdown",this._dom.rootEl),this._menuEl.addEventListener("keydown",this.onMenuKeydown),this._menuEl.addEventListener("click",e)}close(){this._toggleEl.classList.remove("lh-active"),this._toggleEl.setAttribute("aria-expanded","false"),this._menuEl.contains(this._dom.document().activeElement)&&this._toggleEl.focus(),this._menuEl.removeEventListener("focusout",this.onMenuFocusOut),this._dom.document().removeEventListener("keydown",this.onDocumentKeyDown)}open(e){this._toggleEl.classList.contains("lh-active")?e.focus():this._menuEl.addEventListener("transitionend",(()=>{e.focus()}),{once:!0}),this._toggleEl.classList.add("lh-active"),this._toggleEl.setAttribute("aria-expanded","true"),this._menuEl.addEventListener("focusout",this.onMenuFocusOut),this._dom.document().addEventListener("keydown",this.onDocumentKeyDown)}onToggleClick(e){e.preventDefault(),e.stopImmediatePropagation(),this._toggleEl.classList.contains("lh-active")?this.close():this.open(this._getNextMenuItem())}onToggleKeydown(e){switch(e.code){case"ArrowUp":e.preventDefault(),this.open(this._getPreviousMenuItem());break;case"ArrowDown":case"Enter":case" ":e.preventDefault(),this.open(this._getNextMenuItem())}}onMenuKeydown(e){const t=e.target;switch(e.code){case"ArrowUp":e.preventDefault(),this._getPreviousMenuItem(t).focus();break;case"ArrowDown":e.preventDefault(),this._getNextMenuItem(t).focus();break;case"Home":e.preventDefault(),this._getNextMenuItem().focus();break;case"End":e.preventDefault(),this._getPreviousMenuItem().focus()}}onDocumentKeyDown(e){27===e.keyCode&&this.close()}onMenuFocusOut(e){const t=e.relatedTarget;this._menuEl.contains(t)||this.close()}_getNextSelectableNode(e,t){const n=e.filter((e=>e instanceof HTMLElement&&(!e.hasAttribute("disabled")&&"none"!==window.getComputedStyle(e).display)));let r=t?n.indexOf(t)+1:0;return r>=n.length&&(r=0),n[r]}_getNextMenuItem(e){const t=Array.from(this._menuEl.childNodes);return this._getNextSelectableNode(t,e)}_getPreviousMenuItem(e){const t=Array.from(this._menuEl.childNodes).reverse();return this._getNextSelectableNode(t,e)}}
186
186
  /**
187
187
  * @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
188
188
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
189
189
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
190
- */class T{constructor(e,t){this.lhr,this._reportUIFeatures=e,this._dom=t,this._dropDownMenu=new D(this._dom),this._copyAttempt=!1,this.topbarEl,this.categoriesEl,this.stickyHeaderEl,this.highlightEl,this.onDropDownMenuClick=this.onDropDownMenuClick.bind(this),this.onKeyUp=this.onKeyUp.bind(this),this.onCopy=this.onCopy.bind(this),this.collapseAllDetails=this.collapseAllDetails.bind(this)}enable(e){this.lhr=e,this._dom.rootEl.addEventListener("keyup",this.onKeyUp),this._dom.document().addEventListener("copy",this.onCopy),this._dropDownMenu.setup(this.onDropDownMenuClick),this._setUpCollapseDetailsAfterPrinting();this._dom.find(".lh-topbar__logo",this._dom.rootEl).addEventListener("click",(()=>E(this._dom))),this._setupStickyHeader()}onDropDownMenuClick(e){e.preventDefault();const t=e.target;if(t&&t.hasAttribute("data-action")){switch(t.getAttribute("data-action")){case"copy":this.onCopyButtonClick();break;case"print-summary":this.collapseAllDetails(),this._print();break;case"print-expanded":this.expandAllDetails(),this._print();break;case"save-json":{const e=JSON.stringify(this.lhr,null,2);this._reportUIFeatures._saveFile(new Blob([e],{type:"application/json"}));break}case"save-html":{const t=this._reportUIFeatures.getReportHtml();try{this._reportUIFeatures._saveFile(new Blob([t],{type:"text/html"}))}catch(e){this._dom.fireEventOn("lh-log",this._dom.document(),{cmd:"error",msg:"Could not export as HTML. "+e.message})}break}case"open-viewer":this._dom.isDevTools()?async function(e){const t="viewer-"+L(e),n=C()+"/viewer/";await M({lhr:e},n,t)}(this.lhr):F(this.lhr);break;case"save-gist":this._reportUIFeatures.saveAsGist();break;case"toggle-dark":E(this._dom)}this._dropDownMenu.close()}}onCopy(e){this._copyAttempt&&e.clipboardData&&(e.preventDefault(),e.clipboardData.setData("text/plain",JSON.stringify(this.lhr,null,2)),this._dom.fireEventOn("lh-log",this._dom.document(),{cmd:"log",msg:"Report JSON copied to clipboard"})),this._copyAttempt=!1}onCopyButtonClick(){this._dom.fireEventOn("lh-analytics",this._dom.document(),{cmd:"send",fields:{hitType:"event",eventCategory:"report",eventAction:"copy"}});try{this._dom.document().queryCommandSupported("copy")&&(this._copyAttempt=!0,this._dom.document().execCommand("copy")||(this._copyAttempt=!1,this._dom.fireEventOn("lh-log",this._dom.document(),{cmd:"warn",msg:"Your browser does not support copy to clipboard."})))}catch(e){this._copyAttempt=!1,this._dom.fireEventOn("lh-log",this._dom.document(),{cmd:"log",msg:e.message})}}onKeyUp(e){(e.ctrlKey||e.metaKey)&&80===e.keyCode&&this._dropDownMenu.close()}expandAllDetails(){this._dom.findAll(".lh-categories details",this._dom.rootEl).map((e=>e.open=!0))}collapseAllDetails(){this._dom.findAll(".lh-categories details",this._dom.rootEl).map((e=>e.open=!1))}_print(){this._reportUIFeatures._opts.onPrintOverride?this._reportUIFeatures._opts.onPrintOverride(this._dom.rootEl):self.print()}resetUIState(){this._dropDownMenu.close()}_getScrollParent(e){const{overflowY:t}=window.getComputedStyle(e);return"visible"!==t&&"hidden"!==t?e:e.parentElement?this._getScrollParent(e.parentElement):document}_setUpCollapseDetailsAfterPrinting(){"onbeforeprint"in self?self.addEventListener("afterprint",this.collapseAllDetails):self.matchMedia("print").addListener((e=>{e.matches?this.expandAllDetails():this.collapseAllDetails()}))}_setupStickyHeader(){this.topbarEl=this._dom.find("div.lh-topbar",this._dom.rootEl),this.categoriesEl=this._dom.find("div.lh-categories",this._dom.rootEl),window.requestAnimationFrame((()=>window.requestAnimationFrame((()=>{try{this.stickyHeaderEl=this._dom.find("div.lh-sticky-header",this._dom.rootEl)}catch{return}this.highlightEl=this._dom.createChildOf(this.stickyHeaderEl,"div","lh-highlighter");const e=this._getScrollParent(this._dom.find(".lh-container",this._dom.rootEl));e.addEventListener("scroll",(()=>this._updateStickyHeader()));const t=e instanceof window.Document?document.documentElement:e;new window.ResizeObserver((()=>this._updateStickyHeader())).observe(t)}))))}_updateStickyHeader(){if(!this.stickyHeaderEl)return;const e=this.topbarEl.getBoundingClientRect().bottom>=this.categoriesEl.getBoundingClientRect().top,t=Array.from(this._dom.rootEl.querySelectorAll(".lh-category")).filter((e=>e.getBoundingClientRect().top-window.innerHeight/2<0)),n=t.length>0?t.length-1:0,r=this.stickyHeaderEl.querySelectorAll(".lh-gauge__wrapper, .lh-fraction__wrapper"),o=r[n],i=r[0].getBoundingClientRect().left,a=o.getBoundingClientRect().left-i;this.highlightEl.style.transform=`translate(${a}px)`,this.stickyHeaderEl.classList.toggle("lh-sticky-header--visible",e)}}
190
+ */class D{constructor(e,t){this.lhr,this._reportUIFeatures=e,this._dom=t,this._dropDownMenu=new T(this._dom),this._copyAttempt=!1,this.topbarEl,this.categoriesEl,this.stickyHeaderEl,this.highlightEl,this.onDropDownMenuClick=this.onDropDownMenuClick.bind(this),this.onKeyUp=this.onKeyUp.bind(this),this.onCopy=this.onCopy.bind(this),this.collapseAllDetails=this.collapseAllDetails.bind(this)}enable(e){this.lhr=e,this._dom.rootEl.addEventListener("keyup",this.onKeyUp),this._dom.document().addEventListener("copy",this.onCopy),this._dropDownMenu.setup(this.onDropDownMenuClick),this._setUpCollapseDetailsAfterPrinting();this._dom.find(".lh-topbar__logo",this._dom.rootEl).addEventListener("click",(()=>E(this._dom))),this._setupStickyHeader()}onDropDownMenuClick(e){e.preventDefault();const t=e.target;if(t&&t.hasAttribute("data-action")){switch(t.getAttribute("data-action")){case"copy":this.onCopyButtonClick();break;case"print-summary":this.collapseAllDetails(),this._print();break;case"print-expanded":this.expandAllDetails(),this._print();break;case"save-json":{const e=JSON.stringify(this.lhr,null,2);this._reportUIFeatures._saveFile(new Blob([e],{type:"application/json"}));break}case"save-html":{const t=this._reportUIFeatures.getReportHtml();try{this._reportUIFeatures._saveFile(new Blob([t],{type:"text/html"}))}catch(e){this._dom.fireEventOn("lh-log",this._dom.document(),{cmd:"error",msg:"Could not export as HTML. "+e.message})}break}case"open-viewer":this._dom.isDevTools()?async function(e){const t="viewer-"+L(e),n=C()+"/viewer/";await M({lhr:e},n,t)}(this.lhr):F(this.lhr);break;case"save-gist":this._reportUIFeatures.saveAsGist();break;case"toggle-dark":E(this._dom)}this._dropDownMenu.close()}}onCopy(e){this._copyAttempt&&e.clipboardData&&(e.preventDefault(),e.clipboardData.setData("text/plain",JSON.stringify(this.lhr,null,2)),this._dom.fireEventOn("lh-log",this._dom.document(),{cmd:"log",msg:"Report JSON copied to clipboard"})),this._copyAttempt=!1}onCopyButtonClick(){this._dom.fireEventOn("lh-analytics",this._dom.document(),{cmd:"send",fields:{hitType:"event",eventCategory:"report",eventAction:"copy"}});try{this._dom.document().queryCommandSupported("copy")&&(this._copyAttempt=!0,this._dom.document().execCommand("copy")||(this._copyAttempt=!1,this._dom.fireEventOn("lh-log",this._dom.document(),{cmd:"warn",msg:"Your browser does not support copy to clipboard."})))}catch(e){this._copyAttempt=!1,this._dom.fireEventOn("lh-log",this._dom.document(),{cmd:"log",msg:e.message})}}onKeyUp(e){(e.ctrlKey||e.metaKey)&&80===e.keyCode&&this._dropDownMenu.close()}expandAllDetails(){this._dom.findAll(".lh-categories details",this._dom.rootEl).map((e=>e.open=!0))}collapseAllDetails(){this._dom.findAll(".lh-categories details",this._dom.rootEl).map((e=>e.open=!1))}_print(){this._reportUIFeatures._opts.onPrintOverride?this._reportUIFeatures._opts.onPrintOverride(this._dom.rootEl):self.print()}resetUIState(){this._dropDownMenu.close()}_getScrollParent(e){const{overflowY:t}=window.getComputedStyle(e);return"visible"!==t&&"hidden"!==t?e:e.parentElement?this._getScrollParent(e.parentElement):document}_setUpCollapseDetailsAfterPrinting(){"onbeforeprint"in self?self.addEventListener("afterprint",this.collapseAllDetails):self.matchMedia("print").addListener((e=>{e.matches?this.expandAllDetails():this.collapseAllDetails()}))}_setupStickyHeader(){this.topbarEl=this._dom.find("div.lh-topbar",this._dom.rootEl),this.categoriesEl=this._dom.find("div.lh-categories",this._dom.rootEl),window.requestAnimationFrame((()=>window.requestAnimationFrame((()=>{try{this.stickyHeaderEl=this._dom.find("div.lh-sticky-header",this._dom.rootEl)}catch{return}this.highlightEl=this._dom.createChildOf(this.stickyHeaderEl,"div","lh-highlighter");const e=this._getScrollParent(this._dom.find(".lh-container",this._dom.rootEl));e.addEventListener("scroll",(()=>this._updateStickyHeader()));const t=e instanceof window.Document?document.documentElement:e;new window.ResizeObserver((()=>this._updateStickyHeader())).observe(t)}))))}_updateStickyHeader(){if(!this.stickyHeaderEl)return;const e=this.topbarEl.getBoundingClientRect().bottom>=this.categoriesEl.getBoundingClientRect().top,t=Array.from(this._dom.rootEl.querySelectorAll(".lh-category")).filter((e=>e.getBoundingClientRect().top-window.innerHeight/2<0)),n=t.length>0?t.length-1:0,r=this.stickyHeaderEl.querySelectorAll(".lh-gauge__wrapper, .lh-fraction__wrapper"),o=r[n],i=r[0].getBoundingClientRect().left,a=o.getBoundingClientRect().left-i;this.highlightEl.style.transform=`translate(${a}px)`,this.stickyHeaderEl.classList.toggle("lh-sticky-header--visible",e)}}
191
191
  /**
192
192
  * @license Copyright 2017 The Lighthouse Authors. All Rights Reserved.
193
193
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -208,7 +208,7 @@ class D{constructor(e){this._dom=e,this._toggleEl,this._menuEl,this.onDocumentKe
208
208
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
209
209
  * See the License for the specific language governing permissions and
210
210
  * limitations under the License.
211
- */class R{constructor(e,t={}){this.json,this._dom=e,this._opts=t,this._topbar=t.omitTopbar?null:new T(this,e),this.onMediaQueryChange=this.onMediaQueryChange.bind(this)}initFeatures(e){this.json=e,this._fullPageScreenshot=r.getFullPageScreenshot(e),this._topbar&&(this._topbar.enable(e),this._topbar.resetUIState()),this._setupMediaQueryListeners(),this._setupThirdPartyFilter(),this._setupElementScreenshotOverlay(this._dom.rootEl);const t=this._dom.isDevTools()||this._opts.disableDarkMode||this._opts.disableAutoDarkModeAndFireworks;!t&&window.matchMedia("(prefers-color-scheme: dark)").matches&&E(this._dom,!0);const n=["performance","accessibility","best-practices","seo"].every((t=>{const n=e.categories[t];return n&&1===n.score})),o=this._opts.disableFireworks||this._opts.disableAutoDarkModeAndFireworks;n&&!o&&(this._enableFireworks(),t||E(this._dom,!0));if(e.categories.performance&&e.categories.performance.auditRefs.some((t=>Boolean("metrics"===t.group&&e.audits[t.id].errorMessage)))){this._dom.find("input.lh-metrics-toggle__input",this._dom.rootEl).checked=!0}this.json.audits["script-treemap-data"]&&this.json.audits["script-treemap-data"].details&&this.addButton({text:a.strings.viewTreemapLabel,icon:"treemap",onClick:()=>function(e){if(!e.audits["script-treemap-data"].details)throw new Error("no script treemap data found");M({lhr:{mainDocumentUrl:e.mainDocumentUrl,finalUrl:e.finalUrl,finalDisplayedUrl:e.finalDisplayedUrl,audits:{"script-treemap-data":e.audits["script-treemap-data"]},configSettings:{locale:e.configSettings.locale}}},C()+"/treemap/","treemap-"+L(e))}(this.json)}),this._opts.onViewTrace&&this.addButton({text:"simulate"===e.configSettings.throttlingMethod?a.strings.viewOriginalTraceLabel:a.strings.viewTraceLabel,onClick:()=>this._opts.onViewTrace?.()}),this._opts.getStandaloneReportHTML&&this._dom.find('a[data-action="save-html"]',this._dom.rootEl).classList.remove("lh-hidden");for(const e of this._dom.findAll("[data-i18n]",this._dom.rootEl)){const t=e.getAttribute("data-i18n");e.textContent=a.strings[t]}}addButton(e){const t=this._dom.rootEl.querySelector(".lh-audit-group--metrics");if(!t)return;let n=t.querySelector(".lh-buttons");n||(n=this._dom.createChildOf(t,"div","lh-buttons"));const r=["lh-button"];e.icon&&(r.push("lh-report-icon"),r.push(`lh-report-icon--${e.icon}`));const o=this._dom.createChildOf(n,"button",r.join(" "));return o.textContent=e.text,o.addEventListener("click",e.onClick),o}resetUIState(){this._topbar&&this._topbar.resetUIState()}getReportHtml(){if(!this._opts.getStandaloneReportHTML)throw new Error("`getStandaloneReportHTML` is not set");return this.resetUIState(),this._opts.getStandaloneReportHTML()}saveAsGist(){throw new Error("Cannot save as gist from base report")}_enableFireworks(){this._dom.find(".lh-scores-container",this._dom.rootEl).classList.add("lh-score100")}_setupMediaQueryListeners(){const e=self.matchMedia("(max-width: 500px)");e.addListener(this.onMediaQueryChange),this.onMediaQueryChange(e)}_resetUIState(){this._topbar&&this._topbar.resetUIState()}onMediaQueryChange(e){this._dom.rootEl.classList.toggle("lh-narrow",e.matches)}_setupThirdPartyFilter(){const e=["uses-rel-preconnect","third-party-facades"],t=["legacy-javascript"];Array.from(this._dom.rootEl.querySelectorAll("table.lh-table")).filter((e=>e.querySelector("td.lh-table-column--url, td.lh-table-column--source-location"))).filter((t=>{const n=t.closest(".lh-audit");if(!n)throw new Error(".lh-table not within audit");return!e.includes(n.id)})).forEach((e=>{const n=function(e){return Array.from(e.tBodies[0].rows)}(e),o=this._getThirdPartyRows(n,r.getFinalDisplayedUrl(this.json)),i=this._dom.createComponent("3pFilter"),l=this._dom.find("input",i);l.addEventListener("change",(e=>{const t=e.target instanceof HTMLInputElement&&!e.target.checked;let r=!0,i=n[0];for(;i;){const e=t&&o.includes(i);do{i.classList.toggle("lh-row--hidden",e),i.classList.toggle("lh-row--even",!e&&r),i.classList.toggle("lh-row--odd",!e&&!r),i=i.nextElementSibling}while(i&&i.classList.contains("lh-sub-item-row"));e||(r=!r)}})),this._dom.find(".lh-3p-filter-count",i).textContent=`${o.length}`,this._dom.find(".lh-3p-ui-string",i).textContent=a.strings.thirdPartyResourcesLabel;const s=o.length===n.length,c=!o.length;if((s||c)&&(this._dom.find("div.lh-3p-filter",i).hidden=!0),!e.parentNode)return;e.parentNode.insertBefore(i,e);const d=e.closest(".lh-audit");if(!d)throw new Error(".lh-table not within audit");t.includes(d.id)&&!s&&l.click()}))}_setupElementScreenshotOverlay(e){this._fullPageScreenshot&&m.installOverlayFeature({dom:this._dom,rootEl:e,overlayContainerEl:e,fullPageScreenshot:this._fullPageScreenshot})}_getThirdPartyRows(e,t){const n=[],o=r.getRootDomain(t);for(const t of e){if(t.classList.contains("lh-sub-item-row"))continue;const e=t.querySelector("div.lh-text__url");if(!e)continue;const i=e.dataset.url;if(!i)continue;r.getRootDomain(i)!==o&&n.push(t)}return n}_saveFile(e){const t=e.type.match("json")?".json":".html",n=N({finalDisplayedUrl:r.getFinalDisplayedUrl(this.json),fetchTime:this.json.fetchTime})+t;this._opts.onSaveFileOverride?this._opts.onSaveFileOverride(e,n):this._dom.saveFile(e,n)}}
211
+ */class R{constructor(e,t={}){this.json,this._dom=e,this._opts=t,this._topbar=t.omitTopbar?null:new D(this,e),this.onMediaQueryChange=this.onMediaQueryChange.bind(this)}initFeatures(e){this.json=e,this._fullPageScreenshot=r.getFullPageScreenshot(e),this._topbar&&(this._topbar.enable(e),this._topbar.resetUIState()),this._setupMediaQueryListeners(),this._setupThirdPartyFilter(),this._setupElementScreenshotOverlay(this._dom.rootEl);const t=this._dom.isDevTools()||this._opts.disableDarkMode||this._opts.disableAutoDarkModeAndFireworks;!t&&window.matchMedia("(prefers-color-scheme: dark)").matches&&E(this._dom,!0);const n=["performance","accessibility","best-practices","seo"].every((t=>{const n=e.categories[t];return n&&1===n.score})),o=this._opts.disableFireworks||this._opts.disableAutoDarkModeAndFireworks;n&&!o&&(this._enableFireworks(),t||E(this._dom,!0));if(e.categories.performance&&e.categories.performance.auditRefs.some((t=>Boolean("metrics"===t.group&&e.audits[t.id].errorMessage)))){this._dom.find("input.lh-metrics-toggle__input",this._dom.rootEl).checked=!0}this.json.audits["script-treemap-data"]&&this.json.audits["script-treemap-data"].details&&this.addButton({text:a.strings.viewTreemapLabel,icon:"treemap",onClick:()=>function(e){if(!e.audits["script-treemap-data"].details)throw new Error("no script treemap data found");M({lhr:{mainDocumentUrl:e.mainDocumentUrl,finalUrl:e.finalUrl,finalDisplayedUrl:e.finalDisplayedUrl,audits:{"script-treemap-data":e.audits["script-treemap-data"]},configSettings:{locale:e.configSettings.locale}}},C()+"/treemap/","treemap-"+L(e))}(this.json)}),this._opts.onViewTrace&&this.addButton({text:"simulate"===e.configSettings.throttlingMethod?a.strings.viewOriginalTraceLabel:a.strings.viewTraceLabel,onClick:()=>this._opts.onViewTrace?.()}),this._opts.getStandaloneReportHTML&&this._dom.find('a[data-action="save-html"]',this._dom.rootEl).classList.remove("lh-hidden");for(const e of this._dom.findAll("[data-i18n]",this._dom.rootEl)){const t=e.getAttribute("data-i18n");e.textContent=a.strings[t]}}addButton(e){const t=this._dom.rootEl.querySelector(".lh-audit-group--metrics");if(!t)return;let n=t.querySelector(".lh-buttons");n||(n=this._dom.createChildOf(t,"div","lh-buttons"));const r=["lh-button"];e.icon&&(r.push("lh-report-icon"),r.push(`lh-report-icon--${e.icon}`));const o=this._dom.createChildOf(n,"button",r.join(" "));return o.textContent=e.text,o.addEventListener("click",e.onClick),o}resetUIState(){this._topbar&&this._topbar.resetUIState()}getReportHtml(){if(!this._opts.getStandaloneReportHTML)throw new Error("`getStandaloneReportHTML` is not set");return this.resetUIState(),this._opts.getStandaloneReportHTML()}saveAsGist(){throw new Error("Cannot save as gist from base report")}_enableFireworks(){this._dom.find(".lh-scores-container",this._dom.rootEl).classList.add("lh-score100")}_setupMediaQueryListeners(){const e=self.matchMedia("(max-width: 500px)");e.addListener(this.onMediaQueryChange),this.onMediaQueryChange(e)}_resetUIState(){this._topbar&&this._topbar.resetUIState()}onMediaQueryChange(e){this._dom.rootEl.classList.toggle("lh-narrow",e.matches)}_setupThirdPartyFilter(){const e=["uses-rel-preconnect","third-party-facades"],t=["legacy-javascript"];Array.from(this._dom.rootEl.querySelectorAll("table.lh-table")).filter((e=>e.querySelector("td.lh-table-column--url, td.lh-table-column--source-location"))).filter((t=>{const n=t.closest(".lh-audit");if(!n)throw new Error(".lh-table not within audit");return!e.includes(n.id)})).forEach((e=>{const n=function(e){return Array.from(e.tBodies[0].rows)}(e).filter((e=>!e.classList.contains("lh-sub-item-row"))),o=this._getThirdPartyRows(n,r.getFinalDisplayedUrl(this.json)),i=this._dom.createComponent("3pFilter"),l=this._dom.find("input",i);l.addEventListener("change",(e=>{const t=e.target instanceof HTMLInputElement&&!e.target.checked;let r=!0,i=n[0];for(;i;){const e=t&&o.includes(i);do{i.classList.toggle("lh-row--hidden",e),i.classList.toggle("lh-row--even",!e&&r),i.classList.toggle("lh-row--odd",!e&&!r),i=i.nextElementSibling}while(i&&i.classList.contains("lh-sub-item-row"));e||(r=!r)}})),this._dom.find(".lh-3p-filter-count",i).textContent=`${o.length}`,this._dom.find(".lh-3p-ui-string",i).textContent=a.strings.thirdPartyResourcesLabel;const s=o.length===n.length,c=!o.length;if((s||c)&&(this._dom.find("div.lh-3p-filter",i).hidden=!0),!e.parentNode)return;e.parentNode.insertBefore(i,e);const d=e.closest(".lh-audit");if(!d)throw new Error(".lh-table not within audit");t.includes(d.id)&&!s&&l.click()}))}_setupElementScreenshotOverlay(e){this._fullPageScreenshot&&m.installOverlayFeature({dom:this._dom,rootEl:e,overlayContainerEl:e,fullPageScreenshot:this._fullPageScreenshot})}_getThirdPartyRows(e,t){const n=r.getRootDomain(t),o=this.json.entities?.firstParty,i=[];for(const t of e){if(o){if(!t.dataset.entity||t.dataset.entity===o)continue}else{const e=t.querySelector("div.lh-text__url");if(!e)continue;const o=e.dataset.url;if(!o)continue;if(!(r.getRootDomain(o)!==n))continue}i.push(t)}return i}_saveFile(e){const t=e.type.match("json")?".json":".html",n=N({finalDisplayedUrl:r.getFinalDisplayedUrl(this.json),fetchTime:this.json.fetchTime})+t;this._opts.onSaveFileOverride?this._opts.onSaveFileOverride(e,n):this._dom.saveFile(e,n)}}
212
212
  /**
213
213
  * @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
214
214
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "9.5.0-dev.20230126",
4
+ "version": "9.5.0-dev.20230128",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -65,8 +65,8 @@ export class DetailsRenderer {
65
65
  return null;
66
66
 
67
67
  default: {
68
- // @ts-expect-error tsc thinks this is unreachable, but be forward compatible
69
- // with new unexpected detail types.
68
+ // @ts-expect-error - all detail types need to be handled above so tsc thinks this is unreachable.
69
+ // Call _renderUnknown() to be forward compatible with new, unexpected detail types.
70
70
  return this._renderUnknown(details.type, details);
71
71
  }
72
72
  }
@@ -398,9 +398,22 @@ export class DetailsRenderer {
398
398
  let even = true;
399
399
  for (const item of details.items) {
400
400
  const rowsFragment = this._renderTableRowsFromItem(item, details.headings);
401
+
402
+ // The attribute item.entity could be a string (entity-classification), or
403
+ // a LinkValue for ThirdPartySummary audit.
404
+ let entityName;
405
+ if (typeof item.entity === 'object' && item.entity.type === 'link') {
406
+ entityName = item.entity.text;
407
+ } else if (typeof item.entity === 'string') {
408
+ entityName = item.entity;
409
+ }
410
+
401
411
  for (const rowEl of this._dom.findAll('tr', rowsFragment)) {
402
412
  // For zebra styling.
403
413
  rowEl.classList.add(even ? 'lh-row--even' : 'lh-row--odd');
414
+ if (entityName && !rowEl.classList.contains('lh-sub-item-row')) {
415
+ rowEl.dataset.entity = entityName;
416
+ }
404
417
  }
405
418
  even = !even;
406
419
  tbodyElem.append(rowsFragment);
@@ -244,7 +244,9 @@ export class ReportUIFeatures {
244
244
 
245
245
  tablesWithUrls.forEach((tableEl) => {
246
246
  const rowEls = getTableRows(tableEl);
247
- const thirdPartyRows = this._getThirdPartyRows(rowEls, Util.getFinalDisplayedUrl(this.json));
247
+ const primaryRowEls = rowEls.filter(rowEl => !rowEl.classList.contains('lh-sub-item-row'));
248
+ const thirdPartyRowEls = this._getThirdPartyRows(
249
+ primaryRowEls, Util.getFinalDisplayedUrl(this.json));
248
250
 
249
251
  // create input box
250
252
  const filterTemplate = this._dom.createComponent('3pFilter');
@@ -253,9 +255,9 @@ export class ReportUIFeatures {
253
255
  filterInput.addEventListener('change', e => {
254
256
  const shouldHideThirdParty = e.target instanceof HTMLInputElement && !e.target.checked;
255
257
  let even = true;
256
- let rowEl = rowEls[0];
258
+ let rowEl = primaryRowEls[0];
257
259
  while (rowEl) {
258
- const shouldHide = shouldHideThirdParty && thirdPartyRows.includes(rowEl);
260
+ const shouldHide = shouldHideThirdParty && thirdPartyRowEls.includes(rowEl);
259
261
 
260
262
  // Iterate subsequent associated sub item rows.
261
263
  do {
@@ -272,12 +274,12 @@ export class ReportUIFeatures {
272
274
  });
273
275
 
274
276
  this._dom.find('.lh-3p-filter-count', filterTemplate).textContent =
275
- `${thirdPartyRows.length}`;
277
+ `${thirdPartyRowEls.length}`;
276
278
  this._dom.find('.lh-3p-ui-string', filterTemplate).textContent =
277
279
  Globals.strings.thirdPartyResourcesLabel;
278
280
 
279
- const allThirdParty = thirdPartyRows.length === rowEls.length;
280
- const allFirstParty = !thirdPartyRows.length;
281
+ const allThirdParty = thirdPartyRowEls.length === primaryRowEls.length;
282
+ const allFirstParty = !thirdPartyRowEls.length;
281
283
 
282
284
  // If all or none of the rows are 3rd party, hide the control.
283
285
  if (allThirdParty || allFirstParty) {
@@ -319,25 +321,29 @@ export class ReportUIFeatures {
319
321
  * @return {Array<HTMLElement>}
320
322
  */
321
323
  _getThirdPartyRows(rowEls, finalDisplayedUrl) {
322
- /** @type {Array<HTMLElement>} */
323
- const thirdPartyRows = [];
324
324
  const finalDisplayedUrlRootDomain = Util.getRootDomain(finalDisplayedUrl);
325
+ const firstPartyEntityName = this.json.entities?.firstParty;
325
326
 
327
+ /** @type {Array<HTMLElement>} */
328
+ const thirdPartyRowEls = [];
326
329
  for (const rowEl of rowEls) {
327
- if (rowEl.classList.contains('lh-sub-item-row')) continue;
328
-
329
- const urlItem = rowEl.querySelector('div.lh-text__url');
330
- if (!urlItem) continue;
331
-
332
- const datasetUrl = urlItem.dataset.url;
333
- if (!datasetUrl) continue;
334
- const isThirdParty = Util.getRootDomain(datasetUrl) !== finalDisplayedUrlRootDomain;
335
- if (!isThirdParty) continue;
330
+ if (firstPartyEntityName) {
331
+ // We rely on entity-classification for new LHRs that support it.
332
+ if (!rowEl.dataset.entity || rowEl.dataset.entity === firstPartyEntityName) continue;
333
+ } else {
334
+ // Without 10.0's entity classification, fallback to the older root domain-based filtering.
335
+ const urlItem = rowEl.querySelector('div.lh-text__url');
336
+ if (!urlItem) continue;
337
+ const datasetUrl = urlItem.dataset.url;
338
+ if (!datasetUrl) continue;
339
+ const isThirdParty = Util.getRootDomain(datasetUrl) !== finalDisplayedUrlRootDomain;
340
+ if (!isThirdParty) continue;
341
+ }
336
342
 
337
- thirdPartyRows.push(rowEl);
343
+ thirdPartyRowEls.push(rowEl);
338
344
  }
339
345
 
340
- return thirdPartyRows;
346
+ return thirdPartyRowEls;
341
347
  }
342
348
 
343
349
  /**
@@ -9,6 +9,19 @@ export class ReportUtils {
9
9
  * @return {LH.ReportResult}
10
10
  */
11
11
  static prepareReportResult(result: LH.Result): LH.ReportResult;
12
+ /**
13
+ * Given an audit's details, identify and return a URL locator function that
14
+ * can be called later with an `item` to extract the URL of it.
15
+ * @param {LH.FormattedIcu<LH.Audit.Details.TableColumnHeading[]>} headings
16
+ * @return {{(item: LH.FormattedIcu<LH.Audit.Details.TableItem>): string|undefined}=}
17
+ */
18
+ static getUrlLocatorFn(headings: LH.FormattedIcu<LH.Audit.Details.TableColumnHeading[]>): ((item: LH.FormattedIcu<LH.Audit.Details.TableItem>) => string | undefined) | undefined;
19
+ /**
20
+ * Mark TableItems/OpportunityItems with entity names.
21
+ * @param {LH.Result.Entities|undefined} entityClassification
22
+ * @param {import('../../types/lhr/audit-result').Result} audit
23
+ */
24
+ static classifyEntities(entityClassification: LH.Result.Entities | undefined, audit: import('../../types/lhr/audit-result').Result): void;
12
25
  /**
13
26
  * @param {LH.Result['configSettings']} settings
14
27
  * @return {!{deviceEmulation: string, screenEmulation?: string, networkThrottling: string, cpuThrottling: string, summary: string}}
@@ -90,6 +90,9 @@ class ReportUtils {
90
90
  }
91
91
  }
92
92
 
93
+ // Attach table/opportunity items with entity information.
94
+ ReportUtils.classifyEntities(result.entities, audit);
95
+
93
96
  // TODO: convert printf-style displayValue.
94
97
  // Added: #5099, v3
95
98
  // Removed: #6767, v4
@@ -187,6 +190,75 @@ class ReportUtils {
187
190
  return clone;
188
191
  }
189
192
 
193
+ /**
194
+ * Given an audit's details, identify and return a URL locator function that
195
+ * can be called later with an `item` to extract the URL of it.
196
+ * @param {LH.FormattedIcu<LH.Audit.Details.TableColumnHeading[]>} headings
197
+ * @return {{(item: LH.FormattedIcu<LH.Audit.Details.TableItem>): string|undefined}=}
198
+ */
199
+ static getUrlLocatorFn(headings) {
200
+ // The most common type, valueType=url.
201
+ const urlKey = headings.find(heading => heading.valueType === 'url')?.key;
202
+ if (urlKey && typeof urlKey === 'string') {
203
+ // Return a function that extracts item.url.
204
+ return (item) => {
205
+ const url = item[urlKey];
206
+ if (typeof url === 'string') return url;
207
+ };
208
+ }
209
+
210
+ // The second common type, valueType=source-location.
211
+ const srcLocationKey = headings.find(heading => heading.valueType === 'source-location')?.key;
212
+ if (srcLocationKey) {
213
+ // Return a function that extracts item.source.url.
214
+ return (item) => {
215
+ const sourceLocation = item[srcLocationKey];
216
+ if (typeof sourceLocation === 'object' && sourceLocation.type === 'source-location') {
217
+ return sourceLocation.url;
218
+ }
219
+ };
220
+ }
221
+
222
+ // More specific tests go here, as we need to identify URLs in more audits.
223
+ }
224
+
225
+ /**
226
+ * Mark TableItems/OpportunityItems with entity names.
227
+ * @param {LH.Result.Entities|undefined} entityClassification
228
+ * @param {import('../../types/lhr/audit-result').Result} audit
229
+ */
230
+ static classifyEntities(entityClassification, audit) {
231
+ if (!entityClassification) return;
232
+ if (audit.details?.type !== 'opportunity' && audit.details?.type !== 'table') {
233
+ return;
234
+ }
235
+
236
+ // If details.items are already marked with entity attribute during an audit, nothing to do here.
237
+ const {items, headings} = audit.details;
238
+ if (!items.length || items.some(item => item.entity)) return;
239
+
240
+ // Identify a URL-locator function that we could call against each item to get its URL.
241
+ const urlLocatorFn = ReportUtils.getUrlLocatorFn(headings);
242
+ if (!urlLocatorFn) return;
243
+
244
+ for (const item of items) {
245
+ const url = urlLocatorFn(item);
246
+ if (!url) continue;
247
+
248
+ let origin;
249
+ try {
250
+ // Non-URLs can appear in valueType: url columns, like 'Unattributable'
251
+ origin = Util.parseURL(url).origin;
252
+ } catch {}
253
+ if (!origin) continue;
254
+
255
+ const entityIndex = entityClassification.entityIndexByOrigin[origin];
256
+ if (entityIndex === undefined) return;
257
+ const entity = entityClassification.list[entityIndex];
258
+ item.entity = entity.name;
259
+ }
260
+ }
261
+
190
262
  /**
191
263
  * @param {LH.Result['configSettings']} settings
192
264
  * @return {!{deviceEmulation: string, screenEmulation?: string, networkThrottling: string, cpuThrottling: string, summary: string}}