[iOS WK2] With modal overlay and body overflow:hidden, can't access all the content
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 11 Jul 2019 04:28:16 +0000 (04:28 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 11 Jul 2019 04:28:16 +0000 (04:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=199693
rdar://problem/51930364

Reviewed by Tim Horton.
Source/WebCore:

A page with overflow:hidden on the root needs to be scrollable if:
* it's zoomed
* the visual viewport is smaller than the layout viewport (including visible keyboard)
* scrolling is required to hide MobileSafari's squishy bars

This patch does the last two, plumbing a "visual viewport is smaller than layout viewport"
bit up from WebCore via the scrolling tree (that way, when it changes we automatically trigger
a commit), and checking for squished bars in WKWebView.

Tested by new API tests.

* page/FrameView.cpp:
(WebCore::FrameView::updateLayoutViewport):
(WebCore::FrameView::layoutOrVisualViewportChanged): Make this not iOS-specific. It's not yet called
in all the right places (doing so has risk because it's exposed via VisualViewport resize events).
(WebCore::FrameView::didUpdateViewportOverrideRects): Deleted.
* page/FrameView.h:
* page/scrolling/AsyncScrollingCoordinator.cpp:
(WebCore::AsyncScrollingCoordinator::frameViewVisualViewportChanged):
(WebCore::AsyncScrollingCoordinator::setFrameScrollingNodeState):
* page/scrolling/AsyncScrollingCoordinator.h:
* page/scrolling/ScrollingCoordinator.h:
(WebCore::ScrollingCoordinator::frameViewVisualViewportChanged):
* page/scrolling/ScrollingStateFrameScrollingNode.cpp:
(WebCore::ScrollingStateFrameScrollingNode::ScrollingStateFrameScrollingNode):
(WebCore::ScrollingStateFrameScrollingNode::setPropertyChangedBitsAfterReattach):
(WebCore::ScrollingStateFrameScrollingNode::setVisualViewportIsSmallerThanLayoutViewport):
(WebCore::ScrollingStateFrameScrollingNode::dumpProperties const):
* page/scrolling/ScrollingStateFrameScrollingNode.h:
* page/scrolling/ScrollingTreeFrameScrollingNode.cpp:
(WebCore::ScrollingTreeFrameScrollingNode::commitStateBeforeChildren):
(WebCore::ScrollingTreeFrameScrollingNode::dumpProperties const):
* page/scrolling/ScrollingTreeFrameScrollingNode.h:

Source/WebKit:

A page with overflow:hidden on the root needs to be scrollable if:
* it's zoomed
* the visual viewport is smaller than the layout viewport (including visible keyboard)
* scrolling is required to hide MobileSafari's squishy bars

This patch does the last two, plumbing a "visual viewport is smaller than layout viewport"
bit up from WebCore via the scrolling tree (that way, when it changes we automatically trigger
a commit), and checking for squished bars in WKWebView.

* Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp:
(ArgumentCoder<ScrollingStateFrameScrollingNode>::encode):
(ArgumentCoder<ScrollingStateFrameScrollingNode>::decode):
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _didCommitLayerTree:]):
(-[WKWebView _overrideLayoutParametersWithMinimumLayoutSize:maximumUnobscuredSizeOverride:]):
* UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp:
(WebKit::RemoteScrollingCoordinatorProxy::hasScrollableMainFrame const):
* UIProcess/WebPageProxy.h:
(WebKit::WebPageProxy::maximumUnobscuredSize const):
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::dynamicViewportSizeUpdate):
(WebKit::WebPage::updateVisibleContentRects):

Tools:

New API tests that test scrollability with various combinations of content, insets,
input accessory bars etc.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/ios/ScrollViewScrollabilityTests.mm: Added.
(TestWebKitAPI::webViewWithAutofocusedInput):
(TestWebKitAPI::TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@247344 268f45cc-cd09-0410-ab3c-d52691b4dbfc

22 files changed:
LayoutTests/fast/visual-viewport/tiled-drawing/zoomed-fixed-scrolled-down-expected.txt
LayoutTests/fast/visual-viewport/tiled-drawing/zoomed-fixed-scrolled-down-then-up-expected.txt
LayoutTests/fast/visual-viewport/tiled-drawing/zoomed-fixed-scrolling-layers-state-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/page/FrameView.cpp
Source/WebCore/page/FrameView.h
Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp
Source/WebCore/page/scrolling/AsyncScrollingCoordinator.h
Source/WebCore/page/scrolling/ScrollingCoordinator.h
Source/WebCore/page/scrolling/ScrollingStateFrameScrollingNode.cpp
Source/WebCore/page/scrolling/ScrollingStateFrameScrollingNode.h
Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.cpp
Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.h
Source/WebKit/ChangeLog
Source/WebKit/Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp
Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/ios/ScrollViewScrollabilityTests.mm [new file with mode: 0644]

index e88ccf0..fa08671 100644 (file)
@@ -18,6 +18,7 @@
   (min layout viewport origin (0,0))
   (max layout viewport origin (1223,1436))
   (behavior for fixed 0)
+  (visual viewport smaller than layout viewport 1)
   (children 4
     (Fixed node
       (anchor edges: AnchorEdgeLeft AnchorEdgeTop)
index 38f3fdf..2aebece 100644 (file)
@@ -18,6 +18,7 @@
   (min layout viewport origin (0,0))
   (max layout viewport origin (1223,1436))
   (behavior for fixed 0)
+  (visual viewport smaller than layout viewport 1)
   (children 4
     (Fixed node
       (anchor edges: AnchorEdgeLeft AnchorEdgeTop)
index 21250df..537925f 100644 (file)
@@ -15,6 +15,7 @@
   (min layout viewport origin (0,0))
   (max layout viewport origin (1223,1436))
   (behavior for fixed 0)
+  (visual viewport smaller than layout viewport 1)
   (children 4
     (Fixed node
       (anchor edges: AnchorEdgeLeft AnchorEdgeTop)
index 393bdf2..37e1d6a 100644 (file)
@@ -1,3 +1,45 @@
+2019-07-10  Simon Fraser  <simon.fraser@apple.com>
+
+        [iOS WK2] With modal overlay and body overflow:hidden, can't access all the content
+        https://bugs.webkit.org/show_bug.cgi?id=199693
+        rdar://problem/51930364
+
+        Reviewed by Tim Horton.
+
+        A page with overflow:hidden on the root needs to be scrollable if:
+        * it's zoomed
+        * the visual viewport is smaller than the layout viewport (including visible keyboard)
+        * scrolling is required to hide MobileSafari's squishy bars
+
+        This patch does the last two, plumbing a "visual viewport is smaller than layout viewport"
+        bit up from WebCore via the scrolling tree (that way, when it changes we automatically trigger
+        a commit), and checking for squished bars in WKWebView.
+
+        Tested by new API tests.
+
+        * page/FrameView.cpp:
+        (WebCore::FrameView::updateLayoutViewport):
+        (WebCore::FrameView::layoutOrVisualViewportChanged): Make this not iOS-specific. It's not yet called
+        in all the right places (doing so has risk because it's exposed via VisualViewport resize events).
+        (WebCore::FrameView::didUpdateViewportOverrideRects): Deleted.
+        * page/FrameView.h:
+        * page/scrolling/AsyncScrollingCoordinator.cpp:
+        (WebCore::AsyncScrollingCoordinator::frameViewVisualViewportChanged):
+        (WebCore::AsyncScrollingCoordinator::setFrameScrollingNodeState):
+        * page/scrolling/AsyncScrollingCoordinator.h:
+        * page/scrolling/ScrollingCoordinator.h:
+        (WebCore::ScrollingCoordinator::frameViewVisualViewportChanged):
+        * page/scrolling/ScrollingStateFrameScrollingNode.cpp:
+        (WebCore::ScrollingStateFrameScrollingNode::ScrollingStateFrameScrollingNode):
+        (WebCore::ScrollingStateFrameScrollingNode::setPropertyChangedBitsAfterReattach):
+        (WebCore::ScrollingStateFrameScrollingNode::setVisualViewportIsSmallerThanLayoutViewport):
+        (WebCore::ScrollingStateFrameScrollingNode::dumpProperties const):
+        * page/scrolling/ScrollingStateFrameScrollingNode.h:
+        * page/scrolling/ScrollingTreeFrameScrollingNode.cpp:
+        (WebCore::ScrollingTreeFrameScrollingNode::commitStateBeforeChildren):
+        (WebCore::ScrollingTreeFrameScrollingNode::dumpProperties const):
+        * page/scrolling/ScrollingTreeFrameScrollingNode.h:
+
 2019-07-10  Robin Morisset  <rmorisset@apple.com>
 
         [WHLSL] Optional<UniqueRef<T>> -> std::unique_ptr in Return/IfStatement/ForLoop
index 8b0de6b..a14098a 100644 (file)
@@ -1676,10 +1676,7 @@ void FrameView::updateLayoutViewport()
             LayoutPoint newOrigin = computeLayoutViewportOrigin(visualViewportRect(), minStableLayoutViewportOrigin(), maxStableLayoutViewportOrigin(), layoutViewport, StickToDocumentBounds);
             setLayoutViewportOverrideRect(LayoutRect(newOrigin, m_layoutViewportOverrideRect.value().size()));
         }
-        if (frame().settings().visualViewportAPIEnabled()) {
-            if (auto* window = frame().window())
-                window->visualViewport().update();
-        }
+        layoutOrVisualViewportChanged();
         return;
     }
 
@@ -1688,10 +1685,7 @@ void FrameView::updateLayoutViewport()
         setBaseLayoutViewportOrigin(newLayoutViewportOrigin);
         LOG_WITH_STREAM(Scrolling, stream << "layoutViewport changed to " << layoutViewportRect());
     }
-    if (frame().settings().visualViewportAPIEnabled()) {
-        if (auto* window = frame().window())
-            window->visualViewport().update();
-    }
+    layoutOrVisualViewportChanged();
 }
 
 LayoutPoint FrameView::minStableLayoutViewportOrigin() const
@@ -2764,17 +2758,23 @@ void FrameView::updateTiledBackingAdaptiveSizing()
     tiledBacking->setScrollability(computeScrollability());
 }
 
-#if PLATFORM(IOS_FAMILY)
-
-void FrameView::didUpdateViewportOverrideRects()
+// FIXME: This shouldn't be called from outside; FrameView should call it when the relevant viewports change.
+void FrameView::layoutOrVisualViewportChanged()
 {
     if (!frame().settings().visualViewportAPIEnabled())
         return;
 
     if (auto* window = frame().window())
         window->visualViewport().update();
+
+    if (auto* page = frame().page()) {
+        if (auto* scrollingCoordinator = page->scrollingCoordinator())
+            scrollingCoordinator->frameViewVisualViewportChanged(*this);
+    }
 }
 
+#if PLATFORM(IOS_FAMILY)
+
 void FrameView::unobscuredContentSizeChanged()
 {
     updateTiledBackingAdaptiveSizing();
index 740b56b..fc56020 100644 (file)
@@ -273,7 +273,9 @@ public:
     // This is different than visibleContentRect() in that it ignores negative (or overly positive)
     // offsets from rubber-banding, and it takes zooming into account. 
     LayoutRect viewportConstrainedVisibleContentRect() const;
-    
+
+    WEBCORE_EXPORT void layoutOrVisualViewportChanged();
+
     LayoutRect rectForFixedPositionLayout() const;
 
     void viewportContentsChanged();
@@ -596,10 +598,6 @@ public:
     void updateTiledBackingAdaptiveSizing();
     TiledBacking::Scrollability computeScrollability() const;
 
-#if PLATFORM(IOS_FAMILY)
-    WEBCORE_EXPORT void didUpdateViewportOverrideRects();
-#endif
-
     void addPaintPendingMilestones(OptionSet<LayoutMilestone>);
     void firePaintRelatedMilestonesIfNeeded();
     void fireLayoutRelatedMilestonesIfNeeded();
index ca68f76..6e8a1eb 100644 (file)
@@ -150,6 +150,29 @@ void AsyncScrollingCoordinator::frameViewLayoutUpdated(FrameView& frameView)
 #endif
 }
 
+void AsyncScrollingCoordinator::frameViewVisualViewportChanged(FrameView& frameView)
+{
+    ASSERT(isMainThread());
+    ASSERT(m_page);
+
+    if (!coordinatesScrollingForFrameView(frameView))
+        return;
+    
+    // If the root layer does not have a ScrollingStateNode, then we should create one.
+    auto* node = m_scrollingStateTree->stateNodeForID(frameView.scrollingNodeID());
+    if (!node)
+        return;
+
+    auto& frameScrollingNode = downcast<ScrollingStateFrameScrollingNode>(*node);
+
+    auto visualViewportIsSmallerThanLayoutViewport = [](const FrameView& frameView) {
+        auto layoutViewport = frameView.layoutViewportRect();
+        auto visualViewport = frameView.visualViewportRect();
+        return visualViewport.width() < layoutViewport.width() || visualViewport.height() < layoutViewport.height();
+    };
+    frameScrollingNode.setVisualViewportIsSmallerThanLayoutViewport(visualViewportIsSmallerThanLayoutViewport(frameView));
+}
+
 void AsyncScrollingCoordinator::updateExpectsWheelEventTestTriggerWithFrameView(const FrameView& frameView)
 {
     auto* page = frameView.frame().page();
@@ -630,6 +653,14 @@ void AsyncScrollingCoordinator::setFrameScrollingNodeState(ScrollingNodeID nodeI
     frameScrollingNode.setMaxLayoutViewportOrigin(frameView.maxStableLayoutViewportOrigin());
 
     frameScrollingNode.setFixedElementsLayoutRelativeToFrame(frameView.fixedElementsLayoutRelativeToFrame());
+
+    auto visualViewportIsSmallerThanLayoutViewport = [](const FrameView& frameView) {
+        auto layoutViewport = frameView.layoutViewportRect();
+        auto visualViewport = frameView.visualViewportRect();
+        return visualViewport.width() < layoutViewport.width() || visualViewport.height() < layoutViewport.height();
+    };
+    frameScrollingNode.setVisualViewportIsSmallerThanLayoutViewport(visualViewportIsSmallerThanLayoutViewport(frameView));
+    
     frameScrollingNode.setScrollBehaviorForFixedElements(frameView.scrollBehaviorForFixedElements());
 }
 
index 324bb96..05eec86 100644 (file)
@@ -93,6 +93,7 @@ private:
 
     WEBCORE_EXPORT void frameViewLayoutUpdated(FrameView&) override;
     WEBCORE_EXPORT void frameViewRootLayerDidChange(FrameView&) override;
+    WEBCORE_EXPORT void frameViewVisualViewportChanged(FrameView&) override;
     WEBCORE_EXPORT void frameViewEventTrackingRegionsChanged(FrameView&) override;
 
     WEBCORE_EXPORT bool requestScrollPositionUpdate(ScrollableArea&, const IntPoint&) override;
index 1bdfddd..84a7f4c 100644 (file)
@@ -95,6 +95,9 @@ public:
     // Should be called whenever the set of fixed objects changes.
     void frameViewFixedObjectsDidChange(FrameView&);
 
+    // Should be called whenever the FrameView's visual viewport changed.
+    virtual void frameViewVisualViewportChanged(FrameView&) { }
+
     // Called whenever the non-fast scrollable region changes for reasons other than layout.
     virtual void frameViewEventTrackingRegionsChanged(FrameView&) { }
 
index 781cdf9..6169e21 100644 (file)
@@ -57,6 +57,7 @@ ScrollingStateFrameScrollingNode::ScrollingStateFrameScrollingNode(const Scrolli
     , m_synchronousScrollingReasons(stateNode.synchronousScrollingReasons())
     , m_behaviorForFixed(stateNode.scrollBehaviorForFixedElements())
     , m_fixedElementsLayoutRelativeToFrame(stateNode.fixedElementsLayoutRelativeToFrame())
+    , m_visualViewportIsSmallerThanLayoutViewport(stateNode.visualViewportIsSmallerThanLayoutViewport())
     , m_asyncFrameOrOverflowScrollingEnabled(stateNode.asyncFrameOrOverflowScrollingEnabled())
 {
     if (hasChangedProperty(RootContentsLayer))
@@ -102,6 +103,7 @@ void ScrollingStateFrameScrollingNode::setPropertyChangedBitsAfterReattach()
     setPropertyChangedBit(BehaviorForFixedElements);
     setPropertyChangedBit(TopContentInset);
     setPropertyChangedBit(FixedElementsLayoutRelativeToFrame);
+    setPropertyChangedBit(VisualViewportIsSmallerThanLayoutViewport);
     setPropertyChangedBit(AsyncFrameOrOverflowScrollingEnabled);
     setPropertyChangedBit(LayoutViewport);
     setPropertyChangedBit(MinLayoutViewportOrigin);
@@ -255,6 +257,15 @@ void ScrollingStateFrameScrollingNode::setFooterLayer(const LayerRepresentation&
     setPropertyChanged(FooterLayer);
 }
 
+void ScrollingStateFrameScrollingNode::setVisualViewportIsSmallerThanLayoutViewport(bool visualViewportIsSmallerThanLayoutViewport)
+{
+    if (visualViewportIsSmallerThanLayoutViewport == m_visualViewportIsSmallerThanLayoutViewport)
+        return;
+    
+    m_visualViewportIsSmallerThanLayoutViewport = visualViewportIsSmallerThanLayoutViewport;
+    setPropertyChanged(VisualViewportIsSmallerThanLayoutViewport);
+}
+
 void ScrollingStateFrameScrollingNode::setFixedElementsLayoutRelativeToFrame(bool fixedElementsLayoutRelativeToFrame)
 {
     if (fixedElementsLayoutRelativeToFrame == m_fixedElementsLayoutRelativeToFrame)
@@ -329,6 +340,9 @@ void ScrollingStateFrameScrollingNode::dumpProperties(TextStream& ts, ScrollingS
     
     ts.dumpProperty("behavior for fixed", m_behaviorForFixed);
 
+    if (m_visualViewportIsSmallerThanLayoutViewport)
+        ts.dumpProperty("visual viewport smaller than layout viewport", m_visualViewportIsSmallerThanLayoutViewport);
+
     if (m_fixedElementsLayoutRelativeToFrame)
         ts.dumpProperty("fixed elements lay out relative to frame", m_fixedElementsLayoutRelativeToFrame);
 }
index 94751d1..473c442 100644 (file)
@@ -61,6 +61,7 @@ public:
         BehaviorForFixedElements,
         TopContentInset,
         FixedElementsLayoutRelativeToFrame,
+        VisualViewportIsSmallerThanLayoutViewport,
         AsyncFrameOrOverflowScrollingEnabled,
         LayoutViewport,
         MinLayoutViewportOrigin,
@@ -122,6 +123,10 @@ public:
     const LayerRepresentation& footerLayer() const { return m_footerLayer; }
     WEBCORE_EXPORT void setFooterLayer(const LayerRepresentation&);
 
+    // True when the visual viewport is smaller than the layout viewport, indicating that panning should be possible.
+    bool visualViewportIsSmallerThanLayoutViewport() const { return m_visualViewportIsSmallerThanLayoutViewport; }
+    WEBCORE_EXPORT void setVisualViewportIsSmallerThanLayoutViewport(bool);
+
     // These are more like Settings, and should probably move to the Scrolling{State}Tree itself.
     bool fixedElementsLayoutRelativeToFrame() const { return m_fixedElementsLayoutRelativeToFrame; }
     WEBCORE_EXPORT void setFixedElementsLayoutRelativeToFrame(bool);
@@ -157,6 +162,7 @@ private:
     SynchronousScrollingReasons m_synchronousScrollingReasons { 0 };
     ScrollBehaviorForFixedElements m_behaviorForFixed { StickToDocumentBounds };
     bool m_fixedElementsLayoutRelativeToFrame { false };
+    bool m_visualViewportIsSmallerThanLayoutViewport { false };
     bool m_asyncFrameOrOverflowScrollingEnabled { false };
 };
 
index bade01d..4c695f9 100644 (file)
@@ -69,6 +69,9 @@ void ScrollingTreeFrameScrollingNode::commitStateBeforeChildren(const ScrollingS
     if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::TopContentInset))
         m_topContentInset = state.topContentInset();
 
+    if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::VisualViewportIsSmallerThanLayoutViewport))
+        m_visualViewportIsSmallerThanLayoutViewport = state.visualViewportIsSmallerThanLayoutViewport();
+
     if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::FixedElementsLayoutRelativeToFrame))
         m_fixedElementsLayoutRelativeToFrame = state.fixedElementsLayoutRelativeToFrame();
 
@@ -158,6 +161,8 @@ void ScrollingTreeFrameScrollingNode::dumpProperties(TextStream& ts, ScrollingSt
     ts.dumpProperty("behavior for fixed", m_behaviorForFixed);
     if (m_fixedElementsLayoutRelativeToFrame)
         ts.dumpProperty("fixed elements lay out relative to frame", m_fixedElementsLayoutRelativeToFrame);
+    if (m_visualViewportIsSmallerThanLayoutViewport)
+        ts.dumpProperty("visual viewport is smaller than layout viewport", m_visualViewportIsSmallerThanLayoutViewport);
 }
 
 
index 35d2c1c..1aaded5 100644 (file)
@@ -44,6 +44,7 @@ public:
     SynchronousScrollingReasons synchronousScrollingReasons() const { return m_synchronousScrollingReasons; }
     bool shouldUpdateScrollLayerPositionSynchronously() const { return m_synchronousScrollingReasons; }
     bool fixedElementsLayoutRelativeToFrame() const { return m_fixedElementsLayoutRelativeToFrame; }
+    bool visualViewportIsSmallerThanLayoutViewport() const { return m_visualViewportIsSmallerThanLayoutViewport; }
 
     FloatSize viewToContentsOffset(const FloatPoint& scrollPosition) const;
     FloatRect layoutViewportForScrollPosition(const FloatPoint& visibleContentOrigin, float scale) const;
@@ -88,6 +89,7 @@ private:
     ScrollBehaviorForFixedElements m_behaviorForFixed { StickToDocumentBounds };
     
     bool m_fixedElementsLayoutRelativeToFrame { false };
+    bool m_visualViewportIsSmallerThanLayoutViewport { false };
 };
 
 } // namespace WebCore
