-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Description
Background and motivation
Currently AMP provides context API to documents within a 3rd party iframes (including amp-ad). This API provides certain metadata (URL, referrer), as well as IntersectionObserver emulation and ability to request resizing of their AMP elements.
However, the issue is that this API is only accessible within that specific window, e.g. a nested x-domain iframe has no way to use it. At the same time, certain features of that API would be very useful for rich content which might make use of the viewability information or ability to resize (e.g ads).
This is a proposal for a postMessage-based API in AMP capable of supporting communication across nested iframes without introducing the overhead of daisy-chaining the messages from window to window.
API overview
The intent is to support a subset of the functionality offered by the context API. For example master iframe is unlikely to be supported.
The core of the API will be a stable postMessage protocol. This will enable 3rd party iframes to use the API without any need to directly load AMP code in their domain. However, for ease of use we should also provide a JS library wrapping the postMessage protocol in a JS API.
Core functionality to be included:
- static metadata (e.g. canonical URL, AMP runtime version)
- requesting resize of the AMP element
- intersectionObserver emulation (i.e. viewability)
- page visibility updates
Design
Changes to AMP 3P messaging code
Current 3P messaging restrictions:
- It must come from the contentWindow associated with the iframe.
- The origin of the message must match iframe.src.
- The message must carry the correct sentinel value which is a magic number associated with the AMP release.
These are unfortunately incompatible with supporting a flexible messaging API.
- It rejects messages from nested iframes.
- The sentinel is associated with a specific AMP release and thus cannot be supplied by 3P code.
We can however rewrite the restrictions as follows:
- The message must come from the contentWindow associated with the iframe or from one of its descendant windows (i.e. iframe.contentWindow must be in its ancestor chain).
- The message must carry the correct sentinel value which is a magic number generated per 3P iframe by the AMP runtime.
The first one is simply relaxing the original restriction. The key change is the second rule, which introduces a privilege token as sentinel. This token can be passed to 3P iframes via URL fragment, enabling them to communicate with the AMP runtime regardless of how deeply nested they are. At the same time the token has to be passed explicitly into child iframes, which avoids simply escalating privileges of all descendant frames.
Sentinel token
The per-3P-iframe token can be a string of the format "%d-%d". The first number is the depth of the AMP runtime window in the window hierarchy. This will enable the nested iframes to talk directly to the AMP runtime. Most of the time it will be either 0 (the top window) or 1 (e.g. when the AMP document lives in an iframe on another page). The second number in the token is a random positive integer generated per-iframe by the AMP runtime.
This token can be passed into child iframes within the fragment part of their URL by adding [#&]amp-3p-sentinel=$SENTINEL. The AMP runtime will add it automatically for all 3P iframes it creates. Any 3P document loaded in that iframe will then be responsible for passing it to its child iframes (if desired).
Rough postMessage protocol
Following messages will be defined:
- METADATA_REQUEST (to the runtime): request for metadata (URLs, AMP version).
- METADATA_RESPONSE (from the runtime): response with the metadata.
- RESIZE_REQUEST (to the runtime): request to resize the AMP element.
- RESIZE_RESPONSE (from the runtime): either success or failure of the prior request
- VIEWABILITY_START (to the runtime): start sending IntersectionObserver emulation messages. Runtime will need to keep track of windows that requested the updates for a specific 3P iframe.
- VIEWABILITY_STOP (to the runtime): stop sending viewability messages to the source window.
- VIEWABILITY_UPDATE (from the runtime): IntersectionObserver changes, a copy is sent to each window that registered interest for a given 3P iframe.
- PAGE_VISIBILITY_START (to the runtime): start sending page visibility updates to the source window.
- PAGE_VISIBILITY_STOP (to the runtime): stop sending page visibility updates to the source window.
- PAGE_VISIBILITY_UPDATE (from the runtime): page visibility update, sent to each window that registered interest for a given 3P iframe.
Note that it is possible for multiple descendant iframes within a single 3P AMP iframe to register interest in viewability or page visibility. The runtime will need to keep track of their windows and send update messages to all of them.
Rough JS API
We would also provide a JS library as a wrapper around the postMessage API which exposes a simpler API.
- ampContext.observeIntersection(): as in context
- ampContext.observePageVisibility(): equivalent to amp:visibilitychange events
- ampContext.requestResize(): as in context
- referrer, location, canonicalUrl: metadata properties on ampContext
We can allow passing the sentinel explicitly via window.amp3pSentinel, otherwise the library will try to read it from the URL fragment ([&#]amp-3p-sentinel=(\d+-\d+)).
Finally, we will need to support async loading of this API. Perhaps an event we could fire on the window once it is available (i.e. the script was loaded, executed, fetched the metadata via a postMessage and created window.ampContext object).
As a side note, it seems a bit messy that page visibility uses custom events, viewability uses callback functions passed as arguments and resizing the iframe uses callback functions set as properties of the context object. I do wonder whether we could settle on something more coherent.
Implementation plan
The general implementation would proceed in three stages:
- Implement the changes to 3P messaging code, this includes switching existing 3P messaging used by context API to the new code. This does not include the new API and should be a purely internal change.
- Implement the new message protocol. This includes porting the existing context to use it where appropriate to avoid duplication of code.
- Implement the JS wrapper around the postMessage API. This is an entirely new script that will be served from AMP CDN and should not require further changes to the runtime itself.
The exact details of stages 2 and 3 will be worked out in due course (e.g. exact format of the messages).