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

autoresize iframes spec #29

Open
panzi opened this issue Oct 15, 2013 · 14 comments
Open

autoresize iframes spec #29

panzi opened this issue Oct 15, 2013 · 14 comments
Assignees

Comments

@panzi
Copy link
Contributor

panzi commented Oct 15, 2013

This is actually something you (iframely) cannot provide, but that has to be implemented by the embed service and the embed user. But I think someone should specify a way how such a thing can be done. Maybe this isn't the proper place for this, but where would that be? Anyway, I would propose this interface:

The embed sends a message like this on load:

parent.postMessage("iframely:style:width=320px&height=480px&overflow=hidden&border=0%20none%20transparent&background=transparent","*");

iframely:style: is a marker so that the site using the embed understands what to do with this message. The rest is encoded as a URL query string. A string is used instead of a JavaScript object because older browsers only support strings as message data.

Note: The site parsing the message has to filter the style properties (only allow certain ones), so that the embed cannot do any shenanigans. Just allowing the ones used here should be ok. (Things like opacity and absolute/fixed position are not ok.)

Also for older IEs overflow and border have to be translated into scrolling and frameBorder. However, dynamically setting frameBorder="0" seems not to work in IE8, so it has to be done by the embed user anyway. The important things are width and height. Maybe only width, height, overflow and background should be supported?

Who needs this?

Embeds that should integrate more or less seamlessly into the parent page. E.g. Disqus, but also crowdranking when the user want's to use "auto-size" (so that the embedded ranking does not need scroll-bars). There was another service of which I forgot the name/URL. There you can host hole articles with images and what not. These also auto resize to their content.

Currently all these services use their own APIs that require the embed user to include scripts provided by the services, which I deem ugly and unsafe. If the message format for these tasks would be standardized this would not be necessary.

Browser Support

postMessage has to be supported: http://caniuse.com/#search=postMessage

Internet Explorer 8+ (witch the mentioned limitation on frameBorder for IE8), Firefox 3.0+, Chrome 4.0+, Safari 4.0+, Opera 9.5+ etc.

There are hacks for old browsers that do not support postMessage, but they are extremely hackish and produce problems when the page has more than one embed. The hacks work like this: Communicate to the iframe the exact URL of the parent page, so that the iframe can then do:

parent.location = known_parent_url+"#!iframely:style:"+style_query;

And the parent frame has to poll location.hash with setTimeout (because browsers that do not support postMessage don't support onhashchange either). If two embeds set the parent's hash within the same poll interval one message is lost. Also there is no easy way to know which embed sent the message. Also pages that actually use location.hash for their own purposes will have problems with this. So I wouldn't bother with this hack.

Example

http://jsfiddle.net/fF9Hp/6/ (iframe)
http://jsfiddle.net/nxUVp/10/ (embed user)

@iparamonau
Copy link
Member

We have something similar specified in Iframely protocol - https://github.com/itteco/oembed2/blob/master/4%20-%20Embed%20MIME%20types.md

Have you seen it?

@nleush
Copy link
Member

nleush commented Oct 15, 2013

In addition to Ivan:

Example usage here: https://github.com/itteco/iframely/blob/master/plugins/domains/facebook.com/facebook.post.ejs

But you are right, it needs to be more unified for common cases.

Also we are looking at google plus posts embedding style:

https://developers.google.com/+/web/embedded-post/

This method have flexibility and performance benefits on multiple embeds, but it also not so safe as iframe.

I will think about you ideas.

@nleush
Copy link
Member

nleush commented Oct 15, 2013

Maybe only width, height, overflow and background should be supported?

height - in most cases. Width usually are floating. If widget know its width and height it can specify it by media

Usually widgets have static width, and height depends on amount of text. So it could be calculated only after render. And this is not so easy, because content can load dynamically and height will change permanently. We had problem with this in storify plugin.

If widget has floating width - it will calculate height depending on configuration of iframe set by parent and media constraints. And the rest will be all the same as in previous case.

overflow and background - works inside iframe content as I know. How can it act in iframe from parent window?

Maybe you mean ALLOWTRANSPARENCY?

@panzi
Copy link
Contributor Author

panzi commented Oct 15, 2013

Oh, indeed. I haven't seen this. But it has a few problems:

It uses objects, not strings. caniuse.org: "Internet Explorer 8 and 9, and Firefox versions 6.0 and below only support strings as postMessage's message." Especially IE 8 and 9 would be targets that should be supported. Look at the combined market share of IE 8 and 9: http://www.netmarketshare.com/browser-market-share.aspx?qprid=2&qpcustomd=0

Also the windowId is a bit more complicated than necessary. It means that the iframe has to do more complex communication with it's parent (the order of "register" and "iframe loaded" can be arbitrary, sending "resize" has to happen after both). In my example no windowId is needed because it always searches through all iframes and compares iframe.contentWindow === event.source.

And then the message format is a bit generic. I would mark it somehow more unambiguously. In my example I prefixed the message data with iframely:.

I think the simpler the API, the more likely it will be implemented. Ok, my example needs a bit of a script on the side of the embedder, but it's not that big and can be embedded once without the need of adding logic that assigns windowIds to iframes.

@nleush
Copy link
Member

nleush commented Oct 15, 2013

Registering needed to control flow from parent window. Iframe can send signal before events in parent window will be set up. I don't know how critical could it be, but if all this managed by common lib - complexity is not so important.

So its more about control - not to find who sent event.

@panzi
Copy link
Contributor Author

panzi commented Oct 15, 2013

`overflow` and `background` - works inside iframe content as I know. How can it act in iframe from parent window? Maybe you mean ALLOWTRANSPARENCY?

Ah, yes. I also mean allowtransparancy. But actually in my experience you have to set all these things (allowtransparency="true", background:transparent, scrolling="no", overflow:hidden) to be on the safe side. But yes, allowtransparency is missing in my example. Also it seems as if it's no longer supported with IE9. Updated: http://jsfiddle.net/nxUVp/11/

Also now I have second thoughts about some aspects of my example: You can do a lot of shenanigans with CSS. It should not only filter the property names, it should also filter the values (only allow certain value patterns), because e.g. in IE you can embed JavaScript expressions in CSS! So I guess: only allows width and/or height which have to be parsed by parseInt(value, 10), all the rest has to be done by by the embed user (specifying allowstransparency and frameborder etc.).

@panzi
Copy link
Contributor Author

panzi commented Oct 15, 2013

@nleush If the onmessage handler is registered in <head>, then no iframe message should arrive "too early". If the embed user set's up thing asynchronously, they can register a simple onmessage handler that remembers the messages. I think it should be simple for the simple case. E.g. a user want's to have auto resizing iframes, so they just put one simple <script src="..."></script> into their <head> and they are done.

Because I think simplicity is important, here is a simplified version of my example:
http://jsfiddle.net/Cjs7x/2/
http://jsfiddle.net/kBh7c/2/

@iparamonau
Copy link
Member

I like your ideas, Mathias. Having the need for iframe to register itself first isn't the nicest thing. However, we should keep in mind that the same mechanism we'd choose for auto-resizing will also be used for playback sync.
Like sending the sequence of events "user started my player- parent stop everyone else". "Ok everyone: stop". "1st - stopped, 2nd - stop".

Let us think a little more about your suggestions. The goal is to have simple, but not too ambiguous spec

@panzi
Copy link
Contributor Author

panzi commented Oct 15, 2013

I agree. Anyway, I think the most important thing is to use strings instead of objects (IE8 and IE9 support) that are prefixed with something unique like iframely: and general simplicity. If one want's to send more complex data, one could use JSON.stringify/parse. The JSON object is supported in all browsers that support postMessage (well, for Opera JSON support came a tiny bit later, but for IE it's the same).

