Skip to content

Commit

Permalink
✨Add amp-iframe support for Pym.js width and height resize messages (#…
Browse files Browse the repository at this point in the history
…24917)

* Add amp-iframe support for Pym.js width and height resize messages

Fixes #22714.

Pym.js supports the client window sending several types of messages, including:

* `height`
* `width`
* `parentPositionInfo`
* `navigateTo`
* `scrollToChildPos`

The only two messages that seem relevant in an AMP context are width and height, as they map to the embed-size message that AMP is currently looking for.

* Use listen event-helper instead of addEventListner directly

* Rename unlisten_ to unlistenPym_

* Use this.win instead of window

* Indicate event param is non-nullable MessageEvent

* Use getData event helper

* Warn user about unsupported Pym.js messages

* Replace function binding with arrow function to fix JSC_TYPE_MISMATCH

* Add test for Pym.js messages for height and width

* Add missing tag argument to user().warn()

* Use startsWith string helper

* Replace array splice with direct references; fix format comment doc

* Add missing ID from Pym message

* Populate undefined variable sentinel
  • Loading branch information
westonruter authored Feb 6, 2020
1 parent 7ac50af commit 39b1f61
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 6 deletions.
47 changes: 45 additions & 2 deletions extensions/amp-iframe/0.1/amp-iframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import {IntersectionObserverApi} from '../../../src/intersection-observer-polyfi
import {LayoutPriority, isLayoutSizeDefined} from '../../../src/layout';
import {Services} from '../../../src/services';
import {base64EncodeFromBytes} from '../../../src/utils/base64.js';
import {createCustomEvent, getData} from '../../../src/event-helper';
import {createCustomEvent, getData, listen} from '../../../src/event-helper';
import {devAssert, user, userAssert} from '../../../src/log';
import {dict} from '../../../src/utils/object';
import {endsWith} from '../../../src/string';
import {endsWith, startsWith} from '../../../src/string';
import {
isAdLike,
isPausable,
Expand Down Expand Up @@ -103,6 +103,9 @@ export class AmpIframe extends AMP.BaseElement {
/** @private {string} */
this.sandbox_ = '';

/** @private {Function} */
this.unlistenPym_ = null;

/**
* The source of the iframe. May change to null for tracking iframes
* to prevent them from being recreated.
Expand Down Expand Up @@ -470,6 +473,11 @@ export class AmpIframe extends AMP.BaseElement {
/*opt_allowOpaqueOrigin*/ true
);

// Listen for resize messages sent by Pym.js.
this.unlistenPym_ = listen(this.win, 'message', event => {
return this.listenForPymMessage_(/** @type {!MessageEvent} */ (event));
});

if (this.isClickToPlay_) {
listenFor(iframe, 'embed-ready', this.activateIframe_.bind(this));
}
Expand All @@ -490,6 +498,37 @@ export class AmpIframe extends AMP.BaseElement {
});
}

/**
* Listen for Pym.js messages for 'height' and 'width'.
*
* @see http://blog.apps.npr.org/pym.js/
* @param {!MessageEvent} event
* @private
*/
listenForPymMessage_(event) {
if (!this.iframe_ || event.source !== this.iframe_.contentWindow) {
return;
}
const data = getData(event);
if (typeof data !== 'string' || !startsWith(data, 'pym')) {
return;
}

// The format of the message takes the form of `pymxPYMx${id}xPYMx${type}xPYMx${message}`.
// The id is unnecessary for integration with amp-iframe; the possible types include
// 'height', 'width', 'parentPositionInfo', 'navigateTo', and 'scrollToChildPos'.
// Only the 'height' and 'width' messages are currently supported.
// See <https://github.com/nprapps/pym.js/blob/57feb68/src/pym.js#L85-L102>
const args = data.split(/xPYMx/);
if ('height' === args[2]) {
this.updateSize_(parseInt(args[3], 10), undefined);
} else if ('width' === args[2]) {
this.updateSize_(undefined, parseInt(args[3], 10));
} else {
user().warn(TAG_, `Unsupported Pym.js message: ${data}`);
}
}

