Navigation Menu

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

Fix false positive hydration warning for SVG attributes #10676

Merged
merged 3 commits into from Sep 13, 2017

Conversation

gaearon
Copy link
Collaborator

@gaearon gaearon commented Sep 11, 2017

Fixes #10672.
The new test used to fail but now passes.

});

itRenders(
'svg child element with a namespace attribute',
Copy link
Collaborator Author

@gaearon gaearon Sep 11, 2017

Choose a reason for hiding this comment

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

This is an old test. I just renamed it and moved down.

@gaearon
Copy link
Collaborator Author

gaearon commented Sep 11, 2017

@nhunzaker Are there any other cases where this could happen? Do you remember why it didn't use name in the first place? Should I worry about this not being lowercase?

@nhunzaker
Copy link
Contributor

I do not know why name was not used. I think it is bug. I think we also want to lowercase the spot you've linked because SVG attribute names have casing.

@@ -912,7 +912,7 @@ var ReactDOMFiberComponent = {
case 'selected':
break;
default:
extraAttributeNames.add(attributes[i].name);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This was intentional so that you would compare and display it in the case that it was received. For things like SVG where case matters.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think this is right because on SVG we do want things like <svg foobar="" /> vs. <svg fooBar="" /> to show up as an extra attribute.

It might make sense to deal with this at the same time as the "all attributes should be lower-case" warning though.

Copy link
Contributor

@nhunzaker nhunzaker Sep 11, 2017

Choose a reason for hiding this comment

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

I am not sure that we can, unless we can control for browsers that report casing from attribute.name inconsistently. For example, IE Edge reports SVG fill and `stroke in all caps:

Test: https://codepen.io/nhunzaker/pen/qXRPvY

Screenshot of IE Edge 15:

screen shot 2017-09-11 at 6 47 29 pm

Screenshot of Chrome:

screen shot 2017-09-11 at 6 48 30 pm

Copy link
Collaborator

Choose a reason for hiding this comment

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

@nhunzaker Can we special case the known ones since they will also have the warning about invalid case of the prop?

Copy link
Contributor

@nhunzaker nhunzaker Sep 12, 2017

Choose a reason for hiding this comment

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

I can start to compile a list of special cases. I'm sort of worried that this will be tedious and it'll end up being most of SVG, but I don't know that yet. I know of differences in casing of SVG in IE Edge. I didn't know about the cases alluded to in Chrome.

Should I just open up the attribute table in every browser using BrowserStack? How many versions back should I go?

Kind of makes me wonder if it could be automated using their remote API.


I have a few questions, and I apologize in advance if they sound cynical, but I don't want to assume anything so I can better understand the problem:

1: What is the advantage to case-sensitive hydration? Casing warnings for known attributes will raise server-side during markup generation (is this correct?), and then and on update client-side.

2: Wouldn't the same components consistently misspell attribute names server and client side?

3: Even if the casing is produced differently server/client-side, browsers don't appear to care about casing. I'm curious if this is generally true, or if my tests are flawed. They certainly aren't exhaustive.

Copy link
Collaborator

Choose a reason for hiding this comment

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

My concern is that just ignoring case doesn't take the core of the problem in to consideration because it's not the case that browsers completely ignore case. There are specific rules for it.

Specifically, the rules (other than bugs/quirks like the IE thing) is that the HTML parser of a server response makes them lower-case except for a whitelist. https://www.w3.org/TR/html5/syntax.html#adjust-svg-attributes

However, this whitelist doesn't apply to client code. Specifically cases like this is what I'm concerned about:

var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewbox', '0 0 1 1');
console.log(svg.viewBox);

var container = document.createElement('div');
container.innerHTML = '<svg viewbox="0 0 1 1"></svg>';
console.log(container.firstChild.viewBox);

Rendering the "viewbox" attribute on the server and the client behaves differently because it is case-sensitive on the client but not on the server.

So simple saying that they're not case-sensitive is not the full story. It might be the case that this is the best compromise anyway but I want to make sure we've considered all the aspects because "DOM is case-insensitive" is an oversimplification so we might be missing something.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah got it. So there are attributes that care about casing. That's frustrating...

Thank you for spelling this out for me. Prior to this point, I was not aware of the differences between HTML/SVG parsing and the DOM attribute manipulation. What a mess!

@sebmarkbage
Copy link
Collaborator

FYI, this is tangental to this PR but there are a few attributes in the attribute tables that correctly warns because they're buggy in Chrome. Chrome doesn't turn them into camelcase like it should. There are also some that are incorrectly left out of the HTML parsing spec. If we get a report for that, be careful to look into it.

@sebmarkbage
Copy link
Collaborator

sebmarkbage commented Sep 11, 2017

Oh, and if you render viewbox on the server it will be automatically turned into viewBox during hydration, but if you render it on the client that will not work because setAttribute is case sensitive.

@gaearon
Copy link
Collaborator Author

gaearon commented Sep 11, 2017

Hmm. Is new test I added wrong then? It used to fail before this PR. But it uses correct casing (viewBox) and is a common attribute.

@sebmarkbage
Copy link
Collaborator

I think your test is correct because it is a known one. However, we have to be careful when we look at these things because if it wasn't known then we'd warn that you should use lower case (if we add the lowercase warning). That would work with SSR and hydration but not if it was only rendered on the client. E.g. during an update.

Copy link
Collaborator

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

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

I think the real bug is at line 1011. https://github.com/facebook/react/pull/10676/files#diff-9756d35a2f408667aa5e1cfb065b4fdcR1011

It should do toLowerCase for HTML, but not for SVG and it probably needs to be special case aware.

@gaearon
Copy link
Collaborator Author

gaearon commented Sep 12, 2017

I started looking into it but didn’t get far. It will take me a while to understand the constraints here again. If either of you have better insight into the best fix feel free to take this; otherwise I will continue tomorrow.

I could also apply the fix in #10676 (review) but I don’t know which special cases the fix should be aware of.

@gaearon
Copy link
Collaborator Author

gaearon commented Sep 12, 2017

Pushed up a version that threads the parent namespace through for the check. Could also read it from the element. Is it enough for the most narrow fix to unblock this? We can fix edge cases with IE later.

// $FlowFixMe - Should be inferred as not undefined.
extraAttributeNames.delete(propKey.toLowerCase());
let ownNamespace = parentNamespace;
if (ownNamespace === HTML_NAMESPACE) {
Copy link
Contributor

@nhunzaker nhunzaker Sep 12, 2017

Choose a reason for hiding this comment

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

Why rename parentNamespace to ownNamespace here instead of using parentNamespace directly or setting ownNamespace as the argument?

Copy link
Contributor

Choose a reason for hiding this comment

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

Disregard. Sorry I didn't catch the code below.

}
if (ownNamespace === HTML_NAMESPACE) {
// $FlowFixMe - Should be inferred as not undefined.
extraAttributeNames.delete(propKey.toLowerCase());
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if it's worth adding a comment for why we need to downcase the propKey (because HTML reports lowercase, right?)

Copy link
Contributor

@nhunzaker nhunzaker left a comment

Choose a reason for hiding this comment

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

This makes sense to me, the only edge case I can think of is if the casing reporting behavior is inconsistent across SVG attribute names.

I would be okay with making that a follow up to this PR.

Copy link
Collaborator

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

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

Seems fine but I think we'll also need a follow up (which may very well be something case insensitive for unknown but case sensitive for known or something like that).

} else {
// $FlowFixMe - Should be inferred as not undefined.
extraAttributeNames.delete(propKey);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This made me realize that we probably don't validate that two components with the same name but different namespaces line up in the hydration. I'm not sure there's a way to get that wrong in another way than innerHTML SVG content in a HTML root.

@gaearon gaearon merged commit c55ffb3 into facebook:master Sep 13, 2017
@gaearon gaearon deleted the fix-ssr-war branch September 13, 2017 15:31
@gaearon
Copy link
Collaborator Author

gaearon commented Sep 13, 2017

Would that follow up be blocking to 16 release?

@bvaughn bvaughn mentioned this pull request Sep 14, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants