Bug 153852 - <body> with overflow:hidden CSS is scrollable on iOS
Summary: <body> with overflow:hidden CSS is scrollable on iOS
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: CSS (show other bugs)
Version: WebKit Nightly Build
Hardware: iPhone / iPad iOS 10
: P2 Normal
Assignee: Nobody
URL: https://github.com/w3c/csswg-drafts/c...
Keywords: HasReduction, InRadar
Depends on:
Blocks: 159753
  Show dependency treegraph
 
Reported: 2016-02-03 18:58 PST by Chris Rebert
Modified: 2019-09-26 13:31 PDT (History)
28 users (show)

See Also:


Attachments
Testcase demonstrating the problem (8.37 KB, text/html)
2016-02-03 18:58 PST, Chris Rebert
no flags Details
GIF video demonstrating the bug (885.72 KB, image/gif)
2016-02-04 01:38 PST, Chris Rebert
no flags Details
patch (16.67 KB, patch)
2019-05-06 10:15 PDT, Antti Koivisto
no flags Details | Formatted Diff | Diff
patch (16.78 KB, patch)
2019-05-06 10:43 PDT, Antti Koivisto
ews: commit-queue-
Details | Formatted Diff | Diff
Archive of layout-test-results from ews215 for win-future (13.53 MB, application/zip)
2019-05-07 02:42 PDT, Build Bot
no flags Details
patch (14.08 KB, patch)
2019-05-07 03:11 PDT, Antti Koivisto
no flags Details | Formatted Diff | Diff
patch (11.56 KB, patch)
2019-05-07 03:20 PDT, Antti Koivisto
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Rebert 2016-02-03 18:58:32 PST
Created attachment 270621 [details]
Testcase demonstrating the problem

Applying `overflow: hidden` to the `<body>` should make it non-scrollable.
This works correctly on OS X Safari, but doesn't work correctly on iOS Safari.

Original Bootstrap issue:
Lost to the sands of time, but https://github.com/twbs/bootstrap/issues/14839 is relevant & popular.
See also http://getbootstrap.com/getting-started/#overflow-and-scrolling

Steps to reproduce:
1. Open the attached testcase in iOS Safari.
2. Tap the "Launch demo modal" button.
3. A yellow box with text appears.
4. Scroll the yellow box to its bottom, where there are "Close" and "Save changes" buttons, and lift your finger off the screen to ensure that the scroll gesture ends.
5. Attempt to scroll the yellow box further downward (which is to say, apply a "fling upward" gesture to the yellow box).

Actual result:
The <body> underneath the yellow box scrolls downward.

Expected result:
The <body> should not scroll (just like on OS X Safari).
Comment 1 Chris Rebert 2016-02-04 01:38:35 PST
Created attachment 270644 [details]
GIF video demonstrating the bug
Comment 2 Simon Fraser (smfr) 2016-03-22 16:15:42 PDT
I would be concerned about changing behavior here, because it would cause content which has been reachable on earlier iOS versions to become unreachable.
Comment 3 Chris Rebert 2016-03-22 17:21:11 PDT
Android Chrome already made the behavior change and blazed the trail here:
https://bugs.chromium.org/p/chromium/issues/detail?id=175502

They tracked broken sites in https://bugs.chromium.org/p/chromium/issues/detail?id=444581 (and its dupes).
They communicated with several sites and got some to fix their CSS.

In comments 24-25 & 28, they acknowledge some breakage, but don't think there's enough of it to justify switching back:
https://bugs.chromium.org/p/chromium/issues/detail?id=444581#c24
https://bugs.chromium.org/p/chromium/issues/detail?id=444581#c28

I couldn't find any signs of any subsequent revert.

----

If WebKit were to disagree with Chrome's analysis and fear too much breakage,
what would be the path forward?
A new "yes, I really truly want 'hidden' here!" value for the CSS 'overflow' property?
Comment 4 Eric Allam 2016-07-12 04:42:00 PDT
This bug has recently become a problem for a project I'm working on where we are trying to implement a chat interface, with a header element that acts like a navigation bar, and a footer element that includes a text field, with a chat container which includes the chat messages that should be able to scroll. I know this isn't a reduced test case but we have put together a stripped down version of the chat on JSBin which demonstrates this problem too:

