Skip to content

core(preconnect): add warning if preconnect link is not used#6694

Merged
patrickhulce merged 12 commits into
masterfrom
rel_preconnect_tweaks
Dec 13, 2018
Merged

core(preconnect): add warning if preconnect link is not used#6694
patrickhulce merged 12 commits into
masterfrom
rel_preconnect_tweaks

Conversation

@patrickhulce

Copy link
Copy Markdown
Collaborator

Summary
This PR adds a warning to the preconnect audit when the origin is found in a link[rel=preconnect] but still isn't being used. Usually it's because of a missing crossorigin but good to be aware of either way.

I'd also like to start ignoring origins that were discovered within ~300 ms or so of the main document since that's basically what preconnect would do anyway, but thought I'd bring that up for discussion first.

#6676 also suggested mentioning a limit on the number of preconnect origins in our help text. I think that makes sense but goes against our "it's all in the learn more link" stance. Others Ok with it?

Related Issues/PRs
fixes #5932 #6676

Comment thread lighthouse-core/gather/gatherers/seo/hreflang.js Outdated
@wardpeet

wardpeet commented Dec 2, 2018

Copy link
Copy Markdown
Collaborator

I'd also like to start ignoring origins that were discovered within ~300 ms or so of the main document since that's basically what preconnect would do anyway, but thought I'd bring that up for discussion first.

I actually like that we discard these records. Now your warning message isn't super helpful. If I read it I would add crossorigin to the preconnect tag and I'll see the same message pop up. Sometimes crossorigin helps but the urls listed in the bugs do not get fixed by slapping crossorigin on it.

@patrickhulce

Copy link
Copy Markdown
Collaborator Author

I actually like that we discard these records.

As in, you would like it if we started discarding ones started within the first 300 ms? :) Right now it's kinda strict at 50 ms.

Now your warning message isn't super helpful. If I read it I would add crossorigin to the preconnect tag and I'll see the same message pop up.

Good point, should we not mention it at all then just save it for the docs?

@wardpeet

wardpeet commented Dec 4, 2018

Copy link
Copy Markdown
Collaborator

As in, you would like it if we started discarding ones started within the first 300 ms? :) Right now it's kinda strict at 50 ms.

not sure what the correct timing is on this :) i'm fine with whatever is correct

Good point, should we not mention it at all then just save it for the docs?

Yes indeed, maybe just point out that the preconnect fields had no effect and refer to docs

@paulirish paulirish mentioned this pull request Dec 7, 2018
6 tasks

@paulirish paulirish left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Isn't #5932 pointing out we tell the user to preconnect even if they already are?

Seems like we have to exclude any currently preconnected origins from our recommendations, right?

