overflow:scroll elements should not latch to the body if the body is
authorbdakin@apple.com <bdakin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Aug 2014 19:53:31 +0000 (19:53 +0000)
committerbdakin@apple.com <bdakin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Aug 2014 19:53:31 +0000 (19:53 +0000)
overflow:hidden
https://bugs.webkit.org/show_bug.cgi?id=136273

Reviewed by Darin Adler.

Source/WebCore:

This patch adds an optional parameter to isScrollable(). The Scrollability
parameter that allows the caller to define what they mean by 'scrollable.' Most
callers are interested in the default value, Scrollability::Scrollable, which
means that there is actually content to scroll to, and a scrollbar that will allow
you to access it. In some cases, such as this latching case, callers want to know
if the FrameView is allowed to rubber-band, which the main frame might be allowed
to do even if there is no content to scroll to. In that case, callers use
Scrollability::ScrollableOrRubberbandable.
* page/FrameView.cpp:
(WebCore::FrameView::isScrollable):
(WebCore::FrameView::hasScrollableOrRubberbandableAncestor):

New virtual function on ScrollableArea answers whether a ScrollableArea has any
scrollable or rubber-bandable ancestor.
* page/FrameView.h:
* platform/ScrollableArea.h:

Events should only be allowed to prevent stretching if there is some ancestor
ScrollableArea that will be able to make use of the event.
* platform/mac/ScrollAnimatorMac.mm:
(WebCore::ScrollAnimatorMac::allowsVerticalStretching):
(WebCore::ScrollAnimatorMac::allowsHorizontalStretching):

New ScrollableArea virtual function.
* platform/win/PopupMenuWin.h:

New RenderBox isScrollableOrRubberbandable() returns just
canBeScrolledAndHasScrollableArea() for most RenderBox, but will be overridden by
RenderView.
* rendering/RenderBox.cpp:
(WebCore::RenderBox::isScrollableOrRubberbandable):
* rendering/RenderBox.h:

Recurse up the parent chain to find out if anything is scrollable or just rubber-
bandable.
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::hasScrollableOrRubberbandableAncestor):
* rendering/RenderLayer.h:

Call into RenderLayer.
* rendering/RenderListBox.cpp:
(WebCore::RenderListBox::hasScrollableOrRubberbandableAncestor):
* rendering/RenderListBox.h:

Override isScrollableOrRubberbandable() to handle the main frame case where the
main frame is typically allowed to rubber-band even if there is no content to
scroll to.
* rendering/RenderView.cpp:
(WebCore::RenderView::isScrollableOrRubberbandable):
* rendering/RenderView.h:

Source/WebKit2:

New ScrollabeArea virtual function.
* WebProcess/Plugins/PDF/PDFPlugin.h:

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

16 files changed:
Source/WebCore/ChangeLog
Source/WebCore/page/FrameView.cpp
Source/WebCore/page/FrameView.h
Source/WebCore/platform/ScrollableArea.h
Source/WebCore/platform/mac/ScrollAnimatorMac.mm
Source/WebCore/platform/win/PopupMenuWin.h
Source/WebCore/rendering/RenderBox.cpp
Source/WebCore/rendering/RenderBox.h
Source/WebCore/rendering/RenderLayer.cpp
Source/WebCore/rendering/RenderLayer.h
Source/WebCore/rendering/RenderListBox.cpp
Source/WebCore/rendering/RenderListBox.h
Source/WebCore/rendering/RenderView.cpp
Source/WebCore/rendering/RenderView.h
Source/WebKit2/ChangeLog
Source/WebKit2/WebProcess/Plugins/PDF/PDFPlugin.h

