Provide a helper method waiting for HTMLElement upgrade to CustomElement#9542
Provide a helper method waiting for HTMLElement upgrade to CustomElement#9542zhouyx merged 4 commits intoampproject:masterfrom
Conversation
src/dom.js
Outdated
| * Return a promise that resolve when an AMP element upgrade from HTMLElement | ||
| * to CustomElement | ||
| * @param {!Element} element | ||
| * @return {!Promise} |
There was a problem hiding this comment.
Let's do !Promise<!Element> for convenience.
src/dom.js
Outdated
| * @param {!Element} element | ||
| * @return {!Promise} | ||
| */ | ||
| export function whenUpgradeToCustomElement(element) { |
There was a problem hiding this comment.
"whenUpgrade_d_ToCustomElement"
src/dom.js
Outdated
| return Promise.resolve(); | ||
| } | ||
| // If Element is still HTMLElement, wait for it to upgrade to customElement | ||
| if (!element.upgradeToCustomElementPromise_) { |
There was a problem hiding this comment.
This won't work unfortunately like this. Obfuscation will break between versions. Probably will have to work with "pure" strings.
There was a problem hiding this comment.
Changed to element['upgradedToCustomElementPromise']. But I still don't get why element.upgradeToCustomElementPromise_ will break. I tested locally and it works fine before 😕
There was a problem hiding this comment.
Thanks @dvoytenko for the explanation!! I would never be able to find out myself 😄
src/custom-element.js
Outdated
| /** @private {?./layout-delay-meter.LayoutDelayMeter} */ | ||
| this.layoutDelayMeter_ = null; | ||
|
|
||
| if (this.whenUpgradeToCustomElement_) { |
There was a problem hiding this comment.
See below, re: strings instead of private properties.
There was a problem hiding this comment.
changed to this['whenUpgradedToCustomElement'], if this is what you mean here.
There was a problem hiding this comment.
Our current format is smth like __AMP_NAME_OF_FIELD. Let's keep to that format.
There was a problem hiding this comment.
changed to __AMP_UPGRADE_CUS_ELE_PROMISE__ like what we did in action-imp.js
There was a problem hiding this comment.
Sorry to bikeshed. Let's just call __AMP_UPG_PRM and __AMP_UPG_RES.
| if (!ad.whenBuilt) { | ||
| // TODO(@zhouyx, #9126): Cleanup once make sure the fix works | ||
| dev().error(TAG, 'element whenBuilt still do not exist!'); | ||
| timerFor(this.win).delay(() => { |
There was a problem hiding this comment.
This logic can be flattened:
whenUpgradedToCustomElement().then(ad =>
if (ad.whenBuilt)
return ad;
}
// TODO
return timerFor(this.win).promise(1000).then(() => ad);
}).then(ad => {
return ad.whenBuilt();
}).then(() => {
this.mutateElement(() => toggle(this.element, true));
});There was a problem hiding this comment.
I prefer the if/else loop since I am pretty sure it won't get into the if loop. Just add it for extra check, will remove once confirm the fix works.
There was a problem hiding this comment.
Why do we if (ad.whenBuilt)? We already await upgrade, so it's not needed. Right?
There was a problem hiding this comment.
But the duplication.... 😢
There was a problem hiding this comment.
haha, I think @dvoytenko is right. it's not needed. I already tested it and am pretty sure it fix the race condition bug in #9126. @jridgewell I deleted the duplication code 😄
| // may not be attached yet. | ||
| return startsWith(tag, 'AMP-') && | ||
| // Some "amp-*" elements are not really AMP elements. :smh: | ||
| !(tag == 'AMP-STICKY-AD-TOP-PADDING' || tag == 'AMP-BODY'); |
There was a problem hiding this comment.
Merge conflict? I thought this was already in there?
There was a problem hiding this comment.
You didn't merge that PR 😈
| */ | ||
| export function whenUpgradedToCustomElement(element) { | ||
| dev().assert(isAmpElement(element), 'element is not AmpElement'); | ||
| if (element.createdCallback) { |
There was a problem hiding this comment.
Use __AMP_RESOURCE, like Resources does.
There was a problem hiding this comment.
Does a customElement guaranteed to have a resource at before createdCallback being called?
There was a problem hiding this comment.
Not guaranteed. Adding it to DOM can happen much much later. So, I think, createdCallback is a good idea.
src/custom-element.js
Outdated
|
|
||
| if (this['whenUpgradedToCustomElement']) { | ||
| this['whenUpgradedToCustomElement'](this); | ||
| this['whenUpgradedToCustomElement'] = null; |
There was a problem hiding this comment.
Unset the promise, too.
There was a problem hiding this comment.
what promise? element['upgradedToCustomElementPromise']? I am returning it?
There was a problem hiding this comment.
Agree. Probably better to clear the promise out.
There was a problem hiding this comment.
set element['promise'] to null. To confirm when I do return element['promise'] it is not returning a reference to the promise? So setting element['promise'] = null later doesn't affect the returned promise from #whenUpgradedToCustomElement function.
There was a problem hiding this comment.
Shouldn't go to null, but rather delete this['ABC'].
| this.mutateElement(() => { | ||
| toggle(this.element, true); | ||
| }); | ||
| ad.whenBuilt().then(() => { |
There was a problem hiding this comment.
We can unnest:
then(() => {
return ad.whenBuilt();
}).then(() => {
this.mutateElement(() => toggle(this.element, true));
});There was a problem hiding this comment.
sorry I didn't get it, but what's the need to unnest here?
There was a problem hiding this comment.
It's JS style when using Promises (we needed promises because of callback hell). When using promises, instead of chaining a bunch of #then calls inside a then block, you return the promise instead.
then(() => {
return ad.whenBuilt().then(() => {
// this is callback hell
});
});
//vs
then(() => {
return ad.whenBuilt();
}).then(() => {
// left indent is the same, so callback hell is avoided.
});There was a problem hiding this comment.
I see, changed! Thanks for the explanation! However I don't think we have a style requirement for this, and I can find the second usage in a lot of places😢
For #9126