index ccddba3..fd4cde2 100644 (file)
@@ -1,3 +1,34 @@
+2019-07-10  Simon Fraser  <simon.fraser@apple.com>
+
+        [iOS WK2] With modal overlay and body overflow:hidden, can't access all the content
+        https://bugs.webkit.org/show_bug.cgi?id=199693
+        rdar://problem/51930364
+
+        Reviewed by Tim Horton.
+
+        A page with overflow:hidden on the root needs to be scrollable if:
+        * it's zoomed
+        * the visual viewport is smaller than the layout viewport (including visible keyboard)
+        * scrolling is required to hide MobileSafari's squishy bars
+
+        This patch does the last two, plumbing a "visual viewport is smaller than layout viewport"
+        bit up from WebCore via the scrolling tree (that way, when it changes we automatically trigger
+        a commit), and checking for squished bars in WKWebView.
+
+        * Shared/RemoteLayerTree/RemoteScrollingCoordinatorTransaction.cpp:
+        (ArgumentCoder<ScrollingStateFrameScrollingNode>::encode):
+        (ArgumentCoder<ScrollingStateFrameScrollingNode>::decode):
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _didCommitLayerTree:]):
+        (-[WKWebView _overrideLayoutParametersWithMinimumLayoutSize:maximumUnobscuredSizeOverride:]):
+        * UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp:
+        (WebKit::RemoteScrollingCoordinatorProxy::hasScrollableMainFrame const):
+        * UIProcess/WebPageProxy.h:
+        (WebKit::WebPageProxy::maximumUnobscuredSize const):
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::dynamicViewportSizeUpdate):
+        (WebKit::WebPage::updateVisibleContentRects):
+
 2019-07-10  Tim Horton  <timothy_horton@apple.com>
 
         Long pressing on attachments will crash the WebContent process