index 1aeadc1..b8d8065 100644 (file)
@@ -1,3 +1,62 @@
+2014-08-27  Beth Dakin  <bdakin@apple.com>
+
+        overflow:scroll elements should not latch to the body if the body is 
+        overflow:hidden
+        https://bugs.webkit.org/show_bug.cgi?id=136273
+
+        Reviewed by Darin Adler.
+
+        This patch adds an optional parameter to isScrollable(). The Scrollability 
+        parameter that allows the caller to define what they mean by 'scrollable.' Most 
+        callers are interested in the default value, Scrollability::Scrollable, which 
+        means that there is actually content to scroll to, and a scrollbar that will allow 
+        you to access it. In some cases, such as this latching case, callers want to know 
+        if the FrameView is allowed to rubber-band, which the main frame might be allowed 
+        to do even if there is no content to scroll to. In that case, callers use 
+        Scrollability::ScrollableOrRubberbandable.
+        * page/FrameView.cpp:
+        (WebCore::FrameView::isScrollable):
+        (WebCore::FrameView::hasScrollableOrRubberbandableAncestor):
+
+        New virtual function on ScrollableArea answers whether a ScrollableArea has any 
+        scrollable or rubber-bandable ancestor.
+        * page/FrameView.h:
+        * platform/ScrollableArea.h:
+
+        Events should only be allowed to prevent stretching if there is some ancestor 
+        ScrollableArea that will be able to make use of the event. 
+        * platform/mac/ScrollAnimatorMac.mm:
+        (WebCore::ScrollAnimatorMac::allowsVerticalStretching):
+        (WebCore::ScrollAnimatorMac::allowsHorizontalStretching):
+
+        New ScrollableArea virtual function.
+        * platform/win/PopupMenuWin.h:
+
+        New RenderBox isScrollableOrRubberbandable() returns just 
+        canBeScrolledAndHasScrollableArea() for most RenderBox, but will be overridden by 
+        RenderView.
+        * rendering/RenderBox.cpp:
+        (WebCore::RenderBox::isScrollableOrRubberbandable):
+        * rendering/RenderBox.h:
+
+        Recurse up the parent chain to find out if anything is scrollable or just rubber-
+        bandable.
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::hasScrollableOrRubberbandableAncestor):
+        * rendering/RenderLayer.h:
+
+        Call into RenderLayer.
+        * rendering/RenderListBox.cpp:
+        (WebCore::RenderListBox::hasScrollableOrRubberbandableAncestor):
+        * rendering/RenderListBox.h:
+
+        Override isScrollableOrRubberbandable() to handle the main frame case where the 
+        main frame is typically allowed to rubber-band even if there is no content to 
+        scroll to.
+        * rendering/RenderView.cpp:
+        (WebCore::RenderView::isScrollableOrRubberbandable):
+        * rendering/RenderView.h:
+
 2014-08-27  Benjamin Poulain  <bpoulain@apple.com>
 
         Updating attributes on HTML elements do not invalidate the style correctly unless the attribute name is lowercase in the stylesheet
index 3cf0cb1..166a208 100644 (file)
@@ -3265,7 +3265,7 @@ IntRect FrameView::scrollableAreaBoundingBox() const
     return ownerRenderer->absoluteContentQuad().enclosingBoundingBox();
 }
 
-bool FrameView::isScrollable()
+bool FrameView::isScrollable(Scrollability definitionOfScrollable)
 {
     // Check for:
     // 1) If there an actual overflow.
@@ -3273,11 +3273,18 @@ bool FrameView::isScrollable()
     // 3) overflow{-x,-y}: hidden;
     // 4) scrolling: no;
 
+    bool requiresActualOverflowToBeConsideredScrollable = !frame().isMainFrame() || definitionOfScrollable != Scrollability::ScrollableOrRubberbandable;
+#if !ENABLE(RUBBER_BANDING)
+    requiresActualOverflowToBeConsideredScrollable = true;
+#endif
+
     // Covers #1
-    IntSize totalContentsSize = this->totalContentsSize();
-    IntSize visibleContentSize = visibleContentRect(LegacyIOSDocumentVisibleRect).size();
-    if ((totalContentsSize.height() <= visibleContentSize.height() && totalContentsSize.width() <= visibleContentSize.width()))
-        return false;
+    if (requiresActualOverflowToBeConsideredScrollable) {
+        IntSize totalContentsSize = this->totalContentsSize();
+        IntSize visibleContentSize = visibleContentRect(LegacyIOSDocumentVisibleRect).size();
+        if ((totalContentsSize.height() <= visibleContentSize.height() && totalContentsSize.width() <= visibleContentSize.width()))
+            return false;
+    }
 
     // Covers #2.
     HTMLFrameOwnerElement* owner = frame().ownerElement();
@@ -3294,6 +3301,23 @@ bool FrameView::isScrollable()
     return true;
 }
 