http://jsbin.com/ruyito/edit?html,css,output
Comment 5 Chris Rebert 2016-12-04 21:54:42 PST
(In reply to comment #2)
> I would be concerned about changing behavior here, because it would cause
> content which has been reachable on earlier iOS versions to become
> unreachable.

Based on https://github.com/w3c/csswg-drafts/issues/666#issuecomment-257889984 ,
should I assume your opinion has changed?
Comment 6 Simon Fraser (smfr) 2016-12-04 23:03:23 PST
After https://trac.webkit.org/changeset/194667 I am cautious about changing iOS <body> overflow:hidden behavior.
Comment 7 Marcelo Freitas 2017-08-14 13:39:23 PDT
Hi guys, do you have any new about this issue? I am facing similar behavior and I would like to know if is there any solution or workaround!
Comment 8 Sameera 2017-08-15 03:44:22 PDT
I'm having same issue. I have find solution with adding class to body with the style position:fixed. But doing that page scroll to the top.

Any other solutions?
Comment 9 David Bokan 2018-03-08 11:09:40 PST
smfr@, do you have a list of pages you saw affected by this we could check again?

I believe iOS WebKit is the only engine that doesn't respect `overflow: hidden` on body so it'd be great for interop if this could be fixed. FWIW, Chrome has had this behavior for several years now and I haven't heard of it causing issues lately. Perhaps sites have been fixed to take it into account by now.
Comment 11 David Bokan 2018-03-09 09:11:10 PST
Thanks smfr@, I've looked into each case. It seems they fall into one of three buckets (mainly using 6S iOS 9):

No overflow: hidden today - site was fixed:
  https://translate.google.dk/translate?hl=da&sl=en&u=http://www.wikihow.com/&prev=search
  Pages on https://surf.miracosta.edu
  (Apple developer documentation) https://developer.apple.com/documentation/ appears fixed

Overflow(-x): hidden still there but site works correctly in both Chrome and Safari (if overflow: hidden were respected).
  https://books.google.com.mx/books?id=vCMIOfrbYrAC&pg=PA83&dq=Funciones+inyectivas,+suprayectivas+y+biyectivas&ei=AiCHSvDONqbKyQTEhO2fDg#v=onepage&q&f=false
    - Appears intentional and correct - scrolling should occur only on the subscroller
    - Safari allows scrolling main frame a little which actually feels wrong since it shifts the UI out of view
    - Respecting overflow: hidden would actually improve the UX here
  https://mobility.agbar.net/mobility-server/
    - There doesn't appear to be any scrolling on the page.
    - Page loads fully zoomed out
    - overflow: hidden commented out but would work regardless, there's no scrolling on the page
  http://www.arnb.org/Festivals.php
    - overflow-x: hidden - there should be no horizontal scrolling
    - Works correctly, all page content is reachable

Problematic on Safari (all work correctly in Chrome):
  http://archives.nyphil.org/index.php/artifact/63041fe8-4b51-4f96-ae82-e40bccf152cd/fullview#page/1/mode/2up
    - No viewport meta
    - Chrome loads the page zoomed out fully so everything is visible
    - Safari iOS 9 locks the page to zoomed in if overflow: hidden is on. When I turn it off using inspector I can zoom out
  desktop version of live.com/hotmail.com/outlook.com
    - Works correctly in Chrome Android
    - There is overflow-x: hidden
    - I'm guessing Safari loads zoomed in because, even in request desktop site, it respects the viewport <meta> which has initial|min|max-scale: 1? Or perhaps related to overflow: hidden as above.
  https://support.eadocsoftware.com
    - No viewport meta
    - Better on Chrome because Chrome sizes layout viewport to match content width, loads zoomed out - the rest is just pinch zoom
    - Why doesn't Safari allow zooming out?
    - iOS 9 loads zoomed in - iOS11 loads zoomed out like Chrome
  1. Open http://output.jsbin.com/sagowo in iOS 9 Safari.
  2. Tap the blue button to make the Bootstrap modal dialog appear.
    - Works correctly in Chrome
    - Chrome shows the page zoomed out
    - No viewport meta tag
    - Why is safari zoomed in so much? Doesn't allow zoom...
    - iOS 11 works, loads zoomed out

In all the problematic cases it looks like the issue is that Safari loads the page zoomed in and then can't scroll because of overflow: hidden. Was this tried before Safari shipped the visual/layout viewport model? Having the split viewport makes these issues quite tractable; Chrome seems to handle these cases rather gracefully so let me explain what we do:

The layout viewport is sized to cover the entire content width. In the case of a desktop page where we fallback to the 980px initial containing block, the layout viewport will be 980px wide. When the page loads, Chrome zoomes out to the minimum possible scale so the visual viewport will match the layout viewport on load. 

overflow: hidden applies only to the layout viewport scrolling. If the user zooms in, they can still pan around but the layout viewport will not scroll from it's original position. This matches the behavior on desktop. If a page is broken in Chrome Android, it's likely also broken on a desktop browser because the layout viewport operates analogously to the browser window.

When you shipped this did you already have a visual/layout viewport? Did overflow: hidden apply only the the layout viewport. Safari on iOS11 loads all the problematic cases zoomed out  just like Chrome so just marking the layout viewport overflow: hidden wouldn't break any of them.
Comment 12 Simon Fraser (smfr) 2018-03-12 13:14:33 PDT
I don't think we had layout/visual viewports when we tried this before.
Comment 13 Radar WebKit Bug Importer 2018-03-21 11:18:10 PDT
<rdar://problem/38715356>
Comment 14 jonjohnjohnson 2018-12-19 13:51:02 PST
bokan@chromium.org thanks for all your help in urging a way forward for compat.

As for a work around, eric@tinymatter.co.uk, marcelopf@ciandt.com, & supun.sameera@live.com, feel free to analyze this setup using sticky positioning, offset margins, and cross axis overflow: https://vault.jonjohnjohnson.com/examples/cssripstop/
Comment 15 Davison 2018-12-19 22:23:07 PST
Honestly this seems like a situation where making iOS Safari work like every other browser would fix a lot more issues than it would break.  As David pointed out, iOS as it is now seems well positioned for finally eliminating this odd behavior.  Whatever tiny fraction of sites that might have a problem are likely very old, already broken on every other browser, and not frequented by almost anyone.

So many people trying to work around this bug have led to a terrible mess of javascript and css hacks that:
1) Frequently don't actually resolve the issue entirely
2) Sometimes appear to expose other iOS Safari issues that complicate things further

