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(preconnect): add warning if preconnect link is not used #6694

Merged
merged 12 commits into from
Dec 13, 2018

Conversation

patrickhulce
Copy link
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

@wardpeet
Copy link
Collaborator

wardpeet commented Dec 2, 2018

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
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
Copy link
Collaborator

wardpeet commented Dec 4, 2018

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
Copy link
Member

@paulirish paulirish left a comment

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
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.

Copy link
Collaborator Author

@patrickhulce patrickhulce Dec 7, 2018

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
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
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?

Copy link
Member

@paulirish paulirish left a comment

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
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
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
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
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.

@@ -7,6 +7,8 @@

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

// TODO(phulce): remove this in favor of LinkElements gatherer
Copy link
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
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 :)

Copy link
Member

@brendankenny brendankenny Dec 10, 2018

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

@@ -28,6 +29,8 @@ const UIStrings = {
description:
'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
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
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
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
Member

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
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
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

@@ -30,7 +30,7 @@ describe('Default Config', () => {

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

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
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

@@ -28,6 +29,8 @@ const UIStrings = {
description:
'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
Member

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 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
Member

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
Collaborator Author

Choose a reason for hiding this comment

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

sure, done

lighthouse-core/audits/uses-rel-preconnect.js Show resolved Hide resolved
*/
'use strict';

const Gatherer = require('./gatherer');
Copy link
Member

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
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
Member

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
Member

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
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 😆

rel?: string
href?: string
as?: string
crossorigin?: string
Copy link
Member

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
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

@@ -1063,6 +1063,9 @@ Object {
Object {
"path": "accessibility",
},
Object {
"path": "external-resource-links",
Copy link
Member

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
Collaborator Author

Choose a reason for hiding this comment

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

done

@@ -7,6 +7,8 @@

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

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

@brendankenny brendankenny Dec 10, 2018

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

Copy link
Member

@brendankenny brendankenny left a comment

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
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
Member

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
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

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

LGTM!

over to @paulirish

rel: string
href: string
as: string
crossOrigin: string|null
Copy link
Member

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
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
Member

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
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
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