index e9d024a..5f9b68f 100644 (file)
@@ -178,6 +178,7 @@ void ArgumentCoder<ScrollingStateFrameScrollingNode>::encode(Encoder& encoder, c
     SCROLLING_NODE_ENCODE(ScrollingStateFrameScrollingNode::FooterHeight, footerHeight)
     SCROLLING_NODE_ENCODE(ScrollingStateFrameScrollingNode::TopContentInset, topContentInset)
     SCROLLING_NODE_ENCODE(ScrollingStateFrameScrollingNode::FixedElementsLayoutRelativeToFrame, fixedElementsLayoutRelativeToFrame)
+    SCROLLING_NODE_ENCODE(ScrollingStateFrameScrollingNode::VisualViewportIsSmallerThanLayoutViewport, visualViewportIsSmallerThanLayoutViewport)
     // AsyncFrameOrOverflowScrollingEnabled is not relevant for UI-side compositing.
     SCROLLING_NODE_ENCODE(ScrollingStateFrameScrollingNode::LayoutViewport, layoutViewport)
     SCROLLING_NODE_ENCODE(ScrollingStateFrameScrollingNode::MinLayoutViewportOrigin, minLayoutViewportOrigin)
@@ -306,6 +307,7 @@ bool ArgumentCoder<ScrollingStateFrameScrollingNode>::decode(Decoder& decoder, S
     SCROLLING_NODE_DECODE(ScrollingStateFrameScrollingNode::FooterHeight, int, setFooterHeight);
     SCROLLING_NODE_DECODE(ScrollingStateFrameScrollingNode::TopContentInset, float, setTopContentInset);
     SCROLLING_NODE_DECODE(ScrollingStateFrameScrollingNode::FixedElementsLayoutRelativeToFrame, bool, setFixedElementsLayoutRelativeToFrame);
+    SCROLLING_NODE_DECODE(ScrollingStateFrameScrollingNode::VisualViewportIsSmallerThanLayoutViewport, bool, setVisualViewportIsSmallerThanLayoutViewport);
     SCROLLING_NODE_DECODE(ScrollingStateFrameScrollingNode::LayoutViewport, FloatRect, setLayoutViewport)
     SCROLLING_NODE_DECODE(ScrollingStateFrameScrollingNode::MinLayoutViewportOrigin, FloatPoint, setMinLayoutViewportOrigin)
     SCROLLING_NODE_DECODE(ScrollingStateFrameScrollingNode::MaxLayoutViewportOrigin, FloatPoint, setMaxLayoutViewportOrigin)
index 527fe0e..93629fe 100644 (file)
@@ -2025,7 +2025,16 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
 #if ENABLE(ASYNC_SCROLLING)
     bool hasDockedInputView = !CGRectIsEmpty(_inputViewBounds);
     bool isZoomed = layerTreeTransaction.pageScaleFactor() > layerTreeTransaction.initialScaleFactor();
-    [_scrollView _setScrollEnabledInternal:_page->scrollingCoordinatorProxy()->hasScrollableMainFrame() || hasDockedInputView || isZoomed];
+
+    bool scrollingNeededToRevealUI = false;
+    if (_maximumUnobscuredSizeOverride) {
+        auto unobscuredContentRect = _page->unobscuredContentRect();
+        auto maxUnobscuredSize = _page->maximumUnobscuredSize();
+        scrollingNeededToRevealUI = maxUnobscuredSize.width() == unobscuredContentRect.width() && maxUnobscuredSize.height() == unobscuredContentRect.height();
+    }
+
+    bool scrollingEnabled = _page->scrollingCoordinatorProxy()->hasScrollableMainFrame() || hasDockedInputView || isZoomed || scrollingNeededToRevealUI;
+    [_scrollView _setScrollEnabledInternal:scrollingEnabled];
 #endif
     if (!layerTreeTransaction.scaleWasSetByUIProcess() && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _isAnimatingZoom] && [_scrollView zoomScale] != layerTreeTransaction.pageScaleFactor()) {
         LOG_WITH_STREAM(VisibleRects, stream << " updating scroll view with pageScaleFactor " << layerTreeTransaction.pageScaleFactor());
@@ -6300,6 +6309,8 @@ static inline WebKit::FindOptions toFindOptions(_WKFindOptions wkFindOptions)
 
 - (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
 {
+    LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->pageID() << " _overrideLayoutParametersWithMinimumLayoutSize:" << WebCore::FloatSize(minimumLayoutSize) << " maximumUnobscuredSizeOverride:" << WebCore::FloatSize(maximumUnobscuredSizeOverride) << "]");
+
     [self _setViewLayoutSizeOverride:minimumLayoutSize];
     [self _setMaximumUnobscuredSizeOverride:maximumUnobscuredSizeOverride];
 }
index faa41dc..02ab4bb 100644 (file)
@@ -244,7 +244,10 @@ String RemoteScrollingCoordinatorProxy::scrollingTreeAsText() const
 bool RemoteScrollingCoordinatorProxy::hasScrollableMainFrame() const
 {
     auto* rootNode = m_scrollingTree->rootNode();
-    return rootNode && rootNode->canHaveScrollbars();
+    if (!rootNode)
+        return false;
+
+    return rootNode->canHaveScrollbars() || rootNode->visualViewportIsSmallerThanLayoutViewport();
 }
 
 #if ENABLE(POINTER_EVENTS)
index b617d6c..7a59e4e 100644 (file)
@@ -660,6 +660,7 @@ public:
 
     void setViewportConfigurationViewLayoutSize(const WebCore::FloatSize&, double scaleFactor, double minimumEffectiveDeviceWidth);
     void setMaximumUnobscuredSize(const WebCore::FloatSize&);
+    WebCore::FloatSize maximumUnobscuredSize() const { return m_maximumUnobscuredSize; }
     void setDeviceOrientation(int32_t);
     int32_t deviceOrientation() const { return m_deviceOrientation; }
     void setOverrideViewportArguments(const Optional<WebCore::ViewportArguments>&);
index 539fbd1..114398c 100644 (file)
@@ -3243,6 +3243,7 @@ void WebPage::dynamicViewportSizeUpdate(const FloatSize& viewLayoutSize, const W
     auto layoutViewportSize = FrameView::expandedLayoutViewportSize(frameView.baseLayoutViewportSize(), LayoutSize(documentRect.size()), settings.layoutViewportHeightExpansionFactor());
     LayoutRect layoutViewportRect = FrameView::computeUpdatedLayoutViewportRect(frameView.layoutViewportRect(), documentRect, LayoutSize(newUnobscuredContentRect.size()), LayoutRect(newUnobscuredContentRect), layoutViewportSize, frameView.minStableLayoutViewportOrigin(), frameView.maxStableLayoutViewportOrigin(), FrameView::LayoutViewportConstraint::ConstrainedToDocumentRect);
     frameView.setLayoutViewportOverrideRect(layoutViewportRect);
+    frameView.layoutOrVisualViewportChanged();
 
     frameView.setCustomSizeForResizeEvent(expandedIntSize(targetUnobscuredRectInScrollViewCoordinates.size()));
     setDeviceOrientation(deviceOrientation);
@@ -3640,7 +3641,7 @@ void WebPage::updateVisibleContentRects(const VisibleContentRectUpdateInfo& visi
             scheduleFullEditorStateUpdate();
         }
 
-        frameView.didUpdateViewportOverrideRects();
+        frameView.layoutOrVisualViewportChanged();
     }
 
     if (!visibleContentRectUpdateInfo.isChangingObscuredInsetsInteractively())
index 622915e..e24b908 100644 (file)
@@ -1,3 +1,19 @@
+2019-07-10  Simon Fraser  <simon.fraser@apple.com>
+
+        [iOS WK2] With modal overlay and body overflow:hidden, can't access all the content
+        https://bugs.webkit.org/show_bug.cgi?id=199693
+        rdar://problem/51930364
+
+        Reviewed by Tim Horton.
+        
+        New API tests that test scrollability with various combinations of content, insets,
+        input accessory bars etc.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/ios/ScrollViewScrollabilityTests.mm: Added.
+        (TestWebKitAPI::webViewWithAutofocusedInput):
+        (TestWebKitAPI::TEST):
+
 2019-07-10  Tim Horton  <timothy_horton@apple.com>
 
         Long pressing on attachments will crash the WebContent process
index a57f829..1eca950 100644 (file)
@@ -50,6 +50,7 @@
                0F4FFA9E1ED3AA8500F7111F /* SnapshotViaRenderInContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F4FFA9D1ED3AA8500F7111F /* SnapshotViaRenderInContext.mm */; };
                0F5651F71FCE4DDC00310FBC /* NoHistoryItemScrollToFragment.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F5651F61FCE4DDB00310FBC /* NoHistoryItemScrollToFragment.mm */; };
                0F5651F91FCE513500310FBC /* scroll-to-anchor.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 0F5651F81FCE50E800310FBC /* scroll-to-anchor.html */; };