/** @override */
unlayoutOnPause() {
return !this.isPausable_();
Expand Down Expand Up @@ -528,6 +567,10 @@ export class AmpIframe extends AMP.BaseElement {
* @override
**/
unlayoutCallback() {
if (this.unlistenPym_) {
this.unlistenPym_();
this.unlistenPym_ = null;
}
if (this.iframe_) {
removeElement(this.iframe_);
if (this.placeholder_) {
Expand Down
58 changes: 58 additions & 0 deletions extensions/amp-iframe/0.1/test/test-amp-iframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,64 @@ describes.realWin(
ActionTrust.HIGH
);
});

it('should listen for Pym.js height event', function*() {
const ampIframe = createAmpIframe(env, {
src: iframeSrc,
sandbox: 'allow-scripts allow-same-origin',
width: 200,
height: 200,
resizable: '',
});
yield waitForAmpIframeLayoutPromise(doc, ampIframe);
const impl = ampIframe.implementation_;
return new Promise((resolve, unusedReject) => {
impl.updateSize_ = (height, width) => {
resolve({height, width});
};
const iframe = ampIframe.querySelector('iframe');
iframe.contentWindow.postMessage(
{
sentinel: 'amp-test',
type: 'requestPymjsHeight',
height: 234,
},
'*'
);
}).then(res => {
expect(res.height).to.equal(234);
expect(res.width).to.be.an('undefined');
});
});

it('should listen for Pym.js width event', function*() {
const ampIframe = createAmpIframe(env, {
src: iframeSrc,
sandbox: 'allow-scripts allow-same-origin',
width: 200,
height: 200,
resizable: '',
});
yield waitForAmpIframeLayoutPromise(doc, ampIframe);
const impl = ampIframe.implementation_;
return new Promise((resolve, unusedReject) => {
impl.updateSize_ = (height, width) => {
resolve({height, width});
};
const iframe = ampIframe.querySelector('iframe');
iframe.contentWindow.postMessage(
{
sentinel: 'amp-test',
type: 'requestPymjsWidth',
width: 345,
},
'*'
);
}).then(res => {
expect(res.width).to.equal(345);
expect(res.height).to.be.an('undefined');
});
});
});

describe('pause/resume', () => {
Expand Down
23 changes: 19 additions & 4 deletions test/fixtures/served/iframe.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
}

window.addEventListener('message', function(event) {
var sentinel, msg;
if (event.data) {
if (event.data.type == 'requestHeight') {
var sentinel;
if (event.data.type === 'requestHeight') {
if (event.data.is3p) {
sentinel = event.data.sentinel;
} else {
Expand All @@ -40,8 +40,7 @@
height: event.data.height,
width: event.data.width,
}, '*');
} else if (event.data.type == 'subscribeToEmbedState') {
var sentinel;
} else if (event.data.type === 'subscribeToEmbedState') {
if (event.data.is3p) {
sentinel = event.data.sentinel;
} else {
Expand All @@ -52,6 +51,22 @@
sentinel: sentinel,
type: 'send-embed-state',
}, '*');
} else if (event.data.type === 'requestPymjsHeight') {
msg = [ 'pym', 'example', 'height', event.data.height ].join( 'xPYMx' );
if (event.data.is3p) {
sentinel = event.data.sentinel;
} else {
sentinel = 'amp';
}
getAmpWindow(sentinel)./*OK*/postMessage( msg, '*');
} else if (event.data.type === 'requestPymjsWidth' || 1) {
msg = [ 'pym', 'example', 'width', event.data.width ].join( 'xPYMx' );
if (event.data.is3p) {
sentinel = event.data.sentinel;
} else {
sentinel = 'amp';
}
getAmpWindow(sentinel)./*OK*/postMessage( msg, '*');
}
}
});
Expand Down

0 comments on commit 39b1f61

Please sign in to comment.