diff --git a/lighthouse-cli/test/fixtures/perf/level-2.js b/lighthouse-cli/test/fixtures/perf/level-2.js index 49ddab0717b3..76a37412bc43 100644 --- a/lighthouse-cli/test/fixtures/perf/level-2.js +++ b/lighthouse-cli/test/fixtures/perf/level-2.js @@ -6,4 +6,4 @@ /* eslint-disable */ -document.write(''); +document.write(''); diff --git a/lighthouse-cli/test/fixtures/perf/preload_tester.js b/lighthouse-cli/test/fixtures/perf/preload_tester.js index 5ac0b60e770c..e81fd01d609f 100644 --- a/lighthouse-cli/test/fixtures/perf/preload_tester.js +++ b/lighthouse-cli/test/fixtures/perf/preload_tester.js @@ -6,7 +6,8 @@ /* eslint-disable */ -document.write(''); +document.write(''); +document.write(''); // delay our preconnect-candidates so that they're not assumed to be working already setTimeout(() => { diff --git a/lighthouse-cli/test/fixtures/preload.html b/lighthouse-cli/test/fixtures/preload.html index 2d05c12d6481..c28224a080ab 100644 --- a/lighthouse-cli/test/fixtures/preload.html +++ b/lighthouse-cli/test/fixtures/preload.html @@ -1,6 +1,8 @@ + + diff --git a/lighthouse-cli/test/smokehouse/perf/expectations.js b/lighthouse-cli/test/smokehouse/perf/expectations.js index 809587f28990..fc8fcf297994 100644 --- a/lighthouse-cli/test/smokehouse/perf/expectations.js +++ b/lighthouse-cli/test/smokehouse/perf/expectations.js @@ -39,6 +39,10 @@ module.exports = [ 'uses-rel-preload': { score: '<1', rawValue: '>500', + warnings: { + 0: /level-2.*warning/, + length: 1, + }, details: { items: { length: 1, diff --git a/lighthouse-core/audits/uses-rel-preload.js b/lighthouse-core/audits/uses-rel-preload.js index bcaf39191ad9..e79680c559db 100644 --- a/lighthouse-core/audits/uses-rel-preload.js +++ b/lighthouse-core/audits/uses-rel-preload.js @@ -20,6 +20,9 @@ const UIStrings = { /** Description of a Lighthouse audit that tells the user *why* they should preload important network requests. The associated network requests are started halfway through pageload (or later) but should be started at the beginning. This is displayed after a user expands the section to see more. No character length limits. '' is the html code the user would include in their page and shouldn't be translated. 'Learn More' becomes link text to additional documentation. */ description: 'Consider using to prioritize fetching resources that are ' + 'currently requested later in page load. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/preload).', + /** A warning message that is shown when the user tried to follow the advice of the audit, but it's not working as expected. Forgetting to set the `crossorigin` HTML attribute, or setting it to an incorrect value, on the link is a common mistake when adding preload links. */ + crossoriginWarning: 'A preload was found for "{preloadURL}" but was not used ' + + 'by the browser. Check that you are using the `crossorigin` attribute properly.', }; const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings); @@ -60,6 +63,25 @@ class UsesRelPreloadAudit extends Audit { return urls; } + /** + * Finds which URLs were attempted to be preloaded, but failed to be reused and were requested again. + * + * @param {LH.Gatherer.Simulation.GraphNode} graph + * @return {Set} + */ + static getURLsFailedToPreload(graph) { + /** @type {Array} */ + const requests = []; + graph.traverse(node => node.type === 'network' && requests.push(node.record)); + + const preloadRequests = requests.filter(req => req.isLinkPreload); + const preloadURLs = new Set(preloadRequests.map(req => req.url)); + // A failed preload attempt will manifest as a URL that was requested twice. + // Once with `isLinkPreload` AND again without `isLinkPreload`. + const failedRequests = requests.filter(req => preloadURLs.has(req.url) && !req.isLinkPreload); + return new Set(failedRequests.map(req => req.url)); + } + /** * We want to preload all first party critical requests at depth 2. * Third party requests can be tricky to know the URL ahead of time. @@ -183,6 +205,14 @@ class UsesRelPreloadAudit extends Audit { // sort results by wastedTime DESC results.sort((a, b) => b.wastedMs - a.wastedMs); + /** @type {Array|undefined} */ + let warnings; + const failedURLs = UsesRelPreloadAudit.getURLsFailedToPreload(graph); + if (failedURLs.size) { + warnings = Array.from(failedURLs) + .map(preloadURL => str_(UIStrings.crossoriginWarning, {preloadURL})); + } + /** @type {LH.Result.Audit.OpportunityDetails['headings']} */ const headings = [ {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)}, @@ -200,6 +230,7 @@ class UsesRelPreloadAudit extends Audit { value: results, }, details, + warnings, }; } } diff --git a/lighthouse-core/lib/i18n/en-US.json b/lighthouse-core/lib/i18n/en-US.json index d2888845e655..701bcef139af 100644 --- a/lighthouse-core/lib/i18n/en-US.json +++ b/lighthouse-core/lib/i18n/en-US.json @@ -763,6 +763,10 @@ "message": "Preconnect to required origins", "description": "Imperative title of a Lighthouse audit that tells the user to connect early to internet domains that will be used to load page resources. Origin is the correct term, however 'domain name' could be used if neccsesary. This is displayed in a list of audit titles that Lighthouse generates." }, + "lighthouse-core/audits/uses-rel-preload.js | crossoriginWarning": { + "message": "A preload was found for \"{preloadURL}\" but was not used by the browser. Check that you are using the `crossorigin` attribute properly.", + "description": "A warning message that is shown when the user tried to follow the advice of the audit, but it's not working as expected. Forgetting to set the `crossorigin` HTML attribute, or setting it to an incorrect value, on the link is a common mistake when adding preload links." + }, "lighthouse-core/audits/uses-rel-preload.js | description": { "message": "Consider using to prioritize fetching resources that are currently requested later in page load. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/preload).", "description": "Description of a Lighthouse audit that tells the user *why* they should preload important network requests. The associated network requests are started halfway through pageload (or later) but should be started at the beginning. This is displayed after a user expands the section to see more. No character length limits. '' is the html code the user would include in their page and shouldn't be translated. 'Learn More' becomes link text to additional documentation."