Resolving this issue would instantly make modal popups far easier to implement in a simple, cross-platform way.  Thanks to all (especially David) for the work in fleshing this out.
Comment 16 jonjohnjohnson 2018-12-20 13:40:12 PST
Completely agree dlong@nextgentech.net, just trying to offer the only actually clean non js workaround I know, while we wait for compatibility.
Comment 17 Antti Koivisto 2019-05-06 10:15:16 PDT
Created attachment 369132 [details]
patch
Comment 18 Simon Fraser (smfr) 2019-05-06 10:33:02 PDT
Comment on attachment 369132 [details]
patch

View in context: https://bugs.webkit.org/attachment.cgi?id=369132&action=review

> Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingTree.h:61
> +#if PLATFORM(IOS_FAMILY)
> +    void rootScrollingNodeScrollabilityDidChange(bool scrollable);
> +#endif

Shouldn't this be state on a FrameScrollingNode? I would think that iframe have similar logic?
Comment 19 Antti Koivisto 2019-05-06 10:38:21 PDT
> Shouldn't this be state on a FrameScrollingNode? I would think that iframe
> have similar logic?

State is in FrameScrollingNode. This is just to pass things to the main UIScrollView (which is only accessible via PageClient etc).
Comment 20 Antti Koivisto 2019-05-06 10:43:20 PDT
Created attachment 369135 [details]
patch
Comment 21 Simon Fraser (smfr) 2019-05-06 11:20:17 PDT
(In reply to Antti Koivisto from comment #19)
> > Shouldn't this be state on a FrameScrollingNode? I would think that iframe
> > have similar logic?
> 
> State is in FrameScrollingNode. This is just to pass things to the main
> UIScrollView (which is only accessible via PageClient etc).

Oh right, I didn't see it was in the params already. Yay.
Comment 22 Build Bot 2019-05-07 02:42:33 PDT
Comment on attachment 369135 [details]
patch

Attachment 369135 [details] did not pass win-ews (win):
Output: https://webkit-queues.webkit.org/results/12121416

New failing tests:
svg/repaint/remove-border-property-on-root.html
Comment 23 Build Bot 2019-05-07 02:42:36 PDT
Created attachment 369263 [details]
Archive of layout-test-results from ews215 for win-future

The attached test failures were seen while running run-webkit-tests on the win-ews.
Bot: ews215  Port: win-future  Platform: CYGWIN_NT-10.0-17763-3.0.5-338.x86_64-x86_64-64bit
Comment 24 Antti Koivisto 2019-05-07 03:11:24 PDT
Created attachment 369267 [details]
patch
Comment 25 Build Bot 2019-05-07 03:13:13 PDT
Attachment 369267 [details] did not pass style-queue:


ERROR: Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h:70:  Inline functions should not be in classes annotated with WEBCORE_EXPORT. Remove the macro from the class and apply it to each appropriate method, or move the inline function definition out-of-line.  [build/webcore_export] [4]
Total errors found: 1 in 13 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 26 Antti Koivisto 2019-05-07 03:20:30 PDT
Created attachment 369268 [details]
patch
Comment 27 Build Bot 2019-05-07 03:22:18 PDT
Attachment 369268 [details] did not pass style-queue:


ERROR: Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h:70:  Inline functions should not be in classes annotated with WEBCORE_EXPORT. Remove the macro from the class and apply it to each appropriate method, or move the inline function definition out-of-line.  [build/webcore_export] [4]
Total errors found: 1 in 11 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 28 WebKit Commit Bot 2019-05-07 05:02:31 PDT
Comment on attachment 369268 [details]
patch

Clearing flags on attachment: 369268

Committed r245006: <https://trac.webkit.org/changeset/245006>
Comment 29 WebKit Commit Bot 2019-05-07 05:02:33 PDT
All reviewed patches have been landed.  Closing bug.
Comment 30 Davison 2019-05-07 13:16:31 PDT
Am I reading this correctly that with the issue resolved WebKit will now no longer allow the body to scroll if overflow: hidden is set?  If so that is awesome!  How long does it typically take a patch in WebKit to land in an official iOS release?
Comment 31 Simon Fraser (smfr) 2019-05-07 13:28:53 PDT
Correct. We don't make statements about future releases.
Comment 33 Bruno Stasse 2019-09-21 04:01:09 PDT
The patch seems to have landed in iOS 13, but there are cases where it doesn't work.

Scroll on the body is prevented when:
- in Safari in portrait mode when the browser UI is fully visible (i.e. both the address bar and the toolbar bar are visible)
- in Safari in landscape mode (whether the browser UI is fully visible or not)
- in "in-app browsers" (whether the browser UI is fully visible or not)

Scroll on the body is not prevented when:
- in Safari in portrait mode when the browser UI is not fully visible (i.e. the address bar is retracted, and the toolbar bar fully collapsed), whether it is due to a scroll down or with the new "Hide toolbar" feature activated
- in "standalone mode", i.e. when the web page is added to homescreen and no browser UI is displayed.

These two cases are quite common and the behaviour not being consistent is problematic.
Comment 34 Simon Fraser (smfr) 2019-09-21 16:51:32 PDT
(In reply to Bruno Stasse from comment #33)
> Scroll on the body is not prevented when:
> - in Safari in portrait mode when the browser UI is not fully visible (i.e.
> the address bar is retracted, and the toolbar bar fully collapsed), whether
> it is due to a scroll down or with the new "Hide toolbar" feature activated

This is by design; scrolling is necessary to reveal the browser UI. This could be improved in future.

> - in "standalone mode", i.e. when the web page is added to homescreen and no
> browser UI is displayed.

Please file a new bug on this case.
Comment 35 Davison 2019-09-22 17:20:46 PDT
(In reply to Simon Fraser (smfr) from comment #34)
> 
> Please file a new bug on this case.

I don't mean to be disagreeable, but I think this is *extremely* unfortunate.  It's taken so long already to get any action accomplished on this, and the end result doesn't appear to actually fix the issue at all.  Setting overflow:hidden on body should prevent scrolling.  Period.  No exceptions.

There needs to be other mechanisms to deal with special browser UI situations--not overriding standard rendering engine mechanics.  Why can't tapping on the header suffice for revealing the other UI components?  If progress were being made rapidly I'd be more forgiving, but it just feels very defeating because this likely means we'll still not see a true resolution for years to come given the history of this issue.

At the very least until this issue is actually resolved there needs to be a way for a site to easily disable any hiding of the browser UI components (so as to prevent the possibility of body scrolling at all).  This is very important for a variety of situations like menu bars, modals, etc.  Can we please make progress quickly on this?

While I understand the need to file a new bug from a technical perspective, I feel there should be some continuity here because this bug really isn't resolved if scrolling can happen at all.
Comment 36 shrpne 2019-09-22 17:44:56 PDT
I agree with Davison.

How about this:
overflow: hidden disabled scroll,
when user tries to scroll - page doesn't react to it, but UI reacts to scroll gesture, not to page scroll.

Everybody happy: users can reveal UI by usual scroll gesture, developers have page without scrollbar
Comment 37 Bruno Stasse 2019-09-25 05:49:06 PDT
While trying to create testcases to show when body scroll is not prevented in standalone mode, I noticed that the behaviour is not the same depending on the viewport meta-tag (`width`, `user-scalable`, `viewport-fit`, `shrink-to-fit`, etc) added to the HTML, even in Safari. Even more problematic and surprising, I was able to get different behaviours *with the same code*. Depending on where you come from, or the time of the day — I have no idea —, in Safari in portrait mode, I was able to:
- totally prevent body scrolling, even when the UI is hidden
- have scroll prevented only when the UI is visible
- have scroll not prevented at all whether the UI is visible or not.

I would like to be able to be more precise and provide testcases, but unfortunately the results of my tests were changing for no apparent reasons and therefore cannot. Creating a simple HTML page where you can scroll the body and switch overflow auto/hidden on the body and change the viewport metatag should give you a sense of what it is like.


The situation is therefore even more chaotic and unpredictable than before, as depending on situation the scroll might or might not be prevented. I understand the will to let the user reveal the browser UI with scroll at all time, as this is the primary way to make that happen, and the user is used to it. However, this cannot be done at the prejudice of the UX within web pages. Not being able to able to block the body scroll reliably in all situations makes websites and web applications look buggy, and ruin Safari's users experience, making Safari look bad.

For the reasons mentioned before, the current patch cannot be considered as solving the bug, since unfortunately it doesn't even match the design decision that you mention, Simon Fraser. To recapitulate, we have:

In Safari:
- in portrait mode: scroll might or might not be prevented, whether the UI is fully visible or not, depending on the viewport meta-tag
- in landscape mode: scroll is totally prevented, whether the UI is fully visible or not

In in-app browser (tested in Twitter's):
- Scroll is totally prevented, whether the UI is fully visible or not
(I haven't tested with different meta-tags here, the results might change)

In standalone mode:
- Scroll might or might not be prevented, depending on the viewport meta-tag


Different options are available here, which allow to preserve the browser UI experience as well as web pages experience. Here are those I think the best and most simple:

1) Prevent body scrolling at all time when the body is set to `overflow: hidden` and, as suggested by Davison, consider that making the browser UI appear by tapping the address bar or the bottom of the viewport is enough *just like it is in the new "hide toolbar" feature which landed in iOS 13*.

2) Prevent body scrolling at all time when the body is set to `overflow: hidden` and, *as it is the case when the body is set to `position: fixed`*, display the browser UI fully as soon as the body is set to `overflow: hidden`, and leave it that way til it is set to something else. This is what Chrome on iOS does, and it works very well. Instead of blocking the browser UI collapsed (which you want to avoid), it blocks it open when the body cannot be scrolled.

3) Prevent body scrolling at all time when the body is set to `overflow: hidden` and, as suggested by shrpne, show and hide the browser UI on the user swipes without scrolling the page. Not sure how this would feel however.

The best and fastest to implement solution is probably the second one. Doing that should resolve all problems for everyone.
Comment 38 Simon Fraser (smfr) 2019-09-26 03:52:16 PDT
As I said before, please file new bugs on issues you see in iOS 13. This bug is in the FIXED state and reports of new issues here will be ignored.
Comment 39 Davison 2019-09-26 11:03:43 PDT
Simon, with all due respect, can you please clarify your intent with your recent posts?  Are you simply saying we need to open a new bug report because Bugzilla is unable to reopen this bug?

I think it is pretty clear from the recent reports that this bug isn't actually fixed at all, and IMO the proper solution would be to reopen this bug so everyone has the proper context of what has occurred so far.

If that is not technically possible then fine, we can open a new bug, but it would be nice to get some acknowledgement from developers that this issue really isn't resolved.  Since end users can't do production testing to see if a bug really is fixed until an unspecified iOS release occurs it seems odd to close out the issue without production testing.  How are we even supposed to know what bug fixes end up in a particular iOS release?
Comment 40 Frédéric Wang (:fredw) 2019-09-26 13:09:00 PDT
(In reply to Davison from comment #39)

I haven't read the details on this bug, but it is quite common that complex issues covering similar but slightly different cases are handled in different bug entries & landed as separate patches. IIUC one of the aspect is now fixed but there are remaining related issues, so Simon is just suggesting to follow the usual workflow and to open separate bugs.

The best way to help would be to split report into issues that are as isolated as possible, and for each of them provide short description and a minimal test case so that the bug can be easily reproduced and converted into non-regression test.

Regarding testing and releases, I know it's frustrating but the FAQ is quite clear [1]. The best you can do is to try the latest iOS public beta.

Hope that helps.

[1] https://trac.webkit.org/wiki/FAQ#Releases
Comment 41 Davison 2019-09-26 13:20:57 PDT
(In reply to Frédéric Wang (:fredw) from comment #40)
> Regarding testing and releases, I know it's frustrating but the FAQ is quite
> clear [1]. The best you can do is to try the latest iOS public beta.

Thanks for your reply, and I'll proceed with opening new bugs as I have time to test specific cases (assuming others don't already do so).  However, I'm still not sure how we are supposed to know whether to comment on an existing bug or open a new one if we aren't even given the information about which patches have been applied in a given iOS release.  Are we just supposed to assume that the next minor iOS release includes bug fixes that have been marked as resolved before hand?

Can we at least get the new bug reports mentioned here so people can easily know if another bug report has been made?
Comment 42 Frédéric Wang (:fredw) 2019-09-26 13:31:07 PDT
(In reply to Davison from comment #41)
> Thanks for your reply, and I'll proceed with opening new bugs as I have time
> to test specific cases (assuming others don't already do so).

Thanks, please cc' me on new bugs and I can check their status with a trunk build or help where/when I can.

>  However, I'm
> still not sure how we are supposed to know whether to comment on an existing
> bug or open a new one if we aren't even given the information about which
> patches have been applied in a given iOS release. 

I guess it's fine to open new entries, they can be resolved as "duplicate" or as "fixed" by developers (again fixed means in trunk build).

> Are we just supposed to
> assume that the next minor iOS release includes bug fixes that have been
> marked as resolved before hand?

I don't think you can assume anything unfortunately. The closer you can get is to test a beta.

> Can we at least get the new bug reports mentioned here so people can easily
> know if another bug report has been made?

Sure, you can also use "see also" to connect bugs together.