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

Allow invocation of arbitrary 3p methods inside iframes using standard AMP actions. #9074

Closed
aghassemi opened this issue May 1, 2017 · 30 comments

Comments

@aghassemi
Copy link
Contributor

aghassemi commented May 1, 2017

Sample use-case

<amp-iframe src="some-map-embed.html" id="mapEmbed"></amp-iframe>
<button on="tap:mapEmbed.callMethod(method="changeLocation", data="Toronto")>
     Show Toronto's Map
</button>

inside some-map-embed.html:

context.onMethodCall(methodName, data) {
...
}

context is AMP Context

The real use-case that motivated this FR is using iframes to handle Web Payments. Ideally the iframe that uses the Web Payment API to show the native payment sheet is already loaded and when users taps a "buy" button on the AMP page, it can just send a request to the already-loaded iframe to show the payment sheet (as opposed to the alternative slow approach of loading the iframe when "buy" is tapped)

another goal is to support more interactivity between parent page and iframed content. Would be useful for highly interactive journalism (scroll-driven storytelling, data visualizations toggled by page-level filters), but is expected to have a wide range of applications beyond that.

Might also consider taking arbitrary messages passed * out of * amp-iframe, but would need to make sure these aren't executed as code in any way that violates the assumptions of AMP

/cc @cramforce @dvoytenko @rudygalfi @ericlindley-g

@cramforce
Copy link
Member

cramforce commented May 2, 2017 via email

@aghassemi
Copy link
Contributor Author

@lannka mentioned we compile amp context to an external JS file that can included in any 3p page. His opinion (which I really like) is to use this JS object to add APIs for AMP and 3P communication instead of relying on documented postMessage formats.

on a related note, @lannka we should start namespacing amp context object. we don't want everything at top level.

@lannka
Copy link
Contributor

lannka commented May 2, 2017

This totally makes sense to me. It's very like our existing API such as context.observeIntersection(callback).

Just want to point out that the proposal is only one way communication, i.e. it does not wait for a response. Would that limit the usages? Of course, designing a 2-way communication will be quite complicated, and probably would have involved amp-bind.

Re namespacing:

Right now, the existing APIs are still quite generic. We can start scoping the APIs once we have specific domain such as video iframe.

Here is a full list of existing APIs:

Static data:

    this.canary = context.canary;
    this.canonicalUrl = context.canonicalUrl;
    this.clientId = context.clientId;
    this.container = context.container;
    this.data = context.tagName;
    this.hidden = context.hidden;
    this.initialLayoutRect = context.initialLayoutRect;
    this.initialIntersection = context.initialIntersection;
    this.location = context.location;
    this.mode = context.mode;
    this.pageViewId = context.pageViewId;
    this.referrer = context.referrer;
    this.sentinel = context.sentinel;
    this.sourceUrl = context.sourceUrl;
    this.startTime = context.startTime;
    this.tagName = context.tagName;

Methods:

onPageVisibilityChange(callback)
observeIntersection(callback) 
requestResize(width, height)
onResizeSuccess(callback)
onResizeDenied(callback)
addContextToIframe(iframe)
noContentAvailable()

@dreamofabear
Copy link

Cool feature! A couple thoughts:

  • It's not clear to me that using AmpContext is a win here. Besides the origin check, what else is gained? Does AmpContext add version skew risk?
  • Let's use postMessage to avoid name collisions with global actions like show.

A syntax idea:

<!-- Without amp-bind. -->
<button on="tap:myIframe.postMessage(method='myMethod', arg1=123)">

<!-- With amp-bind (to reference state variables). -->
<button on="tap:myIframe.postMessage({method: 'myMethod', arg1: myAmpState.foo})">

This only considers AMP -> iframe communication. I'm not sure that the opposite direction (iframe -> AMP) is safe for non-trivial APIs, e.g. clicking a button in an interactive graphic and showing a popup in the AMP page.

@cramforce
Copy link
Member

If we do postMessage then targetOrigin should be required.

@dreamofabear
Copy link

Thoughts on iframe -> AMP messaging:

  • Can't detect user gestures within cross-domain iframe, so postMessage event should be low-trust (like scroll events).
  • However, AMP.setState is a high-trust action. Add new low-trust variant that only mutates children of amp-iframe and doesn't allow resizing (similar to amp-list).

More optional constraints:

  • Restrict postMessage event to only fire while amp-iframe has focus?
  • Only allow postMessage from original origin? Disallow new origins via [src] binding to avoid risky use of UGC.
<amp-iframe on="postMessage:AMP.setState({
  foo: 'Message ' + event.method + ' received with args: ' + event.arg1,
})">
  <p [text]="foo">Waiting for message from amp-iframe...</p>