+               0FF1134E22D68679009A81DA /* ScrollViewScrollabilityTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0FF1134D22D68679009A81DA /* ScrollViewScrollabilityTests.mm */; };
                115EB3431EE0BA03003C2C0A /* ViewportSizeForViewportUnits.mm in Sources */ = {isa = PBXBuildFile; fileRef = 115EB3421EE0B720003C2C0A /* ViewportSizeForViewportUnits.mm */; };
                1171B24F219F49CD00CB897D /* FirstMeaningfulPaintMilestone_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 11B7FD21219F46DD0069B27F /* FirstMeaningfulPaintMilestone_Bundle.cpp */; };
                118153442208B7AC00B2CCD2 /* deferred-script-load.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 118153432208B7AC00B2CCD2 /* deferred-script-load.html */; };
                        dstPath = TestWebKitAPI.resources;
                        dstSubfolderSpec = 7;
                        files = (
-                               93D119FC22C680F7009BE3C7 /* localstorage-open-window-private.html in Copy Resources */,
                                55A817FF2181021A0004A39A /* 100x100-red.tga in Copy Resources */,
                                1A9E52C913E65EF4006917F5 /* 18-characters.html in Copy Resources */,
                                55A81800218102210004A39A /* 400x400-green.png in Copy Resources */,
                                9368A25E229EFB4700A829CA /* local-storage-process-suspends-1.html in Copy Resources */,
                                9368A25F229EFB4700A829CA /* local-storage-process-suspends-2.html in Copy Resources */,
                                8C10AF98206467920018FD90 /* localstorage-empty-string-value.html in Copy Resources */,
+                               93D119FC22C680F7009BE3C7 /* localstorage-open-window-private.html in Copy Resources */,
                                51E6A8961D2F1CA700C004B6 /* LocalStorageClear.html in Copy Resources */,
                                46C519E61D3563FD00DAA51A /* LocalStorageNullEntries.html in Copy Resources */,
                                46C519E71D3563FD00DAA51A /* LocalStorageNullEntries.localstorage in Copy Resources */,
                0FC6C4CE141034AD005B7F0C /* MetaAllocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MetaAllocator.cpp; sourceTree = "<group>"; };
                0FE447971B76F1E3009498EB /* ParkingLot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParkingLot.cpp; sourceTree = "<group>"; };
                0FEAE3671B7D19CB00CE17F2 /* Condition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Condition.cpp; sourceTree = "<group>"; };
+               0FF1134D22D68679009A81DA /* ScrollViewScrollabilityTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollViewScrollabilityTests.mm; sourceTree = "<group>"; };
                0FFC45A41B73EBE20085BD62 /* Lock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Lock.cpp; sourceTree = "<group>"; };
                115EB3421EE0B720003C2C0A /* ViewportSizeForViewportUnits.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewportSizeForViewportUnits.mm; sourceTree = "<group>"; };
                118153432208B7AC00B2CCD2 /* deferred-script-load.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "deferred-script-load.html"; sourceTree = "<group>"; };
                                5C7148942123A40700FDE3C5 /* WKWebsiteDatastore.mm */,
                                371195AA1FE5797700A1FB92 /* WKWebViewAlwaysShowsScroller.mm */,
                                2EFF06D61D8AF34A0004BB30 /* WKWebViewCandidateTests.mm */,
+                               CDD68F0C22C18317000CF0AE /* WKWebViewCloseAllMediaPresentations.mm */,
                                5CB3CE381FA1691700C3A2D6 /* WKWebViewConfiguration.mm */,
                                7C417F311D19E14800B8EF53 /* WKWebViewDefaultNavigationDelegate.mm */,
                                46E66A8F1F0D75590026D83C /* WKWebViewDiagnosticLogging.mm */,
                                93F56DA81E5F9181003EDE84 /* WKWebViewSnapshot.mm */,
                                CD7F89DB22A86CDA00D683AE /* WKWebViewSuspendAllMediaPlayback.mm */,
                                9984FACA1CFFAEEE008D198C /* WKWebViewTextInput.mm */,
-                               CDD68F0C22C18317000CF0AE /* WKWebViewCloseAllMediaPresentations.mm */,
                        );
                        name = "WebKit Cocoa";
                        path = WebKitCocoa;
                                7560917719259C59009EF06E /* MemoryCacheAddImageToCacheIOS.mm */,
                                F464AF9120BB66EA007F9B18 /* RenderingProgressTests.mm */,
                                F4C8797E2059D8D3009CD00B /* ScrollViewInsetTests.mm */,
