Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new_audit(preload-audit): Adding preload audit #3450

Merged
merged 24 commits into from
Feb 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lighthouse-cli/test/fixtures/perf/level-2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license Copyright 2017 Google Inc. All Rights Reserved.
* 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
* 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.
*/

/* eslint-disable */

document.write('<script src="level-3.js"></script>');
12 changes: 12 additions & 0 deletions lighthouse-cli/test/fixtures/perf/level-3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @license Copyright 2017 Google Inc. All Rights Reserved.
* 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
* 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.
*/

/* eslint-disable */

const dummyDiv = document.createElement('div');
dummyDiv.innerHTML = 'Hello!';

document.body.appendChild(dummyDiv);
8 changes: 8 additions & 0 deletions lighthouse-cli/test/fixtures/perf/preload_style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@font-face {
font-family: "Lobster";
src: url("./lobster-v20-latin-regular.woff2") format("woff2");
}

body {
font-family: "Lobster";
}
9 changes: 9 additions & 0 deletions lighthouse-cli/test/fixtures/perf/preload_tester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license Copyright 2017 Google Inc. All Rights Reserved.
* 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
* 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.
*/

/* eslint-disable */

document.write('<script src="level-2.js?delay=500"></script>')
10 changes: 10 additions & 0 deletions lighthouse-cli/test/fixtures/preload.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<html>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<link rel="stylesheet" href="/perf/preload_style.css" />
<body>
There was nothing special about this site,
nothing was left to be seen,
not even a kite.
<script src="/perf/preload_tester.js"></script>
</body>
</html>
15 changes: 12 additions & 3 deletions lighthouse-cli/test/smokehouse/perf/expectations.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
*/
module.exports = [
{
initialUrl: 'http://localhost:10200/online-only.html',
url: 'http://localhost:10200/online-only.html',
initialUrl: 'http://localhost:10200/preload.html',
url: 'http://localhost:10200/preload.html',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we're changing this URL do we need the separate entry below still?

audits: {
'speed-index-metric': {
score: '>=90',
score: '>=80',
extendedInfo: {
value: {
timings: {},
Expand All @@ -36,6 +36,15 @@ module.exports = [
// Can be flaky, so test float rawValue instead of boolean score
rawValue: '<1000',
},
'uses-rel-preload': {
score: '<100',
rawValue: '>500',
details: {
items: {
length: 1,
},
},
},
},
},
{
Expand Down
48 changes: 43 additions & 5 deletions lighthouse-core/audits/critical-request-chains.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,42 @@ class CriticalRequestChains extends Audit {
return longest;
}

/**
* @param {*} tree
*/
static flattenRequests(tree) {
const flattendChains = {};
const chainMap = new Map();
CriticalRequestChains._traverse(tree, opts => {
let chain;
if (chainMap.has(opts.id)) {
chain = chainMap.get(opts.id);
} else {
chain = {};
flattendChains[opts.id] = chain;
}

const request = opts.node.request;
chain.request = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe that flatten function is needed over here now?

url: request.url,
startTime: request.startTime,
endTime: request.endTime,
responseReceivedTime: request.responseReceivedTime,
transferSize: request.transferSize,
};
chain.children = {};
Object.keys(opts.node.children).forEach(chainId => {
const childChain = {};
chainMap.set(chainId, childChain);
chain.children[chainId] = childChain;
});

chainMap.set(opts.id, chain);
});

return flattendChains;
}

/**
* Audits the page to give a score for First Meaningful Paint.
* @param {!Artifacts} artifacts The artifacts from the gather phase.
Expand All @@ -101,29 +137,31 @@ class CriticalRequestChains extends Audit {
walk(child.children, depth + 1);
}, '');
}
// Convert
const flattenedChains = CriticalRequestChains.flattenRequests(chains);

// Account for initial navigation
const initialNavKey = Object.keys(chains)[0];
const initialNavChildren = initialNavKey && chains[initialNavKey].children;
const initialNavKey = Object.keys(flattenedChains)[0];
const initialNavChildren = initialNavKey && flattenedChains[initialNavKey].children;
if (initialNavChildren && Object.keys(initialNavChildren).length > 0) {
walk(initialNavChildren, 0);
}

const longestChain = CriticalRequestChains._getLongestChain(chains);
const longestChain = CriticalRequestChains._getLongestChain(flattenedChains);

return {
rawValue: chainCount === 0,
displayValue: Util.formatNumber(chainCount),
extendedInfo: {
value: {
chains,
chains: flattenedChains,
longestChain,
},
},
details: {
type: 'criticalrequestchain',
header: {type: 'text', text: 'View critical network waterfall:'},
chains,
chains: flattenedChains,
longestChain,
},
};
Expand Down
110 changes: 110 additions & 0 deletions lighthouse-core/audits/uses-rel-preload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* @license Copyright 2017 Google Inc. All Rights Reserved.
* 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
* 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.
*/

'use strict';

const Audit = require('./audit');
const Util = require('../report/v2/renderer/util');
const UnusedBytes = require('./byte-efficiency/byte-efficiency-audit');
const THRESHOLD_IN_MS = 100;

class UsesRelPreloadAudit extends Audit {
/**
* @return {!AuditMeta}
*/
static get meta() {
return {
category: 'Performance',
name: 'uses-rel-preload',
description: 'Preload key requests',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's mark this as informative: true

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

informative: true,
helpText: 'Consider using <link rel=preload> to prioritize fetching late-discovered ' +
'resources sooner [Learn more](https://developers.google.com/web/updates/2016/03/link-rel-preload).',
requiredArtifacts: ['devtoolsLogs', 'traces'],
scoringMode: Audit.SCORING_MODES.NUMERIC,
};
}

static _flattenRequests(chains, maxLevel, minLevel = 0) {
const requests = [];
const flatten = (chains, level) => {
Object.keys(chains).forEach(chain => {
if (chains[chain]) {
const currentChain = chains[chain];
if (level >= minLevel) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting "bug".. :) repro:

lighthouse --perf https://paulirish.com --save-assets -GA

it lists CRC depth=1 requests in the audit:
screen shot 2018-01-05 at 2 18 16 pm

but they're just depth=1 to my brain. in reality its because https://paulirish.com redirected to https://www.paulirish.com

so we'll want to use the post-redirect URL as the root for this CRC depth calculation.

Not sure how you want to handle this. if you want to grab the mainResource computed artifact or something?

requests.push(currentChain.request);
}

if (level < maxLevel) {
flatten(currentChain.children, level + 1);
}
}
});
};

flatten(chains, 0);

return requests;
}

/**
* @param {!Artifacts} artifacts
* @return {!AuditResult}
*/
static audit(artifacts) {
const devtoolsLogs = artifacts.devtoolsLogs[UsesRelPreloadAudit.DEFAULT_PASS];

return Promise.all([
artifacts.requestCriticalRequestChains(devtoolsLogs),
artifacts.requestMainResource(devtoolsLogs),
]).then(([critChains, mainResource]) => {
const results = [];
let maxWasted = 0;
// get all critical requests 2 + mainResourceIndex levels deep
const mainResourceIndex = mainResource.redirects ? mainResource.redirects.length : 0;

const criticalRequests = UsesRelPreloadAudit._flattenRequests(critChains,
3 + mainResourceIndex, 2 + mainResourceIndex);
criticalRequests.forEach(request => {
const networkRecord = request;
if (!networkRecord._isLinkPreload && networkRecord.protocol !== 'data') {
// calculate time between mainresource.endTime and resource start time
const wastedMs = Math.min(request._startTime - mainResource._endTime,
request._endTime - request._startTime) * 1000;

if (wastedMs >= THRESHOLD_IN_MS) {
maxWasted = Math.max(wastedMs, maxWasted);
results.push({
url: request.url,
wastedMs: Util.formatMilliseconds(wastedMs),
});
}
}
});

// sort results by wastedTime DESC
results.sort((a, b) => b.wastedMs - a.wastedMs);

const headings = [
{key: 'url', itemType: 'url', text: 'URL'},
{key: 'wastedMs', itemType: 'text', text: 'Potential Savings'},
];
const details = Audit.makeTableDetails(headings, results);

return {
score: UnusedBytes.scoreForWastedMs(maxWasted),
rawValue: maxWasted,
displayValue: Util.formatMilliseconds(maxWasted),
extendedInfo: {
value: results,
},
details,
};
});
}
}

module.exports = UsesRelPreloadAudit;
2 changes: 2 additions & 0 deletions lighthouse-core/config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ module.exports = {
'deprecations',
'mainthread-work-breakdown',
'bootup-time',
'uses-rel-preload',
'font-display',
'manual/pwa-cross-browser',
'manual/pwa-page-transitions',
Expand Down Expand Up @@ -279,6 +280,7 @@ module.exports = {
{id: 'uses-request-compression', weight: 0, group: 'perf-hint'},
{id: 'time-to-first-byte', weight: 0, group: 'perf-hint'},
{id: 'redirects', weight: 0, group: 'perf-hint'},
{id: 'uses-rel-preload', weight: 0, group: 'perf-hint'},
{id: 'total-byte-weight', weight: 0, group: 'perf-info'},
{id: 'uses-long-cache-ttl', weight: 0, group: 'perf-info'},
{id: 'dom-size', weight: 0, group: 'perf-info'},
Expand Down
14 changes: 2 additions & 12 deletions lighthouse-core/gather/computed/critical-request-chains.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,6 @@ class CriticalRequestChains extends ComputedArtifact {
const criticalRequests = networkRecords.filter(request =>
CriticalRequestChains.isCritical(request, mainResource));

const flattenRequest = request => {
return {
url: request._url,
startTime: request.startTime,
endTime: request.endTime,
responseReceivedTime: request.responseReceivedTime,
transferSize: request.transferSize,
};
};

// Create a tree of critical requests.
const criticalRequestChains = {};
for (const request of criticalRequests) {
Expand Down Expand Up @@ -103,7 +93,7 @@ class CriticalRequestChains extends ComputedArtifact {
const parentRequestId = parentRequest.requestId;
if (!node[parentRequestId]) {
node[parentRequestId] = {
request: flattenRequest(parentRequest),
request: parentRequest,
children: {},
};
}
Expand All @@ -124,7 +114,7 @@ class CriticalRequestChains extends ComputedArtifact {

// node should now point to the immediate parent for this request.
node[request.requestId] = {
request: flattenRequest(request),
request,
children: {},
};
}
Expand Down
Loading