async afterPass(passContext) {
const driver = passContext.driver;

// TODO(phulce): merge this with the main resource header values too

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it does seem weird to do it both ways.

instead we could stick to the DOM methods and just new URL(href, finalUrl) for the normalized href.

Or just use the JS version and ditch the other.

@patrickhulce patrickhulce Dec 7, 2018

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

oh we're not doing it both ways it returns the JS way, it seems I never pushed up the removal of the dead code in case people screamed bloody murder about doing it in browser-executed js :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

k. sg

@patrickhulce

Copy link
Copy Markdown
Collaborator Author

Isn't #5932 pointing out we tell the user to preconnect even if they already are?

Yes, but there's a common case where they have added it incorrectly so it's not being used (i.e. missing crossorigin). It seems like we have several options

  1. Fail normally (what we do today)
  2. Remove it from the list and don't say anything more
  3. Remove it from the list and add a warning that we didn't see it being respected
  4. Fail normally and add a warning that we didn't see it being respected.

This PR is 3, but sounds like you're a fan of 2?

@paulirish paulirish left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This PR is 3, but sounds like you're a fan of 2?

Hmmm. I guess I'm asking why we say ericbidelman.com should preconnect to https://fonts.googleapis.com.. He already has the preconnect reference (with crossorigin). And it basically looks like the preconnect works? Perhaps this line isn't being satisfied, but probably should be?

// filter out all resources where origins are already resolved
UsesRelPreconnectAudit.hasAlreadyConnectedToOrigin(record) ||	          

Also unfortunately our UI for warnings in opps is kinda ugly:
image

const Gatherer = require('./gatherer');
const getElementsInDocumentString = require('../../lib/page-functions').getElementsInDocumentString;

class LinkElements extends Gatherer {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should rename the file as well.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done

async afterPass(passContext) {
const driver = passContext.driver;

// TODO(phulce): merge this with the main resource header values too

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

k. sg

return driver.evaluateAsync(`(() => {
${getElementsInDocumentString};

return getElementsInDocument('link').map(link => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's probably a difference between link elems in head vs body that will be important at some point, but for now we can ignore it.


const Gatherer = require('../gatherer');

// TODO(phulce): remove this in favor of LinkElements gatherer

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

canonical too fwiw

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this is tracked by your issue now so I'll just remove anyway :)

@brendankenny brendankenny Dec 10, 2018

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

canonical too fwiw

this is tracked by your issue now so I'll just remove anyway :)

we should split those bullet points up into the implementation steps

'Consider adding preconnect or dns-prefetch resource hints to establish early ' +
`connections to important third-party origins. [Learn more](https://developers.google.com/web/fundamentals/performance/resource-prioritization#preconnect).`,
/** 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 on the link is a common mistake when adding preconnect links. */
crossoriginWarning: 'A preconnect link was found for "{securityOrigin}" but was not used.',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's mention crossorigin here in the warning.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I feel weird calling these links. A preconnect "reference"?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@wardpeet and I discussed and he suggested removing the mention of crossorigin, I somewhat agree and given our other positions it makes some sense to keep it in the docs

Now your warning message isn't super helpful. If I read it I would add crossorigin to the preconnect tag and I'll see the same message pop up. Sometimes crossorigin helps but the urls listed in the bugs do not get fixed by slapping crossorigin on it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  • would it be terrible to do "A preconnect <link> was..." to make it clear what's being referenced?
  • if not going to mention crossorigin, does the string var need a new name?
  • "was not used" is kind of ambiguous. "was not used by the browser"? "was not able to be used by the browser"? followed? honored?
  • Is there any more hint we can give about what went wrong?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

would it be terrible

nope sounds good

if not going to mention crossorigin, does the string var need a new name?

heh, yes, but hang on one sec

is kind of ambiguous

was not used by the browser SGTM

Is there any more hint we can give about what went wrong?

This is 99% a crossorigin problem IMO, since you and Paul both are on that side and that was my first instinct as well, I think it's worth going back :) sorry @wardpeet!

@patrickhulce

Copy link
Copy Markdown
Collaborator Author

He already has the preconnect reference (with crossorigin). And it basically looks like the preconnect works?

but he doesn't? :) he's preconnecting to https://fonts.gstatic.com/ not googleapis


// And that they have the correct shape.
opportunityResults.forEach(auditResult => {
assert.strictEqual(auditResult.details.type, 'opportunity');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

any reason for this change?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yeah I failed this test while I was working and the error message you get is useless, this way it shows you what audit details you actually did get back so you know where to go fix

'Consider adding preconnect or dns-prefetch resource hints to establish early ' +
`connections to important third-party origins. [Learn more](https://developers.google.com/web/fundamentals/performance/resource-prioritization#preconnect).`,
/** 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 on the link is a common mistake when adding preconnect links. */
crossoriginWarning: 'A preconnect link was found for "{securityOrigin}" but was not used.',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  • would it be terrible to do "A preconnect <link> was..." to make it clear what's being referenced?
  • if not going to mention crossorigin, does the string var need a new name?
  • "was not used" is kind of ambiguous. "was not used by the browser"? "was not able to be used by the browser"? followed? honored?
  • Is there any more hint we can give about what went wrong?

const URL = artifacts.URL;
const settings = context.settings;
const preconnectLinks = artifacts.LinkElements.filter(el => el.rel === 'preconnect');
const preconnectOrigins = new Set(preconnectLinks.map(link => URL.getOrigin(link.href || '')));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

move these two lines down below where they're used?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

sure, done

Comment thread lighthouse-core/audits/uses-rel-preconnect.js
*/
'use strict';

const Gatherer = require('./gatherer');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we settled(ish) on .js going forward?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done

crossorigin: link.crossorigin,
};
});
})()`, {useIsolation: true});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

a rare useIsolation sighting!

rel: link.rel,
href: link.href,
as: link.as,
crossorigin: link.crossorigin,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

link.crossOrigin

(we probably want to go with the same camel case for the artifact property as well?)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

good points, maybe I shouldn't bring in attributes I'm not using yet 😆

Comment thread types/artifacts.d.ts Outdated
rel?: string
href?: string
as?: string
crossorigin?: string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

shouldn't these all be required since the browser will normalize to at least an empty string? Or we could have the gatherer explicitly drop '' in favor of undefined.

And from the spec/a quick test, crossorigin can be null | 'anonymous' | 'use-credentials' (the browser corrects anything else to `'anonymous')

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yeah, SGTM I'll change to match spec

"path": "accessibility",
},
Object {
"path": "external-resource-links",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

travis failure is just needing to update this snapshot

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done


const Gatherer = require('../gatherer');

// TODO(phulce): remove this in favor of LinkElements gatherer

@brendankenny brendankenny Dec 10, 2018

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

canonical too fwiw

this is tracked by your issue now so I'll just remove anyway :)

we should split those bullet points up into the implementation steps

@brendankenny brendankenny left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems great and love the new artifact.

Smoke testing for the gatherer will follow when the other audits are moved to use this artifact, but any chance of getting a preconnect warning smoke test as well? We have one suggested preconnect origin in preload_style.css already, maybe we could add another and a failing <link rel="preconnect"> in the preload.html test page?

@patrickhulce

Copy link
Copy Markdown
Collaborator Author

any chance of getting a preconnect warning smoke test as well?

this one is trickier because it needs new origins, are you onboard for introducing external internet dependencies into the local smoke tests? I was holding off because of that, but I think a google fonts smoketest for this would the ideal test case since it's also what I imagine is the most common real-world scenario

@brendankenny

Copy link
Copy Markdown
Contributor

this one is trickier because it needs new origins, are you onboard for introducing external internet dependencies into the local smoke tests? I was holding off because of that, but I think a google fonts smoketest for this would the ideal test case since it's also what I imagine is the most common real-world scenario

ha, maybe it is time to give up on that. We do have the two static_server ports so we can technically request from a different origin (used in e.g. fonts.html -> cors-fonts.css), but that still might not be a great setup.

An external dependency is also not as big a deal as it once was for e.g. testing on a plane since yarn smoke was split up into all its different parts, since you can route around bad internet access now.

I guess the big thing would be how flakey it would end up being. Ideally google fonts will be pretty reliable?

@patrickhulce

Copy link
Copy Markdown
Collaborator Author

We do have the two static_server ports so we can technically request from a different origin (used in e.g. fonts.html -> cors-fonts.css), but that still might not be a great setup.

Yeah but that's the one we're already using for real so we already ran out of origins to use for warnings! 😆

An external dependency is also not as big a deal as it once was

Yeah I agree here, I'll take the plunge

@brendankenny brendankenny left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

over to @paulirish

Comment thread lighthouse-cli/test/smokehouse/perf/expectations.js
Comment thread types/artifacts.d.ts Outdated
rel: string
href: string
as: string
crossOrigin: string|null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

not worth the extra specificity?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

not totally sure which direction you're suggesting going :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

'anonymous'|'use-credentials'|null instead of just string|null. It might not buy us anything (maybe if we end up with a switch statement somewhere).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

oh gotcha, sure!

@wardpeet

Copy link
Copy Markdown
Collaborator

👏 sweet, looking good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audit: false positive for preconnect/dns-prefetch suggestion

5 participants