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

✨amp-consent: support external consent flow #15805

Merged
merged 5 commits into from Jun 20, 2018

Conversation

zhouyx
Copy link
Contributor

@zhouyx zhouyx commented Jun 4, 2018

Support external consent flow using <amp-iframe> and send consent response back using postMessages.

cc @choumx @rsimha

@@ -76,6 +79,9 @@ describes.realWin('amp-iframe', {
}
});
setTrackingIframeTimeoutForTesting(20);
setTrackingIframeCountForTesting(0);
sandbox.stub(error, 'reportError');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rsimha I encountered the issue in #15658 as well.
However the test is intentionally trying to test the misconfiguration. I used allowConsoleError, but that doesn't work because user().assert() call #reportError(), and that will rethrow the error again async.
This is my temporarily solution to fix this, stub #reportError(), I think a more general solution would be to stub #reportError() for all tests. WDYT

Copy link
Contributor

Choose a reason for hiding this comment

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

You can use expectAsyncConsoleError(<message>) for async errors. Did you try that?

It's explained in the failure message.

Copy link
Contributor

Choose a reason for hiding this comment

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

I hesitate to stub out reportError since several tests actually check if it fires. However, we could make it so that the error is thrown synchronously during tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

And finally, if a mocha test does something asynchronous, it is responsible for calling the done() callback to indicate that the test has completed. Unfortunately, a lot of our tests fail to do so.

Copy link
Contributor Author

@zhouyx zhouyx Jun 4, 2018

Choose a reason for hiding this comment

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

You can use expectAsyncConsoleError() for async errors. Did you try that?

I tried that. It didn't work because I didn't call done() and the test ended earlier.

I think it will work if I call the #done() callback. But the problem is that I don't know where to call the done() callback if I can't stub the #reportError(). Can I do yield expectAsyncConsoleError(message).

The test logic is super simple, check if a misconfiguration results in a function not being called eventually. I don't think it makes much sense to have these unit tests figure out the logic behind #assert() and call #done() callback to not break tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Talked offline. Will leave the stub there until we figure out a nice general solution.

Copy link
Contributor

Choose a reason for hiding this comment

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

Meanwhile, #16005 should fix the layoutCallback issues we were discussing here.

@zhouyx zhouyx force-pushed the amp-consent-external branch 2 times, most recently from a75eb18 to 346cc1f Compare June 4, 2018 19:41
@@ -821,7 +861,8 @@ describes.realWin('amp-iframe', {
expect(addEventListener).to.not.be.called;
});

it('should receive "message" events from <iframe>', function*() {
// TODO (@choumx): Unskip the test
it.skip('should receive "message" events from <iframe>', function*() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@choumx I can't fix this test : ( Skip it for now

Choose a reason for hiding this comment

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

Thanks for the heads up.

@@ -163,6 +172,11 @@ export class AmpIframe extends AMP.BaseElement {

/** @private */
assertPosition_() {
if (this.consentContainer_) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should keep amp-iframe empty of any logic related to other elements.
Two suggestions:
1- Is there an issue with using the normal must provide a placeholder instead of changing the assertPosition logic? Presence of placeholder can be enforced by amp-consent validation rules (e.g. have a rule that amp-iframe child of amp-consent must have a placeholder)
2- Created a generic list of messages to forward in amp-iframe. Have amp-iframe blindly dispatch an event when it receives one of those messages. For now the list would only contain external-consent-flow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure about the mandatory placeholder. This is a pop up iframe, it doesn't much sense to require a placeholder for it.
I can blindly dispatch the event, but it still needs to be laid out to do so. I would still need to check position of the iframe, which requires me to check if the iframe is within an <amp-consent> component.

Copy link
Contributor

@aghassemi aghassemi Jun 7, 2018

Choose a reason for hiding this comment

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

let's keep dependencies one-way and only from amp-consent -> amp-iframe. If mandatory placeholder does not make sense, alternatively amp-consent can inject a placeholder ( if non exists ) during its build callback into decesendant amp-iframe.

Apologies for pushing this, intertwined dependencies can really make the code complex and as AMP codebase grows we need to watch out for code complexity as much as possible

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Talked offline. We need a smarter API to tell <amp-iframe> whether it is OK to be placed in first viewport. For example, it is sort of a bug for <amp-iframe> within <amp-lightbox>. Even though, it requires user interaction to open the lightbox, an <amp-iframe> within it still requires a placeholder to be laid out.

For this PR. We agreed that we should always require a placeholder for <amp-iframe> within <amp-consent>, because these iframes can be prompted without user interaction, and it makes sense to always include a placeholder to prevent blank space.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@aghassemi dependency removed. <amp-iframe> will only listen to consent-response msg, and blindly dispatch event to the window.

Unfortunately I can't have <amp-consent> directly listen to that postMessage, because it requires <amp-consent> to know the exact iframe to listen to. And there's not API method for that.

@zhouyx
Copy link
Contributor Author

zhouyx commented Jun 9, 2018

@aghassemi

@lannka has an even stronger opinion about the dependency here 😄....
He didn't want me to add a single line of code to amp-iframe. After we talked offline, here's what he proposed. @lannka Could you confirm this is what you want?
Having amp-consent do

win.addEventListener('message', event => {
  triggerWindow = event.source
  iframes = ampConsent.element.querySelectorAll('iframes')
  for each iframe:
    if (iframe.contentWindow == triggerWindow) {
     handleAction(event.action) 
     return
   }
}

My only concern is that register event listener within individual component is not optional and might be expensive, especially given that amp-consent needs to query all its child iframe elements here to check source. But if everyone is happy with this approach, I am happy to switch : )

@zhouyx
Copy link
Contributor Author

zhouyx commented Jun 16, 2018

@lannka @aghassemi
While I was making the change, I realize one thing.
At somepoint, the <amp-iframe> will require the consentId from the parent <amp-consent> element even though we don't support it right away. What would be the best way to support the two way messages then? Would it influence the decision we made here?

@aghassemi
Copy link
Contributor

@zhouyx I assume the iframe would first request the consentId and then receive it? In which case, amp-consent can keep a reference to the iframe window when it receives a request message (there will be a pointer to the window on the message event) and post directly do it with the response.

Copy link
Contributor

@lannka lannka left a comment

Choose a reason for hiding this comment

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

As discussed offline, please remove consentId and sentinel in post message

}

document.querySelector('#a').onclick = function () {
// parent.postMessage("message to be sent", "http://the-website-that-will-receive-the-msg.com")
Copy link
Contributor

Choose a reason for hiding this comment

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

bad indentation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed

@zhouyx
Copy link
Contributor Author

zhouyx commented Jun 20, 2018

As discussed offline. It's really difficult to define the most ideal behaviors here. We decided to add minimal support also put minimum restrictions at first stage. Will expand the functionality if advanced features are required later on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants