csswg-drafts icon indicating copy to clipboard operation
csswg-drafts copied to clipboard

[css-sizing] Auto-resize iframes based on content

Open tabatkins opened this issue 7 years ago • 57 comments

In https://lists.w3.org/Archives/Public/www-style/2017Aug/0045.html, Craig Francis said:

Just re-raising the suggestion of allowing an iframe or textarea to increase its height based on its content.

Mats Palmgren suggested that I raise this issue again, as Firefox is unlikely to implement it unless the CSSWG agrees to spec it.

For iframes, this feature would allow easy and secure (isolated) embedding of third party content without issues relating to scroll bars (i.e. needing custom JavaScript to ask the parent page, via postMessage, to change the iframe height).

For textrareas, this is just a convenience feature, as trying to get JavaScript to do this is tricky (e.g. the browser can automatically add/remove scroll bars, which can throw off the calculations).

When we discussed this last time (after the @seamless attribute on the

iframe,
textrarea {
    height: max-content;
}

In the case of the iframe, the child page would need to send a HTTP header (e.g. Expose-Height-Cross-Origin: 1), so it does not leak information about that website (e.g. if the user is logged in).

PS: This has been discussed in a number of times before, the earliest one I've found is 16 years old:

https://bugzilla.mozilla.org/show_bug.cgi?id=80713

And I've written up some notes about this feature, which includes the suggestion of using it for element height animation (e.g. disclosure widgets):

https://github.com/craigfrancis/iframe-height

tabatkins avatar Aug 31 '17 17:08 tabatkins

I don't understand why “expand to fit scrollHeight” hasn't been the default behaviour for <textarea> all along. In a lot of my demos where I want to add this sort of functionality I achieve this effect by somehow running the following 2 lines of JavaScript from the context of an element:

element.style.height = 'inherit'
element.style.height = element.scrollHeight + 'px'

I've written a little JavaScript function that can help me apply an auto-height or auto-width to elements that match CSS selectors here that uses this same logic: https://github.com/tomhodgins/reprocss/blob/master/mixins/auto-expand.js

Mixin Demo: https://tomhodgins.github.io/reprocss/test/auto-expand-mixin.html

Another Auto-Height demo: https://codepen.io/tomhodgins/pen/KgazaE

Another Auto-Width demo: https://codepen.io/tomhodgins/pen/ZpLxjy

As for auto-height <iframe>, I believe I've seen this happen on iOS before, but I'm not sure I could dig up the CSS. I'll see if I can recreate it :D

tomhodgins avatar Aug 31 '17 17:08 tomhodgins

With iOS, this is their default behaviour for iframes... they call it frame flattening.

craigfrancis avatar Aug 31 '17 17:08 craigfrancis

What happens when

<style>
  p { font-size: 100px; }
  @media (min-height: 50px) { p { font-size: 1px; } }
</style>
<p>hi</p>

gets iframed into

<style>iframe { height: max-content; }</style>
<iframe src="is-this-a-circular-dependency.html"></iframe>

?

UPDATE: I see this has already been asked and that there are already some proposed answers.

eeeps avatar Aug 31 '17 17:08 eeeps

As a website developer, I'd be happy for the iframe to simply increase in size (so the scroll bars weren't required), and to not bother scaling back again.

Which works quite well with the name max-content.

craigfrancis avatar Aug 31 '17 18:08 craigfrancis

This could be a win for accessibility (and monetization/conversion) for inserted adverts, where e.g. the user has specified a larger minimum text size than the default, but at the expense of page reflows as each ad slot gets populated.

liamquin avatar Aug 31 '17 18:08 liamquin

The Working Group just discussed Auto resizing of iframes and textarea based on content size, and agreed to the following resolutions:

  • RESOLVED: Add textarea sizing to Sizing L4
The full IRC log of that discussion <gregwhitworth> Topic: Auto resizing of iframes and textarea based on content size
<astearns> github: https://github.com/w3c/csswg-drafts/issues/1771
<gregwhitworth> TabAtkins: there have been many requests for textarea and iframes resize on content
<gregwhitworth> TabAtkins: we talked it over and thought, yeah probably ok
<gregwhitworth> TabAtkins: we started experimenting with this
<gregwhitworth> TabAtkins: figure our some mechanism content based sizing for textarea and iframes
<gregwhitworth> TabAtkins: we learned some issues impl seamless with iframes due to COR leaking information
<gregwhitworth> TabAtkins: we suspect we'd walk up the frame tree until we hit a fixed size to resolve the mqs
<gregwhitworth> TabAtkins: Someone from Moz impl this
<gregwhitworth> dbaron: we got the spec to say that's how media queries work, but seamless was removed
<gregwhitworth> dbaron: we have code for it - but it's not necessarily something you can write up in a spec
<gregwhitworth> dbaron: you need to figure out how to spec it
<gregwhitworth> iank_: what type of interesting things
<gregwhitworth> dbaron: I'd need to look, like it tries to do layout
<fantasai> s/do layout/do layout, and then tries again/
<gregwhitworth> rossen: we used to have a technology that would allow you to do layout based on then content size and we killed it
<smfr> q+
<gregwhitworth> Rossen: performance becomes a concern for those
<tantek> The iframe use-case for me is known width (set by container), auto (preferably auto-expanding) height
<gregwhitworth> Rossen: when you consider extensions they try to size the box, and it's shrink to fit it becomes very bad for perf
<gregwhitworth> Rossen: Our experimentation for this suggests it was bad, but maybe you'll find something
<gregwhitworth> TabAtkins: they're both pretty separate features anyways
<Rossen> q?
<gregwhitworth> TabAtkins: are we ok experimenting in this space
<gregwhitworth> TabAtkins: the textarea would go into sizing 4
<gregwhitworth> smfr: we've had the auto sizing of the iframe even COR
<gregwhitworth> smfr: it makes your frame layout outside in rather than inside out
<gregwhitworth> smfr: we've had quite a few media query bugs
<gregwhitworth> smfr: we ran into media query cycles
<gregwhitworth> TabAtkins: that means that you weren't doing media queries the way we defined
<smfr> q-
<gregwhitworth> smfr: it does bring about nasty things in the code
<gregwhitworth> TabAtkins: how is this different from regular layout
<gregwhitworth> smfr: you can avoid laying out the iframe, if you have to you have to dirty the other nodes
<gregwhitworth> TabAtkins: ok, let's talk about this later
<gregwhitworth> dbaron: I think the media query problems you had were due to doing what authors weren't expecting
<gregwhitworth> TabAtkins: this would be opt in
<gregwhitworth> tantek: how do you trigger behavior in iOS
<gregwhitworth> smfr: you always get it
<gregwhitworth> smfr: users don't experience nested scrollers, we always wanted page scrolling to win so you don't get trapped
<gregwhitworth> tantek: width is expected and height is auto
<gregwhitworth> smfr: yes
<gregwhitworth> Rossen: ok, so - it seems that Google wants to experiment with this - Apple has it and wants to remove it
<gregwhitworth> Rossen: do you want a resolution
<gregwhitworth> TabAtkins: The textarea one is simple enough to go into sizing 4
<gregwhitworth> TabAtkins: the iframe one can go in WICG
<gregwhitworth> Rossen: any objections to adding textareas to CSS Sizing L3
<gregwhitworth> fantasai: yes
<gregwhitworth> TabAtkins: I said 4
<gregwhitworth> Rossen: Ok, L4
<gregwhitworth> fantasai: ok - I'm ok with that
<gregwhitworth> Rossen: Changes to sizing L4
<fantasai> fantasai: we can add a note for L3
<gregwhitworth> Resolved: Add textarea sizing to Sizing L4
<fantasai> RESOLVED: Add textarea sizing to Sizing L4
<gregwhitworth> *discuss whether to do in WICG*
<gregwhitworth> TabAtkins to spin up a WICG regarding auto sizing of iframes
<tantek> I'm a bit surprised that we're not even putting into a WD something that's been shipping in iOS for 10 years

css-meeting-bot avatar Nov 06 '17 17:11 css-meeting-bot

The auto-resizing (specifically height) of iframes is a highly desirable feature for authors.

I have been using this feature for quite some time on my event posts, e.g.:

http://tantek.com/2017/305/e1/homebrew-website-club

I use an iframe to embed an unknown number of RSVPs generated by an external service, and it is quite handy (e.g. on iOS Safari) for that iframe of RSVPs to auto-grow (in height) as more RSVPs come in.

I'd very much like to see iframe auto-sizing added to Sizing L4 as well.

tantek avatar Nov 07 '17 17:11 tantek

@tantek I'm in favor, but that requires addressing the security concerns at the very least; textarea doesn't have that consideration. If we work it out in the same timeframe as the rest of the features and get implementer backing, I'd be happy to have it in L4.

fantasai avatar Nov 15 '17 05:11 fantasai

I vote for max-content for <textarea/> and other elements but against for not sameorigin <iframe/> and <object/>. If <iframe/> is not sameorigin, the value should be resolved to auto.

Reason: the autosizing of textarea should be implemented, and also horizontal equivalent for width for <input/> but without security problems.

BTW. <iframe/> is old technology and this attribute doesn't have to be implemented on it.

Nadya678 avatar Jan 07 '18 19:01 Nadya678

Moving textarea and input discussion to https://github.com/w3c/csswg-drafts/issues/2141 ; this one will remain open for iframe, since its security implications are more complex.

fantasai avatar Feb 06 '18 22:02 fantasai

Note: The CSSWG resolved to accept this for textarea and text input fields in #2141.

fantasai avatar Mar 04 '18 11:03 fantasai

In the case of the iframe, the child page would need to send a HTTP header (e.g. Expose-Height-Cross-Origin: 1), so it does not leak information about that website

From https://github.com/whatwg/html/issues/555:

For cross-origin I suppose the embeddee would need to opt-in somehow (e.g. meta tag), to not expose new information cross-origin.

Now that we have Feature Policy, I think that's the way to go about that. Similarly, there is a proposal to expose bounds cross-origin.

Malvoz avatar Oct 16 '18 16:10 Malvoz

Since an <iframe> src can be set to an SVG, would max-content work for SVGs as well?

This is actually great that something might actually happen, because it means we could finally:

  1. Have HTML partials!
  2. Emulate container queries!

matthew-dean avatar Dec 19 '18 00:12 matthew-dean

Could it be implemented for same-origin iframes please even if not all security concerns are sorted out for the foreign-origin scenario yet?

prlbr avatar Apr 30 '19 09:04 prlbr

What I'd be interested in seeing here is a more formal description as "contents" isn't really cutting it I think. It's effectively about resizing the viewport to some dimension and that definition will need to account for negative margins and other fun tricks.

annevk avatar May 09 '19 11:05 annevk

When an <iframe> is created at the moment, it will typically create a scrollbar (the thing I want to avoid). I assume the browser knows how big the "content" is to make that scroll bar; so can we use that to set the height?

I'm fairly sure we can treat the width as fixed, as we do with the main browser window - where it will wrap the contents, or use a horizontal scroll bar when that's not possible (I think most developers will understand if they try to show something that's too wide).

craigfrancis avatar May 09 '19 12:05 craigfrancis

Yeah, I'm not really sure where all that machinery is defined and what defines that overflow to the left on ltr pages is ignored, etc. There is probably something there that can be used, but then there's also the complication of defining this in a way that works for mobile, where there's multiple viewports and such.

annevk avatar May 09 '19 12:05 annevk

Just a note on why this would be useful, even if only for same-origin iframes in the beginning: With Edge soon using chromium, all major browsers will support <iframe srcdoc>, and srcdocs are always same-origin.

Encapsulating comments in a blog is a use case for <iframe srcdoc> which is also given as the example in the HTML standard. Comments are almost always displayed in full height in blogs, so having the <iframe> as high as its content is almost always the desired behaviour.

prlbr avatar May 09 '19 14:05 prlbr

@emilio the proposal for recursion appears to be to do an initial pass and then let the scrollbars appear as needed: https://github.com/craigfrancis/iframe-height/blob/master/problems/infinite-loops.md.

annevk avatar May 15 '19 13:05 annevk

So if you do layout during page load, then you get an empty iframe? What Roc implemented in Gecko was that media queries were resolved against the parent document.

emilio avatar May 15 '19 13:05 emilio

@emilio, I'll be very happy if you can improve on this... my suggestion it just there to make the most basic implementation that should work for both browser and website developers (I have since updated that page to note a suggestion where the child page could call a JavaScript method to request the height be updated, but that's just to round off the idea).

craigfrancis avatar May 15 '19 14:05 craigfrancis

Maybe my tone was harsh (sorry) but I am confused about the downvotes with regard to my comment on side channels.

Could it be implemented for same-origin iframes please even if not all security concerns are sorted out for the foreign-origin scenario yet?

I think relaxing the header policy for same origin and subdomains would be reasonable.

At the very least, allow this to be set using <meta> tags.

indolering avatar May 24 '19 08:05 indolering

Side-channels are indeed real and problematic and therefore we should strive not to introduce more of them. The primary security boundary of the web is origins and therefore subdomains are not that reasonable as you might think.

annevk avatar May 24 '19 09:05 annevk

Side-channels are indeed real and problematic and therefore we should strive not to introduce more of them.

I'm sorry, I understand wanting to err on the safe side. A lot of "technically less secure" arguments make implementation and uptake harder without materially impacting security. I was also just interested in what serious attack would be 😸.

Ergonomics is my main concern, which can be addressed by making this feature controllable via HTTP headers and <meta> tags. Would a global JS variable be reasonable?

indolering avatar May 31 '19 09:05 indolering

