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

✨Add amp-iframe support for Pym.js width and height resize messages #24917

Merged

Conversation

westonruter
Copy link
Member

@westonruter westonruter commented Oct 5, 2019

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.

Demo video (YouTube):

Demo video

The code can be tested using this Glitch: https://amp-iframe-pymjs-resizable.glitch.me/

@@ -466,6 +466,8 @@ export class AmpIframe extends AMP.BaseElement {
/*opt_allowOpaqueOrigin*/ true
);

addEventListener('message', this.listenForPymMessage_.bind(this));
Copy link
Member Author

Choose a reason for hiding this comment

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

This seems not ideal. There should rather be one single message event handler that all of the iframes share.

It seems rather that listenFor should be used, but the problem is that the Pym.js event.data is a string and not an object with any sentinel. So there isn't an easy way to integrate with this existing event listener system.

this.updateSize_(parseInt(args[1], 10), undefined);
} else if ('width' === args[0]) {
this.updateSize_(undefined, parseInt(args[1], 10));
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Perhaps a message should be logged out for other message types so that a developer doesn't get confused about why it is not working.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure. We could do a user().warn() for this.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in 197c110

Copy link
Contributor

@dvoytenko dvoytenko left a comment

Choose a reason for hiding this comment

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

Looks good. Couple minor comments. And this will need tests.

@@ -103,6 +103,9 @@ export class AmpIframe extends AMP.BaseElement {
/** @private {string} */
this.sandbox_ = '';

/** @private {Function} */
this.unlisten_ = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's name it unlistenPym_.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in 0b31cca

@@ -466,6 +469,13 @@ export class AmpIframe extends AMP.BaseElement {
/*opt_allowOpaqueOrigin*/ true
);

// Listen for resize messages sent by Pym.js.
this.unlisten_ = listen(
window,
Copy link
Contributor

Choose a reason for hiding this comment

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

s/window/this.win

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in 89dcadb

* Listen for Pym.js messages for 'height' and 'width'.
*
* @see http://blog.apps.npr.org/pym.js/
* @param {MessageEvent} event
Copy link
Contributor

Choose a reason for hiding this comment

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

!MessageEvent

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in 8dce048 and 761267d

this.updateSize_(parseInt(args[1], 10), undefined);
} else if ('width' === args[0]) {
this.updateSize_(undefined, parseInt(args[1], 10));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Sure. We could do a user().warn() for this.

@westonruter westonruter force-pushed the add/amp-iframe-pymjs-support branch 2 times, most recently from c3fa98c to 761267d Compare November 21, 2019 20:10
@westonruter
Copy link
Member Author

And this will need tests.

Tests added in 3a726ba.

@westonruter
Copy link
Member Author

I'm not sure why the tests are failing, as the failures don't seem related to my code. Nevertheless, I wasn't able to get the tests working locally either (even on master).

Cf. Slack thread

Copy link
Contributor

@dvoytenko dvoytenko left a comment

Choose a reason for hiding this comment

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

Code LGTM. The test failures seem superficially relevant. But could be something else. I'll take a look as well.

Comment on lines 55 to 69
} else if (event.data.type === 'requestPymjsHeight') {
var msg = [ 'pym', 'height', event.data.height ].join( 'xPYMx' );
getAmpWindow(sentinel)./*OK*/postMessage( msg, '*');
} else if (event.data.type === 'requestPymjsWidth') {
var msg = [ 'pym', 'width', event.data.height ].join( 'xPYMx' );
getAmpWindow(sentinel)./*OK*/postMessage( msg, '*');
Copy link
Member Author

Choose a reason for hiding this comment

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

This sentinel I believe is undefined here, so that's not right. Nevertheless, I've tried populating sentinel here I tried populating with event.data.sentinel and 'amp' and 'amp-test' but no success.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm. I'll try to take a look tomorrow, but I'm running out of time a bit. Could you ping @jridgewell to see if he might have advise on messaging specifically for tests?

Copy link
Member Author

Choose a reason for hiding this comment

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

The sentinel variable was indeed undefined. This seems to be part of the problem; the var was hoisting so the undefinedness wasn't causing an error. Fixed in c640f88.

return;
}
const data = getData(event);
if (typeof data !== 'string' || 'pym' !== data.substr(0, 3)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We have a startsWith helper

Copy link
Member Author

Choose a reason for hiding this comment

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

Utilized in af98c22

return;
}

// The format of the message takes the form of `pym${id}xPYMx${type}xPYMx${message}`.
Copy link
Contributor

Choose a reason for hiding this comment

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

The format has a "xPYMx" between "pymand${id}`, too. That was confusing me since you were splicing 2 elements from the front of the array (without the "xPYMx" there, you would be splicing too much).

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch. Fixed in eafb57a

// '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/).splice(2);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: no need to splice, just reference indexes 2 and 3.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed in eafb57a

@@ -52,6 +52,12 @@
sentinel: sentinel,
type: 'send-embed-state',
}, '*');
} else if (event.data.type === 'requestPymjsHeight') {
var msg = [ 'pym', 'height', event.data.height ].join( 'xPYMx' );
Copy link
Contributor

Choose a reason for hiding this comment

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

You need to add some bogus id after pym and before height.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed in 2d578da

var msg = [ 'pym', 'height', event.data.height ].join( 'xPYMx' );
getAmpWindow(sentinel)./*OK*/postMessage( msg, '*');
} else if (event.data.type === 'requestPymjsWidth') {
var msg = [ 'pym', 'width', event.data.height ].join( 'xPYMx' );
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed in 2d578da

@jridgewell
Copy link
Contributor

Ready for review?

@jridgewell jridgewell closed this Jan 28, 2020
@jridgewell jridgewell reopened this Jan 28, 2020
@westonruter
Copy link
Member Author

Ready for review?

Yes, I was just waiting for the jobs to finish before re-requesting. But yes!

@westonruter
Copy link
Member Author

Rebased to re-trigger build due to infra issue.

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.

amp-iframe: Add support for message protocols of popular libraries that do resizable iframes
6 participants