+                               0FF1134D22D68679009A81DA /* ScrollViewScrollabilityTests.mm */,
                                CE6E819F20A6935F00E2C80F /* SetTimeoutFunction.mm */,
                                4433A395208044130091ED57 /* SynchronousTimeoutTests.mm */,
                                F45E15742112CE6200307E82 /* TestInputDelegate.h */,
                                CA38459620AE17A900990D3B /* LocalStorageDatabaseTracker.mm in Sources */,
                                46C519DA1D355AB200DAA51A /* LocalStorageNullEntries.mm in Sources */,
                                8C10AF99206467A90018FD90 /* LocalStoragePersistence.mm in Sources */,
-                               CDD68F0D22C18317000CF0AE /* WKWebViewCloseAllMediaPresentations.mm in Sources */,
                                7A6A2C701DCCFA8C00C0D085 /* LocalStorageQuirkTest.mm in Sources */,
                                076E507F1F4513D6006E9F5A /* Logging.cpp in Sources */,
                                CE1866491F72E8F100A0CAB6 /* MarkedText.cpp in Sources */,
                                CDC0932B21C872C10030C4B0 /* ScrollingDoesNotPauseMedia.mm in Sources */,
                                7CCE7F121A411AE600447C4C /* ScrollPinningBehaviors.cpp in Sources */,
                                F4C8797F2059D8D3009CD00B /* ScrollViewInsetTests.mm in Sources */,
