-
Notifications
You must be signed in to change notification settings - Fork 193
detect props that aren't writable, fixes emberjs/ember.js#11221 #353
Conversation
function isSettable(element, attrName) { | ||
var desc = getOwnPropertyDescriptor(element.constructor.prototype, attrName); | ||
if (!desc) { return true; } | ||
if (!desc.writable || !desc.hasOwnProperty('value') && typeof desc.set !== 'function') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the confusing and important part. We can change the "effective value" of a property (e.g. what we would get back if we accessed it) if it is writable OR it doesn't have a value
AND has a set
function. Browser spec guarantees that we will never get back a descriptor that has both a value
and a set
. Always mutually exclusive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Property descriptors present in objects come in two main flavors: data descriptors and accessor descriptors. A data descriptor is a property that has a value, which may or may not be writable. An accessor descriptor is a property described by a getter-setter pair of functions. A descriptor must be one of these two flavors; it cannot be both. ~ Object.defineProperty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note, while writing the tests, I just realized the first part !desc.writable
isn't specific enough because of implicit boolean conversion of undefined
. it needs to be desc.writable === false
this may be better then: #352 but I wonder what examples beyond Can you share some further examples to help me understand this? |
Absolutely. While I wouldn't be surprised if there were more prop/attr pairs in the spec with this behavior, my major concern is indeed custom elements. class XPerson extends HTMLElement {
@readonly
age = 33;
}
document.registerElement('x-person', XPerson); <x-person age={{someAge}}></x-person> When I write his out, it actually indeed makes me wonder if properties truly marked as readonly via I can see cases both ways...So I'm unsure. |
Summary: Do we need a distinction between "effectively readonly" and AFAIK we can always |
@@ -2,6 +2,9 @@ export function isAttrRemovalValue(value) { | |||
return value === null || value === undefined; | |||
} | |||
|
|||
var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you confirm this works on IE8 (which we still have to support)?
https://msdn.microsoft.com/library/dd548686%28v=vs.94%29.aspx
As per that article, getOwnPropertyDescroptor does work in IE8 for "DOM Objects", so it may be perfectly fine, I just want to confirm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking more about this: we have IE8 in CI so if tests are green (and we are testing this path), we are good.
the questions is, should it? Or should it be an additional whitelist |
@stefanpenner If I'm following that thread correctly, IMO that's an unrelated case of "prop doesn't always reflect to attribute". The code for that might be in the same place, but unrelated concepts. |
Okay, I think I understand better now, the issue is actually the fact that is errors, not that it doesn't reflect. (still unrelated, but yeah, fix is in the same code path. I'm attempting a PR right now) |
some funkyness for sure. |
Can haz rebase? |
} | ||
} | ||
propertyCaches[tagName] = cache; | ||
} | ||
|
||
// presumes that the attrName has been lowercased. | ||
var value = cache[attrName]; | ||
var value = cache[attrName.toLowerCase()]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need @mixonic to chime in why before, this "presumed" attrName was lowercased, when in fact it never was (at least in my testing). From what I could tell, the cache always missed when the attrName !== attrName.toLowerCase()
. Was that for some reason intended?
15944de
to
665b64a
Compare
// assign anything. Both cases we will defer to setAttribute instead | ||
var desc = getPropertyDescriptor(element, attrName); | ||
if (!desc) { return true; } | ||
if (desc.writable === false || !desc.hasOwnProperty('value') && typeof desc.set !== 'function') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rwjblue and I are discussing this in Slack...but typeof desc.set !== 'function'
fails in Safari due to this outstanding bug: https://bugs.webkit.org/show_bug.cgi?id=49739. tl;dr Safari is the only browser that doesn't return full descriptors for host element properties.
665b64a
to
f192769
Compare
f192769
to
93e66f2
Compare
@rwjblue @stefanpenner @mixonic Given the discovery here #353 (comment), I've updated the solution to exclusively use a blacklist approach for all native elements. For Custom Elements, the robust solution is used for "ideal" interop. Whether or not setting the attribute when the property is not settable for Custom Elements is the right thing to do (vs. erroring or just not doing anything) is a bigger question of how third party web component libs like polymer, graffiti, etc expect things. Please review and provide thoughts. |
To further complicate things...in some ways we're going down the same path that React does. Check this out: https://github.com/facebook/react/blob/4c3e9650ba6c9ea90956a08542d9fa9b5d72ee88/src/renderers/dom/shared/HTMLDOMPropertyConfig.js |
Find me at the meetup tonight. I believe we have a much better long term fix. Although this may still be a nice interim solution. |
Any updates on this? :) |
@Robdel12 My latest understanding is that the fundamental way we choose whether we use the I'm not the authority on this, just my understanding of it. The idea is that I'm personally in the camp of "this is going to confuse the hell out of people" because: previously we didn't really make it clear to devs that we were doing a props first, setAttribute second to begin with, most devs I've queried understandably don't realize there's a different between properties and attributes (I'm not joking), and maybe it won't be clear when to quote your 'staches and when not to. I'm super attached to this subject though, so I wouldn't be surprised if I was wrong about one or all of my concerns. I think it needs community input, likely an RFC. It's a tough problem because in almost every case we can choose props first and win, the dev doesn't need to know or care about the internals (which is why it was done) but these edge cases are brutal. tl;drI hope this PR gets merged so people can move on, at least for now. |
Thanks for the update! That helped me understand what's going on here. |
I don't know if it helps, but here's a JSBin that shows that
|
Superseded by #374 |
Cc/ @stefanpenner
Working on tests, but want your input on the implementation while I do...