I am a bit confused why the security concern is a blocker for <iframe>, when <object> already does this (at least when embedding an SVG, even cross-origin). <iframe>s are a lot more secure because they have attributes like sandbox and csp, which <object> does not. So currently we are forced to use <object> to embed SVGs in a responsive, accessible, interactive way (<img> doesn't expose contents to screen readers, make links clickable or text selectable) with no way to disallow scripts in SVG to run. Having iframe resizing would therefor be an improvement to security in my eyes because it stops forcing us to use less secure alternatives.

felixfbecker avatar Dec 09 '20 11:12 felixfbecker

Been discussing this with @chrishtr as an idea to revive.

The killer problem with iframe resizing is that the intrinsic size of a third-party page can be an important/dangerous information channel on its own, so allowing sites to get this effect on arbitrary third-party sites is out of the question. A less obvious issue is the reverse; if an iframe can unexpectedly control its own size, it can be used to attack the framing page.

The trick, then, is making sure that this is opt-in on both sides.

  1. In the iframe, leverage the existing window.resizeTo() function, which currently is a no-op in an iframe, to instead fire a "resize" event on the iframe in the containing document (bubbling, cancelable) when resizing is allowed.
  2. In the containing page, explicitly allow resizing with a new value for the allow attribute, allow=resize. If this is not set, the "resize" event will never show up.
  3. The containing page can preventDefault() the event, in which case nothing happens.
  4. Otherwise, the requested size sets a new "from-element intrinsic size" (name TBD) that can be referenced by a new from-element keyword on contain-intrinsic-size. Like the existing c-i-s: auto, you can accompany this with a default size which'll be used before it's set, like contain-intrinsic-size: from-element 400px 500px;.

In total, then, the containing page doesn't need to run any script at all if they trust the framed page; they can just write <iframe allow=resize style="contain-intrinsic-size: from-element 500px 500px"> and be done. The iframe will start out with a 500x500 intrinsic size (rather than 300x150, the default), and if the iframe'd document calls window.resizeTo(), the iframe will automatically switch its intrinsic size to that value.

However, if the outer page does want to control things (inspecting the value before approving it, batching resizes until later, etc) it's trivial to just listen for the event and preventDefault() when it needs to delay something. The iframe'd page can tell when its been resized by listening for the "resize" event on its own window, as normal.

(contain-intrinsic-size is used here because, effectively, iframes are always size-contained (the requirement for c-i-s to apply), and c-i-s lets you hijack the intrinsic size of the element, which is the right place for this size to be inserted into the layout model. It also lets you provide a default, which is useful on its own.)


The above proposal does require the iframe'd page to run some script, and if it wants its size to be content-based, to do measurement on its own, possibly polling if the size might change over time or due to user interaction. This is because even in a fairly trusted situation, directly exposing content-based sizes can still be dangerous for both sides.

However, in a same-origin environment, we can generally loosen these restrictions and assume trustworthiness, so we can do proper content-based resizing and just need to make sure that it's opt-in so we don't get a behavior change on existing pages. So, the proposal for same-origin iframes is:

  1. Continue to use allow=resize and contain-intrinsic-size: from-element to signal that the outer page wants the iframe to be resizable.
  2. If the iframe is same-origin, it can set a new property (name TBD) on its root element: intrinsic-size: auto | <<length>>{1,2};, with auto meaning the laid-out size of the root element. The c-i-s: from-element value automatically updates to match what this property dictates.

So, how does this proposal sound? Agenda+ to intro it at the next call (no need for decision yet).

tabatkins avatar Mar 23 '21 18:03 tabatkins

Is there precedent for this in libraries/frameworks/userland? It seems you could already do this with postMessage() if you wanted to.

annevk avatar Mar 23 '21 18:03 annevk

Is there precedent for this in libraries/frameworks/userland?

There are indeed various libraries that do it. Here are some examples / related links:

https://stackoverflow.com/questions/153152/resizing-an-iframe-based-on-content http://blog.apps.npr.org/pym.js/ https://github.com/whatwg/html/issues/555 (contains more links to libraries implementing the functionality). The Disqus team also asked for it way back when.

I think this is a very common situation also in ads, where the creative may change its sizing and request adjustment in the parent page.

Those comments are also asking for automatic resizing without script in some cases, based on the intrinsic sizing of the iframe content.

It seems you could already do this with postMessage() if you wanted to.

Yes you can, and developers do it. The reason I think this should be a built-in API are:

  • Common use-case: it will help out developers avoid busywork and unnecessary extra script
  • Third-party coordination: it will help third-party iframes use a compatible API with multiple embedding pages without problems
  • UA integration: allows the resize to be controlled by the user agent, based on policies such as the iframe being offscreen or throttled (these integrations could be specified as add-ons)

chrishtr avatar Mar 23 '21 19:03 chrishtr

Yeah, the first solution is basically "standardize and simplify the postMessage() dance".

The second solution is trying to solve the original <iframe seamless> use-cases (safely embed arbitrary content in your webpage, via a locked-down iframe+srcdoc) without the complications of the "apply outer page's style to the iframe'd page" that seamless tried to do; it's slightly more complex than strictly necessary to keep the overall solution consistent with the first one.

tabatkins avatar Mar 23 '21 19:03 tabatkins

Looking at this again it struck me there's no success/failure indication for the caller of window.resizeTo(). Not sure if we can return a promise there that resolves with the new/unchanged dimensions, but that would probably be desirable I suspect.

annevk avatar Mar 24 '21 08:03 annevk