</amp-iframe>

<!-- Not a child of amp-iframe, so will not mutate on postMessage event. -->
<p [text]="foo">But this will change on the next high-trust AMP.setState.</p>

Any other abuse concerns? Will probably need security review.

@cramforce
Copy link
Member

cramforce commented Sep 11, 2017 via email

@ericlindley-g
Copy link
Contributor

@choumx 's proposal sounds good (if I understand correctly, this would offer enough flexibility that developers could do something like a complex seat picker—even if it adds an extra tap outside of the iframe for the rest of the page to reflect seat choices made in the iframe)

The additional origin restriction sounds good, too, as well as explicitly configuring source and target origins, and security review.

Could take this to design review as well to get more folks thinking about any potential vectors for bad UX

@aghassemi
Copy link
Contributor Author

aghassemi commented Sep 12, 2017

@choumx Feature Malte is talking about is this https://bugs.chromium.org/p/chromium/issues/detail?id=621631 , I put another comment in there asking to see if anyone can think of a decent, light hack for detecting user initiated actions.

For low-trust version of AMP.setState, I like the idea but what I had in mind is a bit different:

I was imagining assigning low-high trust values to attributes similar to how you did for actions. So when a low-trust event does setState, mutation happens only for low-trust attributes (e.g. value of input or disabled but not class or text). Restricting to only mutating children of amp-iframe can still have bad UX effects as components don't really create a sandbox around their children.

@dreamofabear
Copy link

@aghassemi Cool, thanks for the crbug reference.

[text] would be nice to update a label, e.g. "Selected seat A in row 43", and shouldn't cause relayout if it's a child of the i-amphtml-fill-content element. [class] could cause funky stuff, e.g. changing to position: fixed, but this is already possible with amp-list.

This is nice-to-have anyways. For MVP, we could just have zero-mutation low-trust AMP.setState and require the iframe itself contain any such labels.

@aghassemi
Copy link
Contributor Author

I agree that for MVP, we should just do no-mutation, just merge state viasetState.

Issue with only mutating children of amp-iframe is that children get overlayed by iframe anyway, if z-index is used to bring them to top of frame, I don't believe it handles many use-cases even for seat selection. If updating text that is overlayed on top of the iframe is the issue, they could actually integrated that with the iframe page itself, chances are they need to update another part of the page (total price somewhere else, or value of a hidden input outside of amp-iframe).

We definitely have some loopholes in our protections (amp list you mentioned, also CSS animations) shhh.. :) but I guess there is the performance aspect of the protection too, without concept of low-trust mutations, postMessage can cause list, iframe src changes triggering network calls if those are children of the iframe.

The right solution for this is really just https://bugs.chromium.org/p/chromium/issues/detail?id=621631 :(

@cramforce
Copy link
Member

cramforce commented Sep 14, 2017 via email

@dvoytenko
Copy link
Contributor

Non-audible tracks sometimes still mute other tracks on the phone, so could be pretty negative UX. I previously used copy-paste, but that (a) not possible to attribute to any one particular iframe; (b) also bad UX due to lost copy buffer from before.

@dreamofabear
Copy link

This is currently blocked on #12498.

@dreamofabear
Copy link

Unfortunately this feature has been disabled pending some investigation into alternate solutions to detect user gestures in iframes: #15617

@Makac
Copy link

Makac commented Aug 28, 2018

@choumx, I notice that iframe-messaging is still available on the Experiments page. Is this intentional? If so is there any documentation available for this feature?

@oste
Copy link
Contributor

oste commented Nov 26, 2018

This would also be useful for amp-ad. My use case is to scroll the user to the top of the amp-ad when they click a button inside the iframe. Posting a message to the parent page and scrolling to a div just above the amp-ad seems like it would work.

@devvercer
Copy link
Contributor

Should this issue be reopend? Or replaced with another feature?

@aghassemi
Copy link
Contributor Author

re-opening. User intent flag is being implement in web platform which would allow us to bring this feature back at some point.

@chasefinch
Copy link

Is the Chrome update that would enable interaction detection still pending? If so, +1 vote to add the low-trust ability to listen for messages and merge state only, in the meantime.

@chasefinch
Copy link

The AMP -> iframe case can already be accomplished by binding iframe [src] to a URL with a hash component. Update only the hash component, and the iframe doc will receive a hashchange event, and can parse the data from the new hash.

@stale
Copy link

stale bot commented Dec 2, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions.

@stale stale bot added the Stale Inactive for one year or more label Dec 2, 2021
@stale stale bot closed this as completed Dec 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests