Skip to content

🐛 Register LINKER_CREATED on ampdoc instead of attribute#28268

Merged
zhouyx merged 5 commits intoampproject:masterfrom
zhouyx:linker-shadowroot
May 9, 2020
Merged

🐛 Register LINKER_CREATED on ampdoc instead of attribute#28268
zhouyx merged 5 commits intoampproject:masterfrom
zhouyx:linker-shadowroot

Conversation

@zhouyx
Copy link
Copy Markdown
Contributor

@zhouyx zhouyx commented May 8, 2020

To fix #28220.

TBH I still don't know what's causing the error. But the purpose of the check is to only initiate the linker manager once for each ampdoc. No need to check for ShadowRoot, the attribute can be applied to the document.body.

@jridgewell
Copy link
Copy Markdown
Contributor

ShadowRoot isn't defined in old Chrome, so trying to reference it throws a ReferenceError.

But we shouldn't be using attributes as markers. It seems we have a need to mark arbitrary data on the ampdoc instance (registerTrackingIframe). Maybe we should generalize that into object properties.

@zhouyx
Copy link
Copy Markdown
Contributor Author

zhouyx commented May 8, 2020

ShadowRoot isn't defined in old Chrome, so trying to reference it throws a ReferenceError.

Thanks! That's good to know. However based on #28220, the error appears very recently despite that the code was merged back in Feb.

But we shouldn't be using attributes as markers. It seems we have a need to mark arbitrary data on the ampdoc instance (registerTrackingIframe). Maybe we should generalize that into object properties.

I like the idea! Will generalize the solution.

@jridgewell
Copy link
Copy Markdown
Contributor

This is an async error being caught by a promise. If there's no catch handler in the promise chain, then usually we'd get a unhandledrejection error event. But, because not everything uses crossorigin=anonymous, unhandledrejection errors get dropped! We'd never receive the error report.

Now, https://github.com/ampproject/amphtml/blame/c985e344b25effca5e8f2a604a3cef11eff6c0d3/extensions/amp-analytics/0.1/amp-analytics.js#L587 breaks the initialization out of the promise chain. It's now happening in chunked event loop. We have wrappers around these event loops that can catch the error and report it properly, even when the error comes from a non-anonymous script.

So, I think the bug was always there, we just never received the reports.

@mdmower
Copy link
Copy Markdown
Contributor

mdmower commented May 8, 2020

Oh, I remember this (#26683). Sorry, headNode instanceof ShadowRoot isn't the most compatible solution. To give some context, attribute i-amphtml-linker-created is added to <meta name="amp-google-client-id-api" content="googleanalytics"> when LinkerManager inits. Since this meta tag is not scoped to AmpDocShadow instances, I opted to attach the the attribute to <body>.

I like that you plan to abandon use of this attribute as a marker. If you need a quick fix in the meantime...

--- a/extensions/amp-analytics/0.1/linker-manager.js
+++ b/extensions/amp-analytics/0.1/linker-manager.js
@@ -234,7 +234,7 @@ export class LinkerManager {
 
     const headNode = this.ampdoc_.getHeadNode();
     const linkerCreatedEl =
-      headNode instanceof ShadowRoot
+      headNode.toString() === '[object ShadowRoot]'
         ? this.ampdoc_.getBody()
         : headNode.querySelector(
             'meta[name="amp-google-client-id-api"][content="googleanalytics"]'

@zhouyx
Copy link
Copy Markdown
Contributor Author

zhouyx commented May 8, 2020

Thank you @jridgewell That makes a lot of sense!
I believe the error reported #28264 was surface recently for the same reason.

@zhouyx
Copy link
Copy Markdown
Contributor Author

zhouyx commented May 8, 2020

Thank all for the suggestion. I've introduced a more generic solution as Justin suggested.

* Attempt to register a service that is allowed/required one for each ampdoc.
* Caller need to handle user error when registration returns false.
*
* Please list your service name below:
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't want to add extra bytes to the runtime by keeping an enum list. But I do want to keep such a list somewhere for reference so that we don't end up with identical service name. Let me know if there's better way to address : )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Enums get eliminated if unused, and the Enum.FOO reference will be rewritten with whatever the value is if it's used. Enums are basically 0 cost. 😉

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's cool! Switched to enum

@zhouyx zhouyx requested review from jridgewell and removed request for dreamofabear May 8, 2020 19:01
Copy link
Copy Markdown
Contributor

@jridgewell jridgewell left a comment

Choose a reason for hiding this comment

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

Thanks for cleaning this up!

);

if (linkerCreatedEl.hasAttribute(LINKER_CREATED)) {
if (!this.ampdoc_.registerService(LINKER_CREATED)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: Just return this.ampdoc_.registerService(LINKER_CREATED)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍 Good point

* Attempt to register a service that is allowed/required one for each ampdoc.
* Caller need to handle user error when registration returns false.
*
* Please list your service name below:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Enums get eliminated if unused, and the Enum.FOO reference will be rewritten with whatever the value is if it's used. Enums are basically 0 cost. 😉

registerTrackingIframe() {
if (this.hasTrackingIframe_) {
return false;
registerService(name) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: I wouldn't call this service, to avoid confusion with ampdoc/window services. Maybe "singleton"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sounds good. Changed to registerSingleton()

* limitations under the License.
*/

import {AMPDOC_SINGLETON_NAME} from '../../../src/service/ampdoc-impl';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe we should move this elsewhere, with only this in the file? That way there are no transitive imports, and we don't need the presubmit whitelist?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I agreed! Any idea where to move it to. I wish there's a src/const.js file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That sounds fine to me. Or src/enums.js

@jridgewell jridgewell changed the title 🐛 Apply LINKER_CREATED attribute to ampdoc.body 🐛 Register LINKER_CREATED on ampdoc instead of attribute May 8, 2020
@zhouyx zhouyx merged commit c8caf9d into ampproject:master May 9, 2020
@zhouyx zhouyx deleted the linker-shadowroot branch May 9, 2020 01:06
* Registred singleton on AMP doc.
* @enum {number}
*/
export const AMPDOC_SINGLETON_NAME = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How about just AMPDOC_SINGLETON?

*/

/**
* Registred singleton on AMP doc.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Registered singleton on AmpDoc.

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.

🚨 Error: ShadowRoot is not defined

5 participants