-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
🚀♻️ Custom Elements V1 Polyfill #17205
Conversation
Incredibly excited to review this. Thank you for the hard work! |
build-system/tasks/compile.js
Outdated
@@ -295,6 +295,8 @@ function compile(entryModuleFilenames, outputDir, | |||
'!build/fake-module/src/polyfills/**/*.js', | |||
'!build/fake-polyfills/src/polyfills.js', | |||
'!src/polyfills/*.js', | |||
// TODO(prateekbh): I don't understand how to add | |||
// src/polyfills/custom-elements.js to the _needed_ polyfills. |
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.
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.
do we need to exclude src/polyfills/custom-elements.js
for the module build?
cuz last time we checked with Malte, the lifecycle without custom elements polyfills is skewed.
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.
No, we need to include it in all builds. I don’t know how to do that for the module build.
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.
By default it will be added in all.
Extra work is for exclusion
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.
Are you sure? The previous line ('!src/polyfills/*.js'
) excludes everything from polyfills directory.
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.
Oh! with your PR we wont need to shadow it anywhere else, gotcha.
best way would be to remove the above line and add a code below this
polyfillsShadowList.forEach(polyfillFile => {
srcs.push(`!src/polyfills/${polyfillFile}.js`);
})
this will exclude all but your polyfill
* resolve: function(), | ||
* }} | ||
*/ | ||
let DeferredDef; |
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 currently has no tests, does it?
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.
None yet. I've manually tested, and was going to build this into a fully fledged node module with tests there.
Note that the our regular tests are going to run with this polyfill instead of document.registerElement
. If that's not enough, I can begin the test suite in this repo and extract all of it out later?
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.
Have you actually tried running the tests with the experiment on?
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.
- https://github.com/ampproject/amphtml/pull/17205/files#diff-8f8103e0f4e0052eab727bfd5c21e090R107
- https://github.com/ampproject/amphtml/pull/17205/files#diff-1179de12a95783d46f8bf0056a156c74R28
I can change the experiment check to test for getMode().test
too, if we want to ensure all tests run with it.
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 think this is missing most integration tests that use a fully built AMP.
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.
Made it so it always runs in tests.
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.
But not yet in integration tests, right?
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.
No, all test run with the new polyfill. At the moment, this includes:
SL_Chrome_67
SL_Firefox_61
SL_Safari_11
We can enable more saucelabs browsers, but I don't think that's absolutely necessary until we're ready to take this out of opt-in experiment?
src/polyfills/custom-elements.js
Outdated
} | ||
|
||
/** | ||
* Was HTMLElement already patched this window? |
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.
Nit: missing for
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.
Done.
*/ | ||
function isPatched(win) { | ||
const tag = win.HTMLElement.toString(); | ||
return tag.indexOf('[native code]') === -1; |
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.
Is this a good check? [native code]
appears in bound fn.
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.
Not full proof, but I don't think it's likely someone will obscure the toString of the HTMLElement
constructor. I wanted to avoid doing a creation test using a subclass of the constructor, because it would require polluting the custom elements registry.
I can try thinking of a better solution if you're not comfortable with it.
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.
Magic property on polyfilled class?
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 was trying to allow PWA publishers to include a fully featured Custom Elements for their own code.
If they do, it's likely they'll patch HTMLElement
as part of the polyfill. In that case, wrapping the HTMLElement
constructor is unnecessary. But I can't do a full feature test to see if HTMLElement
will work in our case unless I register my own CE and try document.createElement
to see if it'll throw. This seemed like a good enough approach.
src/polyfills/custom-elements.js
Outdated
const lifecycleCallbacks = { | ||
'connectedCallback': null, | ||
'disconnectedCallback': null, | ||
// 'adoptedCallback': null, |
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.
Comment?
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.
These aren't actually used in AMP, so I started comment them out. I can remove these and leave TODOs instead.
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.
But we do use attribute change callback, right? And you implement it below?
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 thought so, but Bind created its own mutatedAttributesCallback
and there we no matches for attributeChangedCallback
when grepping.
src/polyfills/custom-elements.js
Outdated
*/ | ||
this.current_ = null; | ||
|
||
const observer = new win.MutationObserver(records => { |
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.
Is this everywhere we care about?
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.
Yup. IE11, Chrome 26, FF 14, Safari 7, even Samsung's "Internet".
src/polyfills/custom-elements.js
Outdated
/** | ||
* Polyfills Custom Elements v1 API | ||
* @param {!Window} win | ||
* @param {!Function} ctor |
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 badly needs docs
* resolve: function(), | ||
* }} | ||
*/ | ||
let DeferredDef; |
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.
Have you actually tried running the tests with the experiment on?
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.
Added copious comments.
* resolve: function(), | ||
* }} | ||
*/ | ||
let DeferredDef; |
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.
Made it so it always runs in tests.
src/polyfills.js
Outdated
'document-register-element/build/document-register-element.patched'; | ||
|
||
installCustomElements(self, 'auto'); | ||
if (isExperimentOn(self, 'custom-elements-v1') || getMode().test) { |
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.
You want at least one test that ensures the old still works. That is unless you are super optimistic about moving over.
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'm more worried about bugs with using native custom elements than I am with this polyfill not matching the current polyfill. Worst case we move to this one, and disable the native CE/Reflect.construct
support.
build-system/tasks/bundle-size.js
Outdated
@@ -22,7 +22,7 @@ const log = require('fancy-log'); | |||
const {getStdout} = require('../exec'); | |||
|
|||
const runtimeFile = './dist/v0.js'; | |||
const maxSize = '79.28KB'; | |||
const maxSize = '80.60KB'; |
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.
Did the maxSize increase because we are shipping both polyfills in this PR?
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.
Yup.
* This intentionally ignores "valid" higher Unicode Code Points. | ||
* https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name | ||
*/ | ||
const VALID_NAME = /^[a-z][a-z0-9._]*-[a-z0-9._-]*$/; |
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'm curious if this should be different for AMP's specific usecase. Could we only allow names matching amp '-' [a-z] (PCENChar)*
?
Anything else wouldn't be a valid AMP component.
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 would eliminate the need for an invalid names list of strings.
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'd rather leave this generic since the plan is to extract this into a node module. We can add amp-
prefix requirements at AMP's registerElement
if we want.
I'm not actually a fan of the amp- prefix on our custom elements. At least
there might be scenarios in the future where not all supported elements
have it.
…On Wed, Aug 1, 2018 at 12:16 PM Kristofer Baxter ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In src/polyfills/custom-elements.js
<#17205 (comment)>:
> +let CustomElementConstructorDef;
+
+/**
+ * @typedef {{
+ * name: string,
+ * ctor: !CustomElementConstructorDef,
+ * }}
+ */
+let CustomElementDef;
+
+/**
+ * Validates the custom element's name.
+ * This intentionally ignores "valid" higher Unicode Code Points.
+ * https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
+ */
+const VALID_NAME = /^[a-z][a-z0-9._]*-[a-z0-9._-]*$/;
This would eliminate the need for an invalid names list of strings.
—
You are receiving this because your review was requested.
Reply to this email directly, view it on GitHub
<#17205 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAFeT8XxpQDHOsT4WMU9BYhKcUJzV-P5ks5uMf6MgaJpZM4VnW8X>
.
|
Uses a custom baked Custom Elements V1 polyfill, supporting just the parts we need (
connectedCallback
,disconnectedCallback
,whenDefined
,define
) using just the features available (MutaitonObserver
) in the browsers we target (recent Chrome, Safari 7+, recent Firefox, IE11+).Preliminary size is 85% smaller than the (much more complete)
document.registerElement
polyfill (1.5kb vs 9.5kb).This supports only "autonomous custom elements" from Custom Elements V1 (not v0). This does not polyfill extending native built-in elements (we don't do this in AMP). This limited V1 use case has been implemented since Chrome 54 and Safari 10.3.
This supports using the native Custom Elements V1, if available. Both ES5 style classes and ES6 native classes are supported (but not at the same time, it's one or the other). If ES6 native classes are used (as in the upcoming AMP module build), nothing is patched. If ES5 classes are used (as in regular AMP), only
HTMLElement
is patched.If native Custom Elements V1 is not supported, this does a full patch. This includes
document.createElement
and friends, and uses aMutationObserver
to detect when nodes become connected/disconnected. In this full-patch mode, Custom Elements are not supported in the shadowy part ofShadowRoot
s (they are supported in the slots of a shadow root, aka the regular DOM).Oh, and there are no issues installing into child windows. So, fixes #16052.
I think this was a pretty good Fixit.