[go: nahoru, domu]

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

core(offscreen-images): pass images with 'loading' attribute #10117

Merged
merged 9 commits into from
Dec 20, 2019
6 changes: 3 additions & 3 deletions lighthouse-cli/test/fixtures/byte-efficiency/tester.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
}

// Add a long task to delay FI
setTimeout(longTask, 2000);
jazyan marked this conversation as resolved.
Show resolved Hide resolved
setTimeout(longTask, 4000);

// Lazily load the image
setTimeout(() => {
Expand Down Expand Up @@ -115,8 +115,8 @@ <h2>Byte efficiency tester page</h2>
<!-- PASS(optimized): image is vector -->
<!-- PASS(webp): image is vector -->
<!-- PASS(responsive): image is vector -->
<!-- FAIL(offscreen): image is offscreen -->
jazyan marked this conversation as resolved.
Show resolved Hide resolved
<img style="width: 100px; height: 100px;" src="large.svg">
<!-- PASS(offscreen): image is offscreen and loads before TTI, but loads lazily -->
<img style="width: 100px; height: 100px;" src="large.svg?loadinglazy-offscreen" loading="lazy">

<!-- PASS(optimized): image is JPEG optimized -->
<!-- PASS(webp): image has insigificant WebP savings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ const expectations = [
url: /lighthouse-480x320.webp$/,
}, {
url: /lighthouse-480x320.webp\?invisible$/,
}, {
url: /large.svg$/,
},
],
},
Expand Down
3 changes: 2 additions & 1 deletion lighthouse-core/audits/byte-efficiency/offscreen-images.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ class OffscreenImages extends ByteEfficiencyAudit {
*/
static computeWaste(image, viewportDimensions, networkRecords) {
const networkRecord = networkRecords.find(record => record.url === image.src);
if (!image.resourceSize || !networkRecord) {
if (!image.resourceSize || !networkRecord || image.loading === 'lazy' ||
jazyan marked this conversation as resolved.
Show resolved Hide resolved
image.loading === 'eager') {
jazyan marked this conversation as resolved.
Show resolved Hide resolved
return null;
}

Expand Down
2 changes: 2 additions & 0 deletions lighthouse-core/gather/gatherers/image-elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ function getHTMLImages(allElements) {
naturalWidth: element.naturalWidth,
naturalHeight: element.naturalHeight,
isCss: false,
// @ts-ignore: loading attribute not yet added to HTMLImageElement definition.
loading: element.loading,
resourceSize: 0, // this will get overwritten below
isPicture: !!element.parentElement && element.parentElement.tagName === 'PICTURE',
usesObjectFit: ['cover', 'contain', 'scale-down', 'none'].includes(
Expand Down
201 changes: 165 additions & 36 deletions lighthouse-core/test/audits/byte-efficiency/offscreen-images-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function generateRecord({
return {
url,
mimeType,
startTime, // DevTools timestamp which is in seconds
startTime, // DevTools timestamp which is in seconds.
resourceSize: resourceSizeInKb * 1024,
};
}
Expand All @@ -33,7 +33,13 @@ function generateSize(width, height, prefix = 'displayed') {
return size;
}

function generateImage(size, coords, networkRecord, src = 'https://google.com/logo.png') {
function generateImage({
size,
coords,
networkRecord,
loading,
src = 'https://google.com/logo.png',
}) {
Object.assign(networkRecord || {}, {url: src});

const x = coords[0];
Expand All @@ -45,9 +51,14 @@ function generateImage(size, coords, networkRecord, src = 'https://google.com/lo
left: x,
right: x + size.displayedWidth,
};
const image = {src, clientRect, ...networkRecord};
Object.assign(image, size);
return image;

return {
jazyan marked this conversation as resolved.
Show resolved Hide resolved
src,
clientRect,
loading,
...networkRecord,
...size,
};
}

describe('OffscreenImages audit', () => {
Expand All @@ -63,7 +74,7 @@ describe('OffscreenImages audit', () => {
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
generateImage(generateSize(100, 100), [0, 0]),
generateImage({size: generateSize(100, 100), coords: [0, 0]}),
],
traces: {defaultPass: createTestTrace({topLevelTasks})},
devtoolsLogs: {},
Expand All @@ -84,9 +95,19 @@ describe('OffscreenImages audit', () => {
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
generateImage(generateSize(200, 200), [0, 0], recordA),
generateImage(generateSize(100, 100), [0, 1080], recordB, urlB),
generateImage(generateSize(400, 400), [1720, 1080], recordC, urlC),
generateImage({size: generateSize(200, 200), coords: [0, 0], networkRecord: recordA}),
generateImage({
size: generateSize(100, 100),
coords: [0, 1080],
networkRecord: recordB,
src: urlB,
}),
generateImage({
size: generateSize(400, 400),
coords: [1720, 1080],
networkRecord: recordC,
src: urlC,
}),
],
traces: {defaultPass: createTestTrace({topLevelTasks})},
devtoolsLogs: {},
Expand All @@ -109,16 +130,40 @@ describe('OffscreenImages audit', () => {
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
// offscreen to the right
generateImage(generateSize(200, 200), [3000, 0], networkRecords[0]),
// offscreen to the bottom
generateImage(generateSize(100, 100), [0, 2000], networkRecords[1], url('B')),
// offscreen to the top-left
generateImage(generateSize(100, 100), [-2000, -1000], networkRecords[2], url('C')),
// offscreen to the bottom-right
generateImage(generateSize(100, 100), [3000, 2000], networkRecords[3], url('D')),
// half offscreen to the top, should not warn
generateImage(generateSize(1000, 1000), [0, -500], networkRecords[4], url('E')),
// Offscreen to the right.
generateImage({
size: generateSize(200, 200),
coords: [3000, 0],
networkRecord: networkRecords[0],
}),
// Offscreen to the bottom.
generateImage({
size: generateSize(100, 100),
coords: [0, 2000],
networkRecord: networkRecords[1],
src: url('B'),
}),
// Offscreen to the top-left.
generateImage({
size: generateSize(100, 100),
coords: [-2000, -1000],
jazyan marked this conversation as resolved.
Show resolved Hide resolved
networkRecord: networkRecords[2],
src: url('C'),
}),
// Offscreen to the bottom-right.
generateImage({
size: generateSize(100, 100),
coords: [3000, 2000],
networkRecord: networkRecords[3],
src: url('D'),
}),
// Half offscreen to the top, should not warn.
generateImage({
size: generateSize(1000, 1000),
coords: [0, -500],
networkRecord: networkRecords[4],
src: url('E'),
}),
],
traces: {defaultPass: createTestTrace({topLevelTasks})},
devtoolsLogs: {},
Expand All @@ -128,13 +173,49 @@ describe('OffscreenImages audit', () => {
assert.equal(auditResult.items.length, 4);
});

it('passes images with a specified loading attribute', async () => {
jazyan marked this conversation as resolved.
Show resolved Hide resolved
const url = s => `https://google.com/logo${s}.png`;
const topLevelTasks = [{ts: 1900, duration: 100}];
const networkRecords = [
generateRecord({url: url('A'), resourceSizeInKb: 100}),
generateRecord({url: url('B'), resourceSizeInKb: 100}),
];
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
// Offscreen to the right, but lazy loaded.
generateImage({
size: generateSize(200, 200),
coords: [3000, 0],
networkRecord: networkRecords[0],
loading: 'lazy',
src: url('A'),
}),
// Offscreen to the bottom, but eager loaded.
generateImage({
size: generateSize(100, 100),
coords: [0, 2000],
networkRecord: networkRecords[1],
loading: 'eager',
src: url('B'),
}),
],
traces: {defaultPass: createTestTrace({topLevelTasks})},
devtoolsLogs: {},
};

return UnusedImages.audit_(artifacts, networkRecords, context).then(auditResult => {
assert.equal(auditResult.items.length, 0);
});
});

it('finds images with 0 area', () => {
const topLevelTasks = [{ts: 1900, duration: 100}];
const networkRecord = generateRecord({resourceSizeInKb: 100});
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
generateImage(generateSize(0, 0), [0, 0], networkRecord),
generateImage({size: generateSize(0, 0), coords: [0, 0], networkRecord}),
],
traces: {defaultPass: createTestTrace({topLevelTasks})},
devtoolsLogs: {},
Expand All @@ -158,10 +239,28 @@ describe('OffscreenImages audit', () => {
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
generateImage(generateSize(50, 50), [0, 0], networkRecords[0]),
generateImage(generateSize(1000, 1000), [1000, 1000], networkRecords[1]),
generateImage(generateSize(50, 50), [0, 1500], networkRecords[2], urlB),
generateImage(generateSize(400, 400), [0, 1500], networkRecords[3], urlB),
generateImage({
size: generateSize(50, 50),
coords: [0, 0],
networkRecord: networkRecords[0],
}),
generateImage({
size: generateSize(1000, 1000),
coords: [1000, 1000],
networkRecord: networkRecords[1],
}),
generateImage({
size: generateSize(50, 50),
coords: [0, 1500],
networkRecord: networkRecords[2],
src: urlB,
}),
generateImage({
size: generateSize(400, 400),
coords: [0, 1500],
networkRecord: networkRecords[3],
src: urlB,
}),
],
traces: {defaultPass: createTestTrace({topLevelTasks})},
devtoolsLogs: {},
Expand All @@ -178,8 +277,8 @@ describe('OffscreenImages audit', () => {
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
// offscreen to the right
generateImage(generateSize(200, 200), [3000, 0], networkRecord),
// Offscreen to the right.
generateImage({size: generateSize(200, 200), coords: [3000, 0], networkRecord}),
],
traces: {defaultPass: createTestTrace({topLevelTasks})},
devtoolsLogs: {},
Expand All @@ -195,8 +294,8 @@ describe('OffscreenImages audit', () => {
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
// offscreen to the right
generateImage(generateSize(200, 200), [3000, 0], networkRecord),
// Offscreen to the right.
generateImage({size: generateSize(200, 200), coords: [3000, 0], networkRecord}),
],
traces: {defaultPass: createTestTrace({traceEnd: 2000})},
devtoolsLogs: {},
Expand All @@ -212,8 +311,8 @@ describe('OffscreenImages audit', () => {
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
// offscreen to the right
generateImage(generateSize(100, 100), [0, 2000], networkRecord),
// Offscreen to the right.
generateImage({size: generateSize(100, 100), coords: [0, 2000], networkRecord}),
],
traces: {defaultPass: createTestTrace({traceEnd: 2000})},
devtoolsLogs: {},
Expand Down Expand Up @@ -251,8 +350,18 @@ describe('OffscreenImages audit', () => {
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
generateImage(generateSize(0, 0), [0, 0], recordA, recordA.url),
generateImage(generateSize(200, 200), [3000, 0], recordB, recordB.url),
generateImage({
size: generateSize(0, 0),
coords: [0, 0],
networkRecord: recordA,
src: recordA.url,
}),
generateImage({
size: generateSize(200, 200),
coords: [3000, 0],
networkRecord: recordB,
src: recordB.url,
}),
],
traces: {defaultPass: createTestTrace({topLevelTasks})},
devtoolsLogs: {defaultPass: devtoolsLog},
Expand Down Expand Up @@ -295,8 +404,18 @@ describe('OffscreenImages audit', () => {
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
generateImage(generateSize(0, 0), [0, 0], recordA, recordA.url),
generateImage(generateSize(200, 200), [3000, 0], recordB, recordB.url),
generateImage({
size: generateSize(0, 0),
coords: [0, 0],
networkRecord: recordA,
src: recordA.url,
}),
generateImage({
size: generateSize(200, 200),
coords: [3000, 0],
networkRecord: recordB,
src: recordB.url,
}),
],
traces: {defaultPass: createTestTrace({topLevelTasks})},
devtoolsLogs: {defaultPass: devtoolsLog},
Expand All @@ -320,8 +439,18 @@ describe('OffscreenImages audit', () => {
const artifacts = {
ViewportDimensions: DEFAULT_DIMENSIONS,
ImageElements: [
generateImage(generateSize(0, 0), [0, 0], networkRecords[0], 'a'),
generateImage(generateSize(200, 200), [3000, 0], networkRecords[1], 'b'),
generateImage({
size: generateSize(0, 0),
coords: [0, 0],
networkRecord: networkRecords[0],
src: 'a',
}),
generateImage({
size: generateSize(200, 200),
coords: [3000, 0],
networkRecord: networkRecords[1],
src: 'b',
}),
],
traces: {defaultPass: createTestTrace({traceEnd: 2000})},
devtoolsLogs: {},
Expand Down
2 changes: 2 additions & 0 deletions types/artifacts.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ declare global {
resourceSize: number;
/** The MIME type of the underlying image file. */
mimeType?: string;
/** The loading attribute of the image. */
loading?: string;
}

export interface OptimizedImage {
Expand Down