@ghost ghost assigned iparamonau Oct 16, 2013
@nleush
Copy link
Member

nleush commented Oct 16, 2013

Ok, lets see what separate problems we have.

  1. parameters available to sent to parent. We can allow object pattern (useing JSON.stringify) but recommend using limited list of attrs.
  2. iframes registering / listening mechanism to sync parent and iframe windows. All must be ready to connect and use two side communication protocol.
  3. Browsers compatibility. Stringss/objects/hashes.

Thats all.

About 1.

Now we support height, its not a problem to add width. Other css attrs looks like more complex logic for me. As I understand you and css:

  1. transparency=true will make: allowtransparency="true", background:transparent
  2. overflow=hidden will make scrolling="no", overflow:hidden

And thats only two cases.

About 2.

As I told I want to have control over all iframes as a parent window. I had cases with async scripts loading in parent window. That's mean it would be +1 problem to bind head event and make queue with iframes messages.

In my current solution you need one script and one function call to start managing iframes in page. No care when it called.

If we listening to head, we need to make parent window use head script. Thats limitation to user. In case of complex async apps it could be a problem.

From the other side: If we use iframely.js to render iframes, then it will be already initialized to listen iframes it created. Buy in case of server render we should force some script loading in head.

So in both cases we have some explicit restrictions:

  1. place script in head or launch script before iframe inserted.
  2. call script after iframe inserted.

I don't have clear vision what is much better, but as I told - i think second case is more manageable.

About 3.

I use this http://www.onlineaspect.com/2010/01/15/backwards-compatible-postmessage/ code in all iframely libs. Its a bit truncated in iframely version, but its not a problem to return full version. Lets assume this code handles browser support and object->string->object conversion.

@nleush
Copy link
Member

nleush commented Oct 16, 2013

One more problem we have with that - is detecting real window size inside iframe. Its not about spec, but more like: how and when detect content size.

In common case iframely acts like following:

  1. calculates default iframe some positive size which will be resized later.
  2. render iframe with wrong size, it can show scrollbars, white rectangle or content partially.
  3. when iframe script know size - iframe will be resized by parent.

The biggest problem here is following:

Iframe should have some (incorrect) size initially. And that size will be visible to user sometime (wait for content loaded). Its very ugly. Positive height allow iframe content be rendered and have real size with scrollbars. I tried to make initial height=0 but that cause problems to iframe window to detect size. Maybe more hacks needed.

Also I had problems with size change after scrollbar added or disappeared.

You can play with that using plugins which use iframely-utils.js.

@panzi
Copy link
Contributor Author

panzi commented Oct 18, 2013

What if you make the iframe filter: alpha(opacity=0); opacity: 0; instead of height:0px;? Yes, it will occupy a wrong size initially, but it would be less of a problem than rendering scrollbars/a white rectangle/partial content. Maybe visibility: hidden; would also work, but one have to test it (maybe browsers optimize that case by not calculating anything, that's what happens with display: none; anyways).

@panzi
Copy link
Contributor Author

panzi commented Oct 18, 2013

You could wrap the iframe in a div that has overflow: hidden; width:0px; height:0px;. Requires more HTML, though.

@iparamonau
Copy link
Member

@panzi I've moved this into itteco/oembed2 repo. Please, join there. We are also discussing the initial handshake in itteco/oembed2#3

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

No branches or pull requests

3 participants