+                               0FF1134E22D68679009A81DA /* ScrollViewScrollabilityTests.mm in Sources */,
                                CE06DF9B1E1851F200E570C9 /* SecurityOrigin.cpp in Sources */,
                                5769C50B1D9B0002000847FB /* SerializedCryptoKeyWrap.mm in Sources */,
                                51EB12941FDF052500A5A1BD /* ServiceWorkerBasic.mm in Sources */,
                                371195AB1FE5797700A1FB92 /* WKWebViewAlwaysShowsScroller.mm in Sources */,
                                514958BE1F7427AC00E87BAD /* WKWebViewAutofillTests.mm in Sources */,
                                2EFF06D71D8AF34A0004BB30 /* WKWebViewCandidateTests.mm in Sources */,
+                               CDD68F0D22C18317000CF0AE /* WKWebViewCloseAllMediaPresentations.mm in Sources */,
                                5CB3CE391FA1697F00C3A2D6 /* WKWebViewConfiguration.mm in Sources */,
                                A14FC5851B89739100D107EB /* WKWebViewConfigurationExtras.mm in Sources */,
                                7C417F331D19E14800B8EF53 /* WKWebViewDefaultNavigationDelegate.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/ios/ScrollViewScrollabilityTests.mm b/Tools/TestWebKitAPI/Tests/ios/ScrollViewScrollabilityTests.mm
new file mode 100644 (file)
index 0000000..e012d1c
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if PLATFORM(IOS_FAMILY)
+
+#import "PlatformUtilities.h"
+#import "TestInputDelegate.h"
+#import "TestWKWebView.h"
+#import <UIKit/UIKit.h>
+#import <WebKit/WKWebViewPrivate.h>
+
+namespace TestWebKitAPI {
+
+static NSString *scrollableDocumentMarkup = @"<meta name='viewport' content='width=device-width, initial-scale=1'><body style='width: 100%; height: 5000px;'>";
+static NSString *nonScrollableDocumentMarkup = @"<meta name='viewport' content='width=device-width, initial-scale=1'><body style='width: 100%; height: 5000px; overflow: hidden'>";
+static NSString *nonScrollableWithInputDocumentMarkup = @"<meta name='viewport' content='width=device-width, initial-scale=1'><body style='width: 100%; height: 5000px; overflow: hidden'><input autofocus>";
+
+static const CGFloat viewHeight = 500;
+
+static RetainPtr<TestWKWebView> webViewWithAutofocusedInput(const RetainPtr<TestInputDelegate>& inputDelegate)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, viewHeight)]);
+
+    bool doneWaiting = false;
+    [inputDelegate setFocusStartsInputSessionPolicyHandler:[&] (WKWebView *, id <_WKFocusedElementInfo>) -> _WKFocusStartsInputSessionPolicy {
+        doneWaiting = true;
+        return _WKFocusStartsInputSessionPolicyAllow;
+    }];
+    [webView _setInputDelegate:inputDelegate.get()];
+    [webView synchronouslyLoadHTMLString:nonScrollableWithInputDocumentMarkup];
+
+    TestWebKitAPI::Util::run(&doneWaiting);
+    doneWaiting = false;
+    return webView;
+}
+
+TEST(ScrollViewScrollabilityTests, ScrollableWithOverflowVisible)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, viewHeight)]);
+    [webView synchronouslyLoadHTMLString:scrollableDocumentMarkup];
+    EXPECT_EQ([[webView scrollView] isScrollEnabled], YES);
+}
+
+TEST(ScrollViewScrollabilityTests, NonScrollableWithOverflowHidden)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, viewHeight)]);
+    [webView synchronouslyLoadHTMLString:nonScrollableDocumentMarkup];
+    EXPECT_EQ([[webView scrollView] isScrollEnabled], NO);
+}
+
+TEST(ScrollViewScrollabilityTests, ScrollableWithOverflowHiddenWhenZoomed)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, viewHeight)]);
+    [webView synchronouslyLoadHTMLString:nonScrollableDocumentMarkup];
+    [[webView scrollView] setZoomScale:1.5 animated:NO];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_EQ([[webView scrollView] isScrollEnabled], YES);
+}
+
+TEST(ScrollViewScrollabilityTests, ScrollableWithOverflowHiddenAndInputView)
+{
+    auto inputView = adoptNS([[UIView alloc] init]);
+    auto inputAccessoryView = adoptNS([[UIView alloc] init]);
+    auto inputDelegate = adoptNS([TestInputDelegate new]);
+    [inputDelegate setWillStartInputSessionHandler:[inputView, inputAccessoryView] (WKWebView *, id<_WKFormInputSession> session) {
+        session.customInputView = inputView.get();
+        session.customInputAccessoryView = inputAccessoryView.get();
+    }];
+
+    auto webView = webViewWithAutofocusedInput(inputDelegate);
+    [webView waitForNextPresentationUpdate];
+    EXPECT_EQ([[webView scrollView] isScrollEnabled], YES);
+}
+
+TEST(ScrollViewScrollabilityTests, ScrollableWithOverflowHiddenAndVisibleUI)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, viewHeight)]);
+
+    // Simulate portrait phone with:
+    // Top bar size: 50
+    // Shrunk top bar size: 40
+    // Bottom bar size: 44
+    UIEdgeInsets obscuredInsets;
+    obscuredInsets.top = 50;
+    obscuredInsets.left = 0;
+    obscuredInsets.bottom = 44;
+    obscuredInsets.right = 0;
+
+    [webView _setObscuredInsets:obscuredInsets];
+    [webView _overrideLayoutParametersWithMinimumLayoutSize:CGSizeMake(320, 406) maximumUnobscuredSizeOverride:CGSizeMake(320, 490)];
+
+    [webView synchronouslyLoadHTMLString:nonScrollableDocumentMarkup];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_EQ([[webView scrollView] isScrollEnabled], NO);
+}
+
+TEST(ScrollViewScrollabilityTests, ScrollableWithOverflowHiddenAndShrunkUI)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, viewHeight, 414)]);
+
+    // Simulate landscape phone with hidden bars.
+    UIEdgeInsets obscuredInsets;
+    obscuredInsets.top = 0;
+    obscuredInsets.left = 0;
+    obscuredInsets.bottom = 0;
+    obscuredInsets.right = 0;
+
+    [webView _setObscuredInsets:obscuredInsets];
+    [webView _overrideLayoutParametersWithMinimumLayoutSize:CGSizeMake(viewHeight, 414) maximumUnobscuredSizeOverride:CGSizeMake(viewHeight, 414)];
+
+    [webView synchronouslyLoadHTMLString:nonScrollableDocumentMarkup];
+    [webView waitForNextPresentationUpdate];
+    EXPECT_EQ([[webView scrollView] isScrollEnabled], YES);
+}
+
+} // namespace TestWebKitAPI
+
+#endif // PLATFORM(IOS_FAMILY)