Full-page `position:fixed` iframe – hiding the address bar on mobile when scrolling down within iframe, like for top-level scroll

Question:

On mobile (in some browsers) when you scroll down, the user agent has some sort of heuristic for hiding the browser address bar. This is nice because it gives more screen real estate “for free”.

I have a situation where I’m embedding a ~fullscreen (other than a header/menu bar) iframe in order to sandbox user-written code (think CodePen/JSBin/etc), and I’d like to take advantage of this browser address bar hiding feature if at all possible.

Extra context:

  • I’m in control of the top-level page, and can inject code into the embedded iframe (but note that they’re cross-origin in case that’s relevant).
  • I’m willing to get quite hacky with my solution (e.g. involving postMessage between frames) if it would result in an end result that is quite robust – i.e. unlikely to randomly break scrolling/whatever in some browsers, under some conditions (e.g. user pinches to zoom on page), or after some valid update to the heuristic, etc. If it works in some browsers, but automatically/safely falls back to not having any effect in unsupported, then that’s fine.
  • Any “interventions” within the iframe shouldn’t interfere with the user code in a way that is likely to be relevant to the vast majority of web apps. E.g. a postMessage to the parent is fine, since it is very unlikely to interfere with user code.
  • Probably not relevant, but the iframe is thread/performance isolated with Origin-Agent-Cluster.

Non-Solutions:

  • Making the iframe as tall as its content is not a solution, since this would result in an abnormal visual viewport size, which would affect user code that e.g. visually centers their content within the viewport, or relies on properties like window.innerHeight (and CSS vh) for calculations.
  • Adding fullscreen button is not a solution, but it does offer one alternative that could suit some use cases.
  • Running the user code at the top level (with its own subdomain) is not a solution, since I need the parent frame for important platform-level functionality.

Attempted Solutions:

I’m not optimistic that this is currently possible to achieve, given the set of requirements that I’ve outlined above.

  1. My first thought was that I could just programmatically scroll the top-level frame in sync with the child frame (using postMessage from the child), such that the address bar would naturally hide itself when they’d scrolled the child frame far enough. But unfortunately (in Android Chrome, at least), the user agent does not hide the address bar based on programmatic scrolling – probably for security/anti-deception reasons.

  2. My second thought was that I’d have to tell the embedded iframe to set overflow:hidden; on document.scrollingElement, so that the overscroll triggers the parent frame scroll, which triggers the user agent to hide the address bar, and then maybe I could detect that with Visual Viewport API, and tell the iframe to re-enable scrolling. Even if that would work, this is definitely pushing the limits of the “do not interfere with user code” rule. Ideally it would be the parent that decides that the iframe is not scrollable until the address bar has disappeared. But there’s also the problem of knowing if the address bar will actually disappear at all, and when it will start to disappear, since I don’t think the address bar hiding happens right away – this complicates things a lot.

So, it seems like you’d need something extremely hacky to get this working. I’m hoping that there’s eventually be a browser-standards-supported way of doing this, and part of my reason for posting this question despite being quite pessimistic is so that I’ll hopefully get notified when it does become possible.