+bool FrameView::hasScrollableOrRubberbandableAncestor()
+{
+    if (frame().isMainFrame())
+        return isScrollable(Scrollability::ScrollableOrRubberbandable);
+
+    FrameView* parentFrameView = this->parentFrameView();
+    if (!parentFrameView)
+        return false;
+
+    RenderView* parentRenderView = parentFrameView->renderView();
+    if (!parentRenderView)
+        return false;
+
+    RenderLayer* enclosingLayer = parentRenderView->enclosingLayer();
+    return enclosingLayer && enclosingLayer->hasScrollableOrRubberbandableAncestor();
+}
+
 void FrameView::updateScrollableAreaSet()
 {
     // That ensures that only inner frames are cached.
index f3c70e7..ac9198d 100644 (file)
@@ -396,7 +396,14 @@ public:
 
     bool isFrameViewScrollCorner(RenderScrollbarPart* scrollCorner) const { return m_scrollCorner == scrollCorner; }
 
-    bool isScrollable();
+    // isScrollable() takes an optional Scrollability parameter that allows the caller to define what they mean by 'scrollable.'
+    // Most callers are interested in the default value, Scrollability::Scrollable, which means that there is actually content
+    // to scroll to, and a scrollbar that will allow you to access it. In some cases, callers want to know if the FrameView is allowed
+    // to rubber-band, which the main frame might be allowed to do even if there is no content to scroll to. In that case,
+    // callers use Scrollability::ScrollableOrRubberbandable.
+    enum class Scrollability { Scrollable, ScrollableOrRubberbandable };
+    bool isScrollable(Scrollability definitionOfScrollable = Scrollability::Scrollable);
+    virtual bool hasScrollableOrRubberbandableAncestor() override;
 
     enum ScrollbarModesCalculationStrategy { RulesFromWebContentOnly, AnyRule };
     void calculateScrollbarModesForLayout(ScrollbarMode& hMode, ScrollbarMode& vMode, ScrollbarModesCalculationStrategy = AnyRule);
index 443cd54..907b012 100644 (file)
@@ -207,6 +207,8 @@ public:
     // Note that this only returns scrollable areas that can actually be scrolled.
     virtual ScrollableArea* enclosingScrollableArea() const = 0;
 
+    virtual bool hasScrollableOrRubberbandableAncestor() = 0;
+
     // Returns the bounding box of this scrollable area, in the coordinate system of the enclosing scroll view.
     virtual IntRect scrollableAreaBoundingBox() const = 0;
 
index 3c0689a..43f909e 100644 (file)
@@ -1158,7 +1158,7 @@ bool ScrollAnimatorMac::allowsVerticalStretching(const PlatformWheelEvent& wheel
         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
         bool scrollbarsAllowStretching = ((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled()));
-        bool eventPreventsStretching = newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Vertical);
+        bool eventPreventsStretching = m_scrollableArea->hasScrollableOrRubberbandableAncestor() && newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Vertical);
         return scrollbarsAllowStretching && !eventPreventsStretching;
     }
     case ScrollElasticityNone:
@@ -1178,7 +1178,7 @@ bool ScrollAnimatorMac::allowsHorizontalStretching(const PlatformWheelEvent& whe
         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
         bool scrollbarsAllowStretching = ((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled()));
-        bool eventPreventsStretching = newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Horizontal);
+        bool eventPreventsStretching = m_scrollableArea->hasScrollableOrRubberbandableAncestor() && newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Horizontal);
         return scrollbarsAllowStretching && !eventPreventsStretching;
     }
     case ScrollElasticityNone:
index 8411b5f..bb9d1f6 100644 (file)
@@ -95,6 +95,7 @@ private:
     virtual void invalidateScrollCornerRect(const IntRect&) override { }
     virtual bool isActive() const override { return true; }
     ScrollableArea* enclosingScrollableArea() const override { return 0; }
+    virtual bool hasScrollableOrRubberbandableAncestor() override { return true; }
     virtual bool isScrollCornerVisible() const override { return false; }
     virtual IntRect scrollCornerRect() const override { return IntRect(); }
     virtual Scrollbar* verticalScrollbar() const override { return m_scrollbar.get(); }
index 03c8cfe..3abcee9 100644 (file)
@@ -835,7 +835,12 @@ bool RenderBox::canBeScrolledAndHasScrollableArea() const
 {
     return canBeProgramaticallyScrolled() && (scrollHeight() != clientHeight() || scrollWidth() != clientWidth());
 }
-    
+
+bool RenderBox::isScrollableOrRubberbandable() const
+{
+    return canBeScrolledAndHasScrollableArea();
+}
+
 bool RenderBox::canBeProgramaticallyScrolled() const
 {
     if (isRenderView())
index 70858ac..4289815 100644 (file)
@@ -462,6 +462,7 @@ public:
     virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1, Element** stopElement = nullptr, RenderBox* startBox = nullptr, const IntPoint& wheelEventAbsolutePoint = IntPoint());
     virtual bool logicalScroll(ScrollLogicalDirection, ScrollGranularity, float multiplier = 1, Element** stopElement = 0);
     bool canBeScrolledAndHasScrollableArea() const;
+    virtual bool isScrollableOrRubberbandable() const;
     virtual bool canBeProgramaticallyScrolled() const;
     virtual void autoscroll(const IntPoint&);
     bool canAutoscroll() const;
index 5881d29..020d626 100644 (file)
@@ -3065,6 +3065,16 @@ ScrollableArea* RenderLayer::enclosingScrollableArea() const
     return 0;
 }
 
+bool RenderLayer::hasScrollableOrRubberbandableAncestor()
+{
+    for (RenderLayer* nextLayer = parentLayerCrossFrame(this); nextLayer; nextLayer = parentLayerCrossFrame(nextLayer)) {
+        if (nextLayer->renderer().isBox() && toRenderBox(nextLayer->renderer()).isScrollableOrRubberbandable())
+            return true;
+    }
+
+    return false;
+}
+
 #if ENABLE(CSS_SCROLL_SNAP)
 void RenderLayer::updateSnapOffsets()
 {
index 9c4898c..0922bfe 100644 (file)
@@ -439,6 +439,7 @@ public:
     virtual Scrollbar* horizontalScrollbar() const override { return m_hBar.get(); }
     virtual Scrollbar* verticalScrollbar() const override { return m_vBar.get(); }
     virtual ScrollableArea* enclosingScrollableArea() const override;
+    virtual bool hasScrollableOrRubberbandableAncestor() override;
 #if ENABLE(CSS_SCROLL_SNAP)
     virtual void updateSnapOffsets() override;
 #endif
index 060d3ce..d42c3a5 100644 (file)
@@ -787,6 +787,11 @@ ScrollableArea* RenderListBox::enclosingScrollableArea() const
     return 0;
 }
 
+bool RenderListBox::hasScrollableOrRubberbandableAncestor()
+{
+    return enclosingLayer() && enclosingLayer()->hasScrollableOrRubberbandableAncestor();
+}
+
 IntRect RenderListBox::scrollableAreaBoundingBox() const
 {
     return absoluteBoundingBoxRect();
index 600b7d4..10ab05d 100644 (file)
@@ -131,6 +131,7 @@ private:
     virtual bool forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const override;
 
     virtual ScrollableArea* enclosingScrollableArea() const override;
+    virtual bool hasScrollableOrRubberbandableAncestor() override;
     virtual IntRect scrollableAreaBoundingBox() const override;
 
     // NOTE: This should only be called by the overriden setScrollOffset from ScrollableArea.
index 290c702..d272528 100644 (file)
@@ -710,6 +710,14 @@ void RenderView::computeRectForRepaint(const RenderLayerModelObject* repaintCont
         rect = LayoutRect(layer()->transform()->mapRect(pixelSnappedForPainting(rect, document().deviceScaleFactor())));
 }
 
+bool RenderView::isScrollableOrRubberbandable() const
+{
+    // The main frame might be allowed to rubber-band even if there is no content to scroll to. This is unique to
+    // the main frame; subframes and overflow areas have to have content that can be scrolled to in order to rubber-band.
+    FrameView::Scrollability defineScrollable = frame().ownerElement() ? FrameView::Scrollability::Scrollable : FrameView::Scrollability::ScrollableOrRubberbandable;
+    return frameView().isScrollable(defineScrollable);
+}
+
 void RenderView::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
 {
     rects.append(pixelSnappedIntRect(accumulatedOffset, layer()->size()));
index 2032006..25c0ef7 100644 (file)
@@ -303,6 +303,8 @@ private:
     friend class LayoutStateMaintainer;
     friend class LayoutStateDisabler;
 
+    virtual bool isScrollableOrRubberbandable() const;
+
     void splitSelectionBetweenSubtrees(RenderObject* start, int startPos, RenderObject* end, int endPos, SelectionRepaintMode blockRepaintMode);
     void clearSubtreeSelection(const SelectionSubtreeRoot&, SelectionRepaintMode, OldSelectionData&);
     void updateSelectionForSubtrees(RenderSubtreesMap&, SelectionRepaintMode);
index dc66e37..dfd8b23 100644 (file)
@@ -1,3 +1,14 @@
+2014-08-27  Beth Dakin  <bdakin@apple.com>
+
+        overflow:scroll elements should not latch to the body if the body is 
+        overflow:hidden
+        https://bugs.webkit.org/show_bug.cgi?id=136273
+
+        Reviewed by Darin Adler.
+
+        New ScrollabeArea virtual function.
+        * WebProcess/Plugins/PDF/PDFPlugin.h:
+
 2014-08-26  Matt Lilek  <mrl@apple.com>
 
         Add WebKit SPI to control the navigator.standalone property
index 901c144..a0bb2cc 100644 (file)
@@ -175,6 +175,7 @@ private:
     // ScrollableArea functions.
     virtual WebCore::IntRect scrollCornerRect() const override;
     virtual WebCore::ScrollableArea* enclosingScrollableArea() const override;
+    virtual bool hasScrollableOrRubberbandableAncestor() override { return true; }
     virtual WebCore::IntRect scrollableAreaBoundingBox() const override;
     virtual void setScrollOffset(const WebCore::IntPoint&) override;
     virtual void invalidateScrollbarRect(WebCore::Scrollbar*, const WebCore::IntRect&) override;