Web Automation: elements larger than the viewport have incorrect in-view center point
[WebKit-https.git] / Source / WebCore / platform / ScrollView.cpp
index c022d12..61bb518 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "GraphicsContext.h"
 #include "GraphicsLayer.h"
 #include "HostWindow.h"
+#include "Logging.h"
 #include "PlatformMouseEvent.h"
 #include "PlatformWheelEvent.h"
 #include "ScrollAnimator.h"
 #include "Scrollbar.h"
 #include "ScrollbarTheme.h"
 #include <wtf/StdLibExtras.h>
+#include <wtf/text/TextStream.h>
 
 namespace WebCore {
 
-ScrollView::ScrollView()
-    : m_horizontalScrollbarMode(ScrollbarAuto)
-    , m_verticalScrollbarMode(ScrollbarAuto)
-    , m_horizontalScrollbarLock(false)
-    , m_verticalScrollbarLock(false)
-    , m_prohibitsScrolling(false)
-    , m_canBlitOnScroll(true)
-    , m_scrollbarsAvoidingResizer(0)
-    , m_scrollbarsSuppressed(false)
-    , m_inUpdateScrollbars(false)
-    , m_updateScrollbarsPass(0)
-    , m_drawPanScrollIcon(false)
-    , m_useFixedLayout(false)
-    , m_paintsEntireContents(false)
-    , m_clipsRepaints(true)
-    , m_delegatesScrolling(false)
-{
-}
+ScrollView::ScrollView() = default;
 
-ScrollView::~ScrollView()
-{
-}
+ScrollView::~ScrollView() = default;
 
-void ScrollView::addChild(PassRefPtr<Widget> prpChild) 
+void ScrollView::addChild(Widget& child)
 {
-    Widget* child = prpChild.get();
-    ASSERT(child != this && !child->parent());
-    child->setParent(this);
-    m_children.add(prpChild);
-    if (child->platformWidget())
-        platformAddChild(child);
+    ASSERT(&child != this);
+    ASSERT(!child.parent());
+    child.setParent(this);
+    m_children.add(child);
+    if (child.platformWidget())
+        platformAddChild(&child);
 }
 
-void ScrollView::removeChild(Widget* child)
+void ScrollView::removeChild(Widget& child)
 {
-    ASSERT(child->parent() == this);
-    child->setParent(0);
-    m_children.remove(child);
-    if (child->platformWidget())
-        platformRemoveChild(child);
+    ASSERT(child.parent() == this);
+    child.setParent(nullptr);
+    m_children.remove(&child);
+    if (child.platformWidget())
+        platformRemoveChild(&child);
 }
 
 bool ScrollView::setHasHorizontalScrollbar(bool hasBar, bool* contentSizeAffected)
 {
-    ASSERT(!hasBar || !avoidScrollbarCreation());
-    if (hasBar && !m_horizontalScrollbar) {
-        m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
-        addChild(m_horizontalScrollbar.get());
-        didAddScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar);
-        m_horizontalScrollbar->styleChanged();
-        if (contentSizeAffected)
-            *contentSizeAffected = !m_horizontalScrollbar->isOverlayScrollbar();
-        return true;
-    }
-    
-    if (!hasBar && m_horizontalScrollbar) {
-        bool wasOverlayScrollbar = m_horizontalScrollbar->isOverlayScrollbar();
-        willRemoveScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar);
-        removeChild(m_horizontalScrollbar.get());
-        m_horizontalScrollbar = 0;
-        if (contentSizeAffected)
-            *contentSizeAffected = !wasOverlayScrollbar;
-        return true;
-    }
-
-    return false;
+    return setHasScrollbarInternal(m_horizontalScrollbar, HorizontalScrollbar, hasBar, contentSizeAffected);
 }
 
 bool ScrollView::setHasVerticalScrollbar(bool hasBar, bool* contentSizeAffected)
 {
+    return setHasScrollbarInternal(m_verticalScrollbar, VerticalScrollbar, hasBar, contentSizeAffected);
+}
+
+bool ScrollView::setHasScrollbarInternal(RefPtr<Scrollbar>& scrollbar, ScrollbarOrientation orientation, bool hasBar, bool* contentSizeAffected)
+{
     ASSERT(!hasBar || !avoidScrollbarCreation());
-    if (hasBar && !m_verticalScrollbar) {
-        m_verticalScrollbar = createScrollbar(VerticalScrollbar);
-        addChild(m_verticalScrollbar.get());
-        didAddScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
-        m_verticalScrollbar->styleChanged();
+
+    if (hasBar && !scrollbar) {
+        scrollbar = createScrollbar(orientation);
+        addChild(*scrollbar);
+        didAddScrollbar(scrollbar.get(), orientation);
+        scrollbar->styleChanged();
         if (contentSizeAffected)
-            *contentSizeAffected = !m_verticalScrollbar->isOverlayScrollbar();
+            *contentSizeAffected = !scrollbar->isOverlayScrollbar();
         return true;
     }
     
-    if (!hasBar && m_verticalScrollbar) {
-        bool wasOverlayScrollbar = m_verticalScrollbar->isOverlayScrollbar();
-        willRemoveScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
-        removeChild(m_verticalScrollbar.get());
-        m_verticalScrollbar = 0;
+    if (!hasBar && scrollbar) {
+        bool wasOverlayScrollbar = scrollbar->isOverlayScrollbar();
+        willRemoveScrollbar(scrollbar.get(), orientation);
+        removeChild(*scrollbar);
+        scrollbar = nullptr;
         if (contentSizeAffected)
             *contentSizeAffected = !wasOverlayScrollbar;
         return true;
@@ -132,10 +100,9 @@ bool ScrollView::setHasVerticalScrollbar(bool hasBar, bool* contentSizeAffected)
     return false;
 }
 
-#if !PLATFORM(GTK)
-PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
+Ref<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
 {
-    return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
+    return Scrollbar::createNativeScrollbar(*this, orientation, RegularScrollbar);
 }
 
 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode,
@@ -165,9 +132,8 @@ void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode v
     if (platformWidget())
         platformSetScrollbarModes();
     else
-        updateScrollbars(scrollOffset());
+        updateScrollbars(scrollPosition());
 }
-#endif
 
 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
 {
@@ -222,11 +188,6 @@ void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
     m_paintsEntireContents = paintsEntireContents;
 }
 
-void ScrollView::setClipsRepaints(bool clipsRepaints)
-{
-    m_clipsRepaints = clipsRepaints;
-}
-
 void ScrollView::setDelegatesScrolling(bool delegatesScrolling)
 {
     if (m_delegatesScrolling == delegatesScrolling)
@@ -238,7 +199,7 @@ void ScrollView::setDelegatesScrolling(bool delegatesScrolling)
 
 IntPoint ScrollView::contentsScrollPosition() const
 {
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
     if (platformWidget())
         return actualScrollPosition();
 #endif
@@ -247,14 +208,14 @@ IntPoint ScrollView::contentsScrollPosition() const
 
 void ScrollView::setContentsScrollPosition(const IntPoint& position)
 {
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
     if (platformWidget())
         setActualScrollPosition(position);
 #endif
     setScrollPosition(position);
 }
 
-#if !PLATFORM(IOS)
+#if !PLATFORM(IOS_FAMILY)
 IntRect ScrollView::unobscuredContentRect(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
 {
     return unobscuredContentRectInternal(scrollbarInclusion);
@@ -263,43 +224,37 @@ IntRect ScrollView::unobscuredContentRect(VisibleContentRectIncludesScrollbars s
 
 IntRect ScrollView::unobscuredContentRectInternal(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
 {
-    FloatSize visibleContentSize = unscaledUnobscuredVisibleContentSize(scrollbarInclusion);
+    FloatSize visibleContentSize = sizeForUnobscuredContent(scrollbarInclusion);
     visibleContentSize.scale(1 / visibleContentScaleFactor());
-    return IntRect(IntPoint(m_scrollOffset), expandedIntSize(visibleContentSize));
+    return IntRect(m_scrollPosition, expandedIntSize(visibleContentSize));
 }
 
-IntSize ScrollView::unscaledTotalVisibleContentSize(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
+IntSize ScrollView::sizeForVisibleContent(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
 {
     if (platformWidget())
-        return platformVisibleContentSize(scrollbarInclusion == IncludeScrollbars);
+        return platformVisibleContentSizeIncludingObscuredArea(scrollbarInclusion == IncludeScrollbars);
 
-#if USE(TILED_BACKING_STORE)
-    if (!m_fixedVisibleContentRect.isEmpty())
+#if USE(COORDINATED_GRAPHICS)
+    if (m_useFixedLayout && !m_fixedVisibleContentRect.isEmpty())
         return m_fixedVisibleContentRect.size();
 #endif
 
-    int verticalScrollbarWidth = 0;
-    int horizontalScrollbarHeight = 0;
+    IntSize scrollbarSpace;
+    if (scrollbarInclusion == ExcludeScrollbars)
+        scrollbarSpace = scrollbarIntrusion();
 
-    if (scrollbarInclusion == ExcludeScrollbars) {
-        if (Scrollbar* verticalBar = verticalScrollbar())
-            verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
-        if (Scrollbar* horizontalBar = horizontalScrollbar())
-            horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
-    }
-
-    return IntSize(width() - verticalScrollbarWidth, height() - horizontalScrollbarHeight).expandedTo(IntSize());
+    return IntSize(width() - scrollbarSpace.width(), height() - scrollbarSpace.height()).expandedTo(IntSize());
 }
     
-IntSize ScrollView::unscaledUnobscuredVisibleContentSize(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
+IntSize ScrollView::sizeForUnobscuredContent(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
 {
-    IntSize visibleContentSize = unscaledTotalVisibleContentSize(scrollbarInclusion);
-    
     if (platformWidget())
-        return visibleContentSize;
+        return platformVisibleContentSize(scrollbarInclusion == IncludeScrollbars);
 
-#if USE(TILED_BACKING_STORE)
-    if (!m_fixedVisibleContentRect.isEmpty())
+    IntSize visibleContentSize = sizeForVisibleContent(scrollbarInclusion);
+
+#if USE(COORDINATED_GRAPHICS)
+    if (m_useFixedLayout && !m_fixedVisibleContentRect.isEmpty())
         return visibleContentSize;
 #endif
 
@@ -307,13 +262,12 @@ IntSize ScrollView::unscaledUnobscuredVisibleContentSize(VisibleContentRectInclu
     return visibleContentSize;
 }
 
-#if !PLATFORM(GTK)
 IntRect ScrollView::visibleContentRectInternal(VisibleContentRectIncludesScrollbars scrollbarInclusion, VisibleContentRectBehavior visibleContentRectBehavior) const
 {
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
     if (visibleContentRectBehavior == LegacyIOSDocumentViewRect) {
         if (platformWidget())
-            return platformVisibleContentRect(true /* include scrollbars */);
+            return platformVisibleContentRect(scrollbarInclusion == IncludeScrollbars);
     }
     
     if (platformWidget())
@@ -325,18 +279,17 @@ IntRect ScrollView::visibleContentRectInternal(VisibleContentRectIncludesScrollb
     if (platformWidget())
         return platformVisibleContentRect(scrollbarInclusion == IncludeScrollbars);
 
-#if USE(TILED_BACKING_STORE)
-    if (!m_fixedVisibleContentRect.isEmpty())
+#if USE(COORDINATED_GRAPHICS)
+    if (m_useFixedLayout && !m_fixedVisibleContentRect.isEmpty())
         return m_fixedVisibleContentRect;
 #endif
 
-    return unobscuredContentRectInternal(scrollbarInclusion);
+    return unobscuredContentRect(scrollbarInclusion);
 }
-#endif
 
 IntSize ScrollView::layoutSize() const
 {
-    return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? unscaledUnobscuredVisibleContentSize(ExcludeScrollbars) : m_fixedLayoutSize;
+    return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? sizeForUnobscuredContent() : m_fixedLayoutSize;
 }
 
 IntSize ScrollView::fixedLayoutSize() const
@@ -348,9 +301,11 @@ void ScrollView::setFixedLayoutSize(const IntSize& newSize)
 {
     if (fixedLayoutSize() == newSize)
         return;
+
+    LOG_WITH_STREAM(Layout, stream << "ScrollView " << this << " setFixedLayoutSize " << newSize);
     m_fixedLayoutSize = newSize;
     if (m_useFixedLayout)
-        fixedLayoutSizeChanged();
+        availableContentSizeChanged(AvailableSizeChangeReason::AreaSizeChanged);
 }
 
 bool ScrollView::useFixedLayout() const
@@ -364,13 +319,18 @@ void ScrollView::setUseFixedLayout(bool enable)
         return;
     m_useFixedLayout = enable;
     if (!m_fixedLayoutSize.isEmpty())
-        fixedLayoutSizeChanged();
+        availableContentSizeChanged(AvailableSizeChangeReason::AreaSizeChanged);
 }
 
-void ScrollView::fixedLayoutSizeChanged()
+void ScrollView::availableContentSizeChanged(AvailableSizeChangeReason reason)
 {
-    updateScrollbars(scrollOffset());
-    contentsResized();
+    ScrollableArea::availableContentSizeChanged(reason);
+
+    if (platformWidget())
+        return;
+
+    if (reason != AvailableSizeChangeReason::ScrollbarsChanged)
+        updateScrollbars(scrollPosition());
 }
 
 IntSize ScrollView::contentsSize() const
@@ -386,42 +346,36 @@ void ScrollView::setContentsSize(const IntSize& newSize)
     if (platformWidget())
         platformSetContentsSize();
     else
-        updateScrollbars(scrollOffset());
+        updateScrollbars(scrollPosition());
     updateOverhangAreas();
 }
 
-IntPoint ScrollView::maximumScrollPosition() const
-{
-    IntPoint maximumOffset(contentsWidth() - visibleWidth() - scrollOrigin().x(), totalContentsSize().height() - visibleHeight() - scrollOrigin().y());
-    maximumOffset.clampNegativeToZero();
-    return maximumOffset;
-}
-
-IntPoint ScrollView::minimumScrollPosition() const
+ScrollPosition ScrollView::maximumScrollPosition() const
 {
-    return IntPoint(-scrollOrigin().x(), -scrollOrigin().y());
+    ScrollPosition maximumPosition = ScrollableArea::maximumScrollPosition();
+    // FIXME: can this be moved into the base class?
+    maximumPosition.clampNegativeToZero();
+    return maximumPosition;
 }
 
-IntPoint ScrollView::adjustScrollPositionWithinRange(const IntPoint& scrollPoint) const
+ScrollPosition ScrollView::adjustScrollPositionWithinRange(const ScrollPosition& scrollPoint) const
 {
-    if (!constrainsScrollingToContentEdge())
+    if (!constrainsScrollingToContentEdge() || m_allowsUnclampedScrollPosition)
         return scrollPoint;
 
-    IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
-    newScrollPosition = newScrollPosition.expandedTo(minimumScrollPosition());
-    return newScrollPosition;
+    return scrollPoint.constrainedBetween(minimumScrollPosition(), maximumScrollPosition());
 }
 
-IntSize ScrollView::scrollOffsetRelativeToDocument() const
+ScrollPosition ScrollView::documentScrollPositionRelativeToViewOrigin() const
 {
-    IntSize scrollOffset = this->scrollOffset();
-    return IntSize(scrollOffset.width(), scrollOffset.height() - headerHeight() - topContentInset());
+    return scrollPosition() - IntSize(
+        shouldPlaceBlockDirectionScrollbarOnLeft() && m_verticalScrollbar ? m_verticalScrollbar->occupiedWidth() : 0,
+        headerHeight() + topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset));
 }
 
-IntPoint ScrollView::scrollPositionRelativeToDocument() const
+ScrollPosition ScrollView::documentScrollPositionRelativeToScrollableAreaOrigin() const
 {
-    IntPoint scrollPosition = this->scrollPosition();
-    return IntPoint(scrollPosition.x(), scrollPosition.y() - headerHeight() - topContentInset());
+    return scrollPosition() - IntSize(0, headerHeight());
 }
 
 int ScrollView::scrollSize(ScrollbarOrientation orientation) const
@@ -441,78 +395,119 @@ void ScrollView::notifyPageThatContentAreaWillPaint() const
 {
 }
 
-void ScrollView::setScrollOffset(const IntPoint& offset)
+void ScrollView::setScrollOffset(const ScrollOffset& offset)
+{
+    LOG_WITH_STREAM(Scrolling, stream << "\nScrollView::setScrollOffset " << offset << " constrains " << constrainsScrollingToContentEdge());
+
+    IntPoint constrainedOffset = offset;
+    if (constrainsScrollingToContentEdge())
+        constrainedOffset = constrainedOffset.constrainedBetween(IntPoint(), maximumScrollOffset());
+
+    scrollTo(scrollPositionFromOffset(constrainedOffset));
+}
+
+void ScrollView::scrollOffsetChangedViaPlatformWidget(const ScrollOffset& oldOffset, const ScrollOffset& newOffset)
 {
-    int horizontalOffset = offset.x();
-    int verticalOffset = offset.y();
-    if (constrainsScrollingToContentEdge()) {
-        horizontalOffset = std::max(std::min(horizontalOffset, contentsWidth() - visibleWidth()), 0);
-        verticalOffset = std::max(std::min(verticalOffset, totalContentsSize().height() - visibleHeight()), 0);
+    // We should not attempt to actually modify (paint) platform widgets if the layout phase
+    // is not complete. Instead, defer the scroll event until the layout finishes.
+    if (shouldDeferScrollUpdateAfterContentSizeChange()) {
+        // We only care about the most recent scroll position change request
+        m_deferredScrollOffsets = std::make_pair(oldOffset, newOffset);
+        return;
     }
 
-    IntSize newOffset = m_scrollOffset;
-    newOffset.setWidth(horizontalOffset - scrollOrigin().x());
-    newOffset.setHeight(verticalOffset - scrollOrigin().y());
+    scrollOffsetChangedViaPlatformWidgetImpl(oldOffset, newOffset);
+}
+
+void ScrollView::handleDeferredScrollUpdateAfterContentSizeChange()
+{
+    ASSERT(!shouldDeferScrollUpdateAfterContentSizeChange());
+
+    if (!m_deferredScrollDelta && !m_deferredScrollOffsets)
+        return;
+
+    ASSERT(static_cast<bool>(m_deferredScrollDelta) != static_cast<bool>(m_deferredScrollOffsets));
 
-    scrollTo(newOffset);
+    if (m_deferredScrollDelta)
+        completeUpdatesAfterScrollTo(m_deferredScrollDelta.value());
+    else if (m_deferredScrollOffsets)
+        scrollOffsetChangedViaPlatformWidgetImpl(m_deferredScrollOffsets.value().first, m_deferredScrollOffsets.value().second);
+    
+    m_deferredScrollDelta = WTF::nullopt;
+    m_deferredScrollOffsets = WTF::nullopt;
 }
 
-void ScrollView::scrollTo(const IntSize& newOffset)
+void ScrollView::scrollTo(const ScrollPosition& newPosition)
 {
-    IntSize scrollDelta = newOffset - m_scrollOffset;
-    if (scrollDelta == IntSize())
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollView::scrollTo " << newPosition << " min: " << minimumScrollPosition() << " max: " << maximumScrollPosition());
+
+    IntSize scrollDelta = newPosition - m_scrollPosition;
+    if (scrollDelta.isZero())
         return;
-    m_scrollOffset = newOffset;
+
+    m_scrollPosition = newPosition;
 
     if (scrollbarsSuppressed())
         return;
 
-#if USE(TILED_BACKING_STORE)
+#if USE(COORDINATED_GRAPHICS)
     if (delegatesScrolling()) {
-        hostWindow()->delegatedScrollRequested(IntPoint(newOffset));
+        requestScrollPositionUpdate(newPosition);
         return;
     }
 #endif
+    // We should not attempt to actually modify layer contents if the layout phase
+    // is not complete. Instead, defer the scroll event until the layout finishes.
+    if (shouldDeferScrollUpdateAfterContentSizeChange()) {
+        ASSERT(!m_deferredScrollDelta);
+        m_deferredScrollDelta = scrollDelta;
+        return;
+    }
+
+    completeUpdatesAfterScrollTo(scrollDelta);
+}
+
+void ScrollView::completeUpdatesAfterScrollTo(const IntSize& scrollDelta)
+{
     updateLayerPositionsAfterScrolling();
     scrollContents(scrollDelta);
     updateCompositingLayersAfterScrolling();
 }
 
-int ScrollView::scrollPosition(Scrollbar* scrollbar) const
+int ScrollView::scrollOffset(ScrollbarOrientation orientation) const
 {
-    if (scrollbar->orientation() == HorizontalScrollbar)
-        return scrollPosition().x() + scrollOrigin().x();
-    if (scrollbar->orientation() == VerticalScrollbar)
-        return scrollPosition().y() + scrollOrigin().y();
+    ScrollOffset offset = scrollOffsetFromPosition(scrollPosition());
+
+    if (orientation == HorizontalScrollbar)
+        return offset.x();
+
+    if (orientation == VerticalScrollbar)
+        return offset.y();
+
     return 0;
 }
 
-void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
+void ScrollView::setScrollPosition(const ScrollPosition& scrollPosition)
 {
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollView::setScrollPosition " << scrollPosition);
+
     if (prohibitsScrolling())
         return;
 
     if (platformWidget()) {
-        platformSetScrollPosition(scrollPoint);
+        platformSetScrollPosition(scrollPosition);
         return;
     }
 
-#if USE(TILED_BACKING_STORE)
-    if (delegatesScrolling()) {
-        hostWindow()->delegatedScrollRequested(scrollPoint);
-        return;
-    }
-#endif
+    ScrollPosition newScrollPosition = !delegatesScrolling() ? adjustScrollPositionWithinRange(scrollPosition) : scrollPosition;
 
-    IntPoint newScrollPosition = adjustScrollPositionWithinRange(scrollPoint);
-
-    if (newScrollPosition == scrollPosition())
+    if ((!delegatesScrolling() || currentScrollType() == ScrollType::User) && newScrollPosition == this->scrollPosition())
         return;
 
     if (requestScrollPositionUpdate(newScrollPosition))
         return;
 
-    updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
+    updateScrollbars(newScrollPosition);
 }
 
 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
@@ -532,42 +527,57 @@ IntSize ScrollView::overhangAmount() const
 {
     IntSize stretch;
 
-    int physicalScrollY = scrollPosition().y() + scrollOrigin().y();
-    if (physicalScrollY < 0)
-        stretch.setHeight(physicalScrollY);
-    else if (totalContentsSize().height() && physicalScrollY > totalContentsSize().height() - visibleHeight())
-        stretch.setHeight(physicalScrollY - (totalContentsSize().height() - visibleHeight()));
+    // FIXME: use maximumScrollOffset()
+    ScrollOffset scrollOffset = scrollOffsetFromPosition(scrollPosition());
+    if (scrollOffset.y() < 0)
+        stretch.setHeight(scrollOffset.y());
+    else if (totalContentsSize().height() && scrollOffset.y() > totalContentsSize().height() - visibleHeight())
+        stretch.setHeight(scrollOffset.y() - (totalContentsSize().height() - visibleHeight()));
 
-    int physicalScrollX = scrollPosition().x() + scrollOrigin().x();
-    if (physicalScrollX < 0)
-        stretch.setWidth(physicalScrollX);
-    else if (contentsWidth() && physicalScrollX > contentsWidth() - visibleWidth())
-        stretch.setWidth(physicalScrollX - (contentsWidth() - visibleWidth()));
+    if (scrollOffset.x() < 0)
+        stretch.setWidth(scrollOffset.x());
+    else if (contentsWidth() && scrollOffset.x() > contentsWidth() - visibleWidth())
+        stretch.setWidth(scrollOffset.x() - (contentsWidth() - visibleWidth()));
 
     return stretch;
 }
 
-void ScrollView::windowResizerRectChanged()
+bool ScrollView::managesScrollbars() const
 {
+#if PLATFORM(IOS_FAMILY)
+    return false;
+#else
     if (platformWidget())
-        return;
-
-    updateScrollbars(scrollOffset());
+        return false;
+    if (delegatesScrolling())
+        return false;
+    return true;
+#endif
 }
 
-void ScrollView::updateScrollbars(const IntSize& desiredOffset)
+void ScrollView::updateScrollbars(const ScrollPosition& desiredPosition)
 {
-    if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget() || delegatesScrolling())
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollView::updateScrollbars " << desiredPosition);
+
+    if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget())
         return;
+    
+    if (!managesScrollbars()) {
+        if (scrollOriginChanged()) {
+            ScrollableArea::scrollToOffsetWithoutAnimation(scrollOffsetFromPosition(desiredPosition));
+            resetScrollOriginChanged();
+        }
+        return;
+    }
 
     bool hasOverlayScrollbars = (!m_horizontalScrollbar || m_horizontalScrollbar->isOverlayScrollbar()) && (!m_verticalScrollbar || m_verticalScrollbar->isOverlayScrollbar());
 
-    // If we came in here with the view already needing a layout, then go ahead and do that
-    // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
+    // If we came in here with the view already needing a layout then do that first.
+    // (This will be the common case, e.g., when the page changes due to window resizing for example).
     // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
     if (!m_scrollbarsSuppressed && !hasOverlayScrollbars) {
         m_inUpdateScrollbars = true;
-        visibleContentsResized();
+        updateContentsSize();
         m_inUpdateScrollbars = false;
     }
 
@@ -612,7 +622,7 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset)
 
         bool needAnotherPass = false;
         if (!hasOverlayScrollbars) {
-            // If we ever turn one scrollbar off, always turn the other one off too.  Never ever
+            // If we ever turn one scrollbar off, do not turn the other one on. Never ever
             // try to both gain/lose a scrollbar in the same pass.
             if (!m_updateScrollbarsPass && docSize.width() <= fullVisibleSize.width() && docSize.height() <= fullVisibleSize.height()) {
                 if (hScroll == ScrollbarAuto)
@@ -620,11 +630,11 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset)
                 if (vScroll == ScrollbarAuto)
                     newHasVerticalScrollbar = false;
             }
-            if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn) {
+            if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn && !hasVerticalScrollbar) {
                 newHasVerticalScrollbar = false;
                 needAnotherPass = true;
             }
-            if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn) {
+            if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn && !hasHorizontalScrollbar) {
                 newHasHorizontalScrollbar = false;
                 needAnotherPass = true;
             }
@@ -632,7 +642,7 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset)
 
         if (hasHorizontalScrollbar != newHasHorizontalScrollbar && (hasHorizontalScrollbar || !avoidScrollbarCreation())) {
             if (scrollOrigin().y() && !newHasHorizontalScrollbar)
-                ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x(), scrollOrigin().y() - m_horizontalScrollbar->height()));
+                ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x(), scrollOrigin().y() - m_horizontalScrollbar->occupiedHeight()));
             if (m_horizontalScrollbar)
                 m_horizontalScrollbar->invalidate();
 
@@ -645,7 +655,7 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset)
 
         if (hasVerticalScrollbar != newHasVerticalScrollbar && (hasVerticalScrollbar || !avoidScrollbarCreation())) {
             if (scrollOrigin().x() && !newHasVerticalScrollbar)
-                ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x() - m_verticalScrollbar->width(), scrollOrigin().y()));
+                ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x() - m_verticalScrollbar->occupiedWidth(), scrollOrigin().y()));
             if (m_verticalScrollbar)
                 m_verticalScrollbar->invalidate();
 
@@ -659,14 +669,14 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset)
         const unsigned cMaxUpdateScrollbarsPass = 2;
         if ((sendContentResizedNotification || needAnotherPass) && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
             m_updateScrollbarsPass++;
-            contentsResized();
-            visibleContentsResized();
+            availableContentSizeChanged(AvailableSizeChangeReason::ScrollbarsChanged);
+            updateContentsSize();
             IntSize newDocSize = totalContentsSize();
             if (newDocSize == docSize) {
                 // The layout with the new scroll state had no impact on
                 // the document's overall size, so updateScrollbars didn't get called.
                 // Recur manually.
-                updateScrollbars(desiredOffset);
+                updateScrollbars(desiredPosition);
             }
             m_updateScrollbarsPass--;
         }
@@ -684,11 +694,11 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset)
 
     if (m_horizontalScrollbar) {
         int clientWidth = visibleWidth();
-        int pageStep = std::max(std::max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1);
+        int pageStep = Scrollbar::pageStep(clientWidth);
         IntRect oldRect(m_horizontalScrollbar->frameRect());
-        IntRect hBarRect(0,
+        IntRect hBarRect(shouldPlaceBlockDirectionScrollbarOnLeft() && m_verticalScrollbar ? m_verticalScrollbar->occupiedWidth() : 0,
             height() - m_horizontalScrollbar->height(),
-            width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
+            width() - (m_verticalScrollbar ? m_verticalScrollbar->occupiedWidth() : 0),
             m_horizontalScrollbar->height());
         m_horizontalScrollbar->setFrameRect(hBarRect);
         if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
@@ -705,12 +715,12 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset)
 
     if (m_verticalScrollbar) {
         int clientHeight = visibleHeight();
-        int pageStep = std::max(std::max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1);
+        int pageStep = Scrollbar::pageStep(clientHeight);
         IntRect oldRect(m_verticalScrollbar->frameRect());
-        IntRect vBarRect(width() - m_verticalScrollbar->width(), 
+        IntRect vBarRect(shouldPlaceBlockDirectionScrollbarOnLeft() ? 0 : width() - m_verticalScrollbar->width(),
             topContentInset(),
             m_verticalScrollbar->width(),
-            height() - topContentInset() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
+            height() - topContentInset() - (m_horizontalScrollbar ? m_horizontalScrollbar->occupiedHeight() : 0));
         m_verticalScrollbar->setFrameRect(vBarRect);
         if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
             m_verticalScrollbar->invalidate();
@@ -733,12 +743,12 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset)
             invalidateScrollCornerRect(oldScrollCornerRect);
     }
 
-    IntPoint adjustedScrollPosition = IntPoint(desiredOffset);
+    IntPoint adjustedScrollPosition = desiredPosition;
     if (!isRubberBandInProgress())
         adjustedScrollPosition = adjustScrollPositionWithinRange(adjustedScrollPosition);
 
     if (adjustedScrollPosition != scrollPosition() || scrollOriginChanged()) {
-        ScrollableArea::scrollToOffsetWithoutAnimation(adjustedScrollPosition + toIntSize(scrollOrigin()));
+        ScrollableArea::scrollToOffsetWithoutAnimation(scrollOffsetFromPosition(adjustedScrollPosition));
         resetScrollOriginChanged();
     }
 
@@ -797,9 +807,8 @@ void ScrollView::scrollContents(const IntSize& scrollDelta)
         if (!scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
             scrollContentsSlowPath(updateRect);
     } else { 
-       // We need to go ahead and repaint the entire backing store.  Do it now before moving the
-       // windowed plugins.
-       scrollContentsSlowPath(updateRect);
+        // We need to repaint the entire backing store. Do it now before moving the windowed plugins.
+        scrollContentsSlowPath(updateRect);
     }
 
     // Invalidate the overhang areas if they are visible.
@@ -812,100 +821,162 @@ void ScrollView::scrollContents(const IntSize& scrollDelta)
     window->invalidateRootView(IntRect());
 }
 
-bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
-{
-    hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
-    return true;
-}
-
 void ScrollView::scrollContentsSlowPath(const IntRect& updateRect)
 {
     hostWindow()->invalidateContentsForSlowScroll(updateRect);
 }
 
-IntPoint ScrollView::rootViewToContents(const IntPoint& rootViewPoint) const
+IntPoint ScrollView::viewToContents(const IntPoint& point) const
 {
     if (delegatesScrolling())
-        return convertFromRootView(rootViewPoint);
+        return point;
 
-    IntPoint viewPoint = convertFromRootView(rootViewPoint);
-    return viewPoint + scrollOffsetRelativeToDocument();
+    return point + toIntSize(documentScrollPositionRelativeToViewOrigin());
 }
 
-IntPoint ScrollView::contentsToRootView(const IntPoint& contentsPoint) const
+IntPoint ScrollView::contentsToView(const IntPoint& point) const
 {
     if (delegatesScrolling())
-        return convertToRootView(contentsPoint);
+        return point;
 
-    IntPoint viewPoint = contentsPoint + IntSize(0, headerHeight() + topContentInset()) - scrollOffset();
-    return convertToRootView(viewPoint);  
+    return point - toIntSize(documentScrollPositionRelativeToViewOrigin());
 }
 
-IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const
+FloatPoint ScrollView::viewToContents(const FloatPoint& point) const
 {
     if (delegatesScrolling())
-        return convertFromRootView(rootViewRect);
+        return point;
 
-    IntRect viewRect = convertFromRootView(rootViewRect);
-    viewRect.move(scrollOffsetRelativeToDocument());
-    return viewRect;
+    return viewToContents(IntPoint(point));
 }
 
-IntRect ScrollView::contentsToRootView(const IntRect& contentsRect) const
+FloatPoint ScrollView::contentsToView(const FloatPoint& point) const
 {
     if (delegatesScrolling())
-        return convertToRootView(contentsRect);
+        return point;
 
-    IntRect viewRect = contentsRect;
-    viewRect.move(-scrollOffset() + IntSize(0, headerHeight() + topContentInset()));
-    return convertToRootView(viewRect);
+    return contentsToView(IntPoint(point));
 }
 
-IntPoint ScrollView::rootViewToTotalContents(const IntPoint& rootViewPoint) const
+IntRect ScrollView::viewToContents(IntRect rect) const
 {
     if (delegatesScrolling())
-        return convertFromRootView(rootViewPoint);
+        return rect;
 
-    IntPoint viewPoint = convertFromRootView(rootViewPoint);
-    return viewPoint + scrollOffset();
+    rect.moveBy(documentScrollPositionRelativeToViewOrigin());
+    return rect;
 }
 
-IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
+FloatRect ScrollView::viewToContents(FloatRect rect) const
 {
     if (delegatesScrolling())
-        return convertFromContainingWindow(windowPoint);
+        return rect;
 
-    IntPoint viewPoint = convertFromContainingWindow(windowPoint);
-    return viewPoint + scrollOffsetRelativeToDocument();
+    rect.moveBy(documentScrollPositionRelativeToViewOrigin());
+    return rect;
 }
 
-IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
+IntRect ScrollView::contentsToView(IntRect rect) const
 {
     if (delegatesScrolling())
-        return convertToContainingWindow(contentsPoint);
+        return rect;
 
-    IntPoint viewPoint = contentsPoint + IntSize(0, headerHeight() + topContentInset()) - scrollOffset();
-    return convertToContainingWindow(viewPoint);  
+    rect.moveBy(-documentScrollPositionRelativeToViewOrigin());
+    return rect;
 }
 
-IntRect ScrollView::windowToContents(const IntRect& windowRect) const
+FloatRect ScrollView::contentsToView(FloatRect rect) const
 {
     if (delegatesScrolling())
-        return convertFromContainingWindow(windowRect);
+        return rect;
 
-    IntRect viewRect = convertFromContainingWindow(windowRect);
-    viewRect.move(scrollOffsetRelativeToDocument());
-    return viewRect;
+    rect.moveBy(-documentScrollPositionRelativeToViewOrigin());
+    return rect;
 }
 
-IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
+IntPoint ScrollView::contentsToContainingViewContents(const IntPoint& point) const
+{
+    if (const ScrollView* parentScrollView = parent()) {
+        IntPoint pointInContainingView = convertToContainingView(contentsToView(point));
+        return parentScrollView->viewToContents(pointInContainingView);
+    }
+
+    return contentsToView(point);
+}
+
+IntRect ScrollView::contentsToContainingViewContents(IntRect rect) const
+{
+    if (const ScrollView* parentScrollView = parent()) {
+        IntRect rectInContainingView = convertToContainingView(contentsToView(rect));
+        return parentScrollView->viewToContents(rectInContainingView);
+    }
+
+    return contentsToView(rect);
+}
+
+IntPoint ScrollView::rootViewToContents(const IntPoint& rootViewPoint) const
+{
+    return viewToContents(convertFromRootView(rootViewPoint));
+}
+
+IntPoint ScrollView::contentsToRootView(const IntPoint& contentsPoint) const
+{
+    return convertToRootView(contentsToView(contentsPoint));
+}
+
+FloatPoint ScrollView::contentsToRootView(const FloatPoint& contentsPoint) const
+{
+    return convertToRootView(contentsToView(contentsPoint));
+}
+
+IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const
+{
+    return viewToContents(convertFromRootView(rootViewRect));
+}
+
+FloatRect ScrollView::rootViewToContents(const FloatRect& rootViewRect) const
+{
+    return viewToContents(convertFromRootView(rootViewRect));
+}
+
+FloatRect ScrollView::contentsToRootView(const FloatRect& contentsRect) const
+{
+    return convertToRootView(contentsToView(contentsRect));
+}
+
+IntPoint ScrollView::rootViewToTotalContents(const IntPoint& rootViewPoint) const
 {
     if (delegatesScrolling())
-        return convertToContainingWindow(contentsRect);
+        return convertFromRootView(rootViewPoint);
+
+    IntPoint viewPoint = convertFromRootView(rootViewPoint);
+    // Like rootViewToContents(), but ignores headerHeight.
+    return viewPoint + toIntSize(scrollPosition()) - IntSize(0, topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset));
+}
+
+IntRect ScrollView::contentsToRootView(const IntRect& contentsRect) const
+{
+    return convertToRootView(contentsToView(contentsRect));
+}
 
-    IntRect viewRect = contentsRect;
-    viewRect.move(-scrollOffset() + IntSize(0, headerHeight() + topContentInset()));
-    return convertToContainingWindow(viewRect);
+IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
+{
+    return viewToContents(convertFromContainingWindow(windowPoint));
+}
+
+IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
+{
+    return convertToContainingWindow(contentsToView(contentsPoint));
+}
+
+IntRect ScrollView::windowToContents(const IntRect& windowRect) const
+{
+    return viewToContents(convertFromContainingWindow(windowRect));
+}
+
+IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
+{
+    return convertToContainingWindow(contentsToView(contentsRect));
 }
 
 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
@@ -928,41 +999,6 @@ IntPoint ScrollView::screenToContents(const IntPoint& point) const
     return rootViewToContents(window->screenToRootView(point));
 }
 
-bool ScrollView::containsScrollbarsAvoidingResizer() const
-{
-    return !m_scrollbarsAvoidingResizer;
-}
-
-void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
-{
-    int oldCount = m_scrollbarsAvoidingResizer;
-    m_scrollbarsAvoidingResizer += overlapDelta;
-    if (parent())
-        parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta);
-    else if (!scrollbarsSuppressed()) {
-        // If we went from n to 0 or from 0 to n and we're the outermost view,
-        // we need to invalidate the windowResizerRect(), since it will now need to paint
-        // differently.
-        if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
-            (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
-            invalidateRect(windowResizerRect());
-    }
-}
-
-void ScrollView::setParent(ScrollView* parentView)
-{
-    if (parentView == parent())
-        return;
-
-    if (m_scrollbarsAvoidingResizer && parent())
-        parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
-
-    Widget::setParent(parentView);
-
-    if (m_scrollbarsAvoidingResizer && parent())
-        parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
-}
-
 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
 {
     if (suppressed == m_scrollbarsSuppressed)
@@ -988,10 +1024,12 @@ Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
     if (platformWidget())
         return 0;
 
-    IntPoint viewPoint = convertFromContainingWindow(windowPoint);
-    if (m_horizontalScrollbar && m_horizontalScrollbar->shouldParticipateInHitTesting() && m_horizontalScrollbar->frameRect().contains(viewPoint))
+    // convertFromContainingWindow doesn't do what it sounds like it does. We need it here just to get this
+    // point into the right coordinates if this is the ScrollView of a sub-frame.
+    IntPoint convertedPoint = convertFromContainingWindow(windowPoint);
+    if (m_horizontalScrollbar && m_horizontalScrollbar->shouldParticipateInHitTesting() && m_horizontalScrollbar->frameRect().contains(convertedPoint))
         return m_horizontalScrollbar.get();
-    if (m_verticalScrollbar && m_verticalScrollbar->shouldParticipateInHitTesting() && m_verticalScrollbar->frameRect().contains(viewPoint))
+    if (m_verticalScrollbar && m_verticalScrollbar->shouldParticipateInHitTesting() && m_verticalScrollbar->frameRect().contains(convertedPoint))
         return m_verticalScrollbar.get();
     return 0;
 }
@@ -1004,36 +1042,33 @@ void ScrollView::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
 
 void ScrollView::setFrameRect(const IntRect& newRect)
 {
+    Ref<ScrollView> protectedThis(*this);
     IntRect oldRect = frameRect();
     
     if (newRect == oldRect)
         return;
 
     Widget::setFrameRect(newRect);
-
     frameRectsChanged();
 
-    updateScrollbars(scrollOffset());
-
+    updateScrollbars(scrollPosition());
+    
     if (!m_useFixedLayout && oldRect.size() != newRect.size())
-        contentsResized();
+        availableContentSizeChanged(AvailableSizeChangeReason::AreaSizeChanged);
 }
 
 void ScrollView::frameRectsChanged()
 {
     if (platformWidget())
         return;
-
-    HashSet<RefPtr<Widget>>::const_iterator end = m_children.end();
-    for (HashSet<RefPtr<Widget>>::const_iterator current = m_children.begin(); current != end; ++current)
-        (*current)->frameRectsChanged();
+    for (auto& child : m_children)
+        child->frameRectsChanged();
 }
 
 void ScrollView::clipRectChanged()
 {
-    HashSet<RefPtr<Widget>>::const_iterator end = m_children.end();
-    for (HashSet<RefPtr<Widget>>::const_iterator current = m_children.begin(); current != end; ++current)
-        (*current)->clipRectChanged();
+    for (auto& child : m_children)
+        child->clipRectChanged();
 }
 
 static void positionScrollbarLayer(GraphicsLayer* graphicsLayer, Scrollbar* scrollbar)
@@ -1079,7 +1114,7 @@ void ScrollView::positionScrollbarLayers()
 void ScrollView::repaintContentRectangle(const IntRect& rect)
 {
     IntRect paintRect = rect;
-    if (clipsRepaints() && !paintsEntireContents())
+    if (!paintsEntireContents())
         paintRect.intersect(visibleContentRect(LegacyIOSDocumentVisibleRect));
     if (paintRect.isEmpty())
         return;
@@ -1104,14 +1139,14 @@ IntRect ScrollView::scrollCornerRect() const
     int heightTrackedByScrollbar = height() - topContentInset();
 
     if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
-        cornerRect.unite(IntRect(m_horizontalScrollbar->width(),
+        cornerRect.unite(IntRect(shouldPlaceBlockDirectionScrollbarOnLeft() ? 0 : m_horizontalScrollbar->width(),
             height() - m_horizontalScrollbar->height(),
             width() - m_horizontalScrollbar->width(),
             m_horizontalScrollbar->height()));
     }
 
     if (m_verticalScrollbar && heightTrackedByScrollbar - m_verticalScrollbar->height() > 0) {
-        cornerRect.unite(IntRect(width() - m_verticalScrollbar->width(),
+        cornerRect.unite(IntRect(shouldPlaceBlockDirectionScrollbarOnLeft() ? 0 : width() - m_verticalScrollbar->width(),
             m_verticalScrollbar->height() + topContentInset(),
             m_verticalScrollbar->width(),
             heightTrackedByScrollbar - m_verticalScrollbar->height()));
@@ -1125,28 +1160,24 @@ bool ScrollView::isScrollCornerVisible() const
     return !scrollCornerRect().isEmpty();
 }
 
-void ScrollView::scrollbarStyleChanged(int, bool forceUpdate)
+void ScrollView::scrollbarStyleChanged(ScrollbarStyle newStyle, bool forceUpdate)
 {
+    ScrollableArea::scrollbarStyleChanged(newStyle, forceUpdate);
     if (!forceUpdate)
         return;
 
-    contentsResized();
-    updateScrollbars(scrollOffset());
+    updateScrollbars(scrollPosition());
     positionScrollbarLayers();
 }
 
-void ScrollView::updateScrollCorner()
-{
-}
-
-void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
+void ScrollView::paintScrollCorner(GraphicsContext& context, const IntRect& cornerRect)
 {
-    ScrollbarTheme::theme()->paintScrollCorner(this, context, cornerRect);
+    ScrollbarTheme::theme().paintScrollCorner(context, cornerRect);
 }
 
-void ScrollView::paintScrollbar(GraphicsContext* context, Scrollbar* bar, const IntRect& rect)
+void ScrollView::paintScrollbar(GraphicsContext& context, Scrollbar& bar, const IntRect& rect)
 {
-    bar->paint(context, rect);
+    bar.paint(context, rect);
 }
 
 void ScrollView::invalidateScrollCornerRect(const IntRect& rect)
@@ -1154,12 +1185,12 @@ void ScrollView::invalidateScrollCornerRect(const IntRect& rect)
     invalidateRect(rect);
 }
 
-void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
+void ScrollView::paintScrollbars(GraphicsContext& context, const IntRect& rect)
 {
     if (m_horizontalScrollbar && !layerForHorizontalScrollbar())
-        paintScrollbar(context, m_horizontalScrollbar.get(), rect);
+        paintScrollbar(context, *m_horizontalScrollbar.get(), rect);
     if (m_verticalScrollbar && !layerForVerticalScrollbar())
-        paintScrollbar(context, m_verticalScrollbar.get(), rect);
+        paintScrollbar(context, *m_verticalScrollbar.get(), rect);
 
     if (layerForScrollCorner())
         return;
@@ -1167,47 +1198,48 @@ void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
     paintScrollCorner(context, scrollCornerRect());
 }
 
-void ScrollView::paintPanScrollIcon(GraphicsContext* context)
+void ScrollView::paintPanScrollIcon(GraphicsContext& context)
 {
-    static Image* panScrollIcon = Image::loadPlatformResource("panIcon").leakRef();
+    static Image& panScrollIcon = Image::loadPlatformResource("panIcon").leakRef();
     IntPoint iconGCPoint = m_panScrollIconPoint;
     if (parent())
         iconGCPoint = parent()->windowToContents(iconGCPoint);
-    context->drawImage(panScrollIcon, ColorSpaceDeviceRGB, iconGCPoint);
+    context.drawImage(panScrollIcon, iconGCPoint);
 }
 
-void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
+void ScrollView::paint(GraphicsContext& context, const IntRect& rect, SecurityOriginPaintPolicy securityOriginPaintPolicy)
 {
     if (platformWidget()) {
         Widget::paint(context, rect);
         return;
     }
 
-    if (context->paintingDisabled() && !context->updatingControlTints())
+    if (context.paintingDisabled() && !context.performingPaintInvalidation())
         return;
 
     notifyPageThatContentAreaWillPaint();
 
     IntRect documentDirtyRect = rect;
     if (!paintsEntireContents()) {
-        IntRect visibleAreaWithoutScrollbars(location(), visibleContentRect(LegacyIOSDocumentVisibleRect).size());
+        IntRect visibleAreaWithoutScrollbars(locationOfContents(), visibleContentRect(LegacyIOSDocumentVisibleRect).size());
         documentDirtyRect.intersect(visibleAreaWithoutScrollbars);
     }
 
     if (!documentDirtyRect.isEmpty()) {
-        GraphicsContextStateSaver stateSaver(*context);
+        GraphicsContextStateSaver stateSaver(context);
 
-        context->translate(x(), y());
-        documentDirtyRect.moveBy(-location());
+        IntPoint locationOfContents = this->locationOfContents();
+        context.translate(locationOfContents.x(), locationOfContents.y());
+        documentDirtyRect.moveBy(-locationOfContents);
 
         if (!paintsEntireContents()) {
-            context->translate(-scrollX(), -scrollY());
+            context.translate(-scrollX(), -scrollY());
             documentDirtyRect.moveBy(scrollPosition());
 
-            context->clip(visibleContentRect(LegacyIOSDocumentVisibleRect));
+            context.clip(visibleContentRect(LegacyIOSDocumentVisibleRect));
         }
 
-        paintContents(context, documentDirtyRect);
+        paintContents(context, documentDirtyRect, securityOriginPaintPolicy);
     }
 
 #if ENABLE(RUBBER_BANDING)
@@ -1219,12 +1251,13 @@ void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
 
     // Now paint the scrollbars.
     if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
-        GraphicsContextStateSaver stateSaver(*context);
+        GraphicsContextStateSaver stateSaver(context);
         IntRect scrollViewDirtyRect = rect;
         IntRect visibleAreaWithScrollbars(location(), unobscuredContentRectIncludingScrollbars().size());
         scrollViewDirtyRect.intersect(visibleAreaWithScrollbars);
-        context->translate(x(), y());
+        context.translate(x(), y());
         scrollViewDirtyRect.moveBy(-location());
+        context.clip(IntRect(IntPoint(), visibleAreaWithScrollbars.size()));
 
         paintScrollbars(context, scrollViewDirtyRect);
     }
@@ -1236,38 +1269,35 @@ void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
 
 void ScrollView::calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect)
 {
-    int verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar())
-        ? verticalScrollbar()->width() : 0;
-    int horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar())
-        ? horizontalScrollbar()->height() : 0;
+    IntSize scrollbarSpace = scrollbarIntrusion();
 
-    int physicalScrollY = scrollPosition().y() + scrollOrigin().y();
-    if (physicalScrollY < 0) {
+    // FIXME: use maximumScrollOffset().
+    ScrollOffset scrollOffset = scrollOffsetFromPosition(scrollPosition());
+    if (scrollOffset.y() < 0) {
         horizontalOverhangRect = frameRect();
-        horizontalOverhangRect.setHeight(-physicalScrollY);
-        horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
-    } else if (totalContentsSize().height() && physicalScrollY > totalContentsSize().height() - visibleHeight()) {
-        int height = physicalScrollY - (totalContentsSize().height() - visibleHeight());
+        horizontalOverhangRect.setHeight(-scrollOffset.y());
+        horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - scrollbarSpace.width());
+    } else if (totalContentsSize().height() && scrollOffset.y() > totalContentsSize().height() - visibleHeight()) {
+        int height = scrollOffset.y() - (totalContentsSize().height() - visibleHeight());
         horizontalOverhangRect = frameRect();
-        horizontalOverhangRect.setY(frameRect().maxY() - height - horizontalScrollbarHeight);
+        horizontalOverhangRect.setY(frameRect().maxY() - height - scrollbarSpace.height());
         horizontalOverhangRect.setHeight(height);
-        horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
+        horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - scrollbarSpace.width());
     }
 
-    int physicalScrollX = scrollPosition().x() + scrollOrigin().x();
-    if (physicalScrollX < 0) {
-        verticalOverhangRect.setWidth(-physicalScrollX);
-        verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
+    if (scrollOffset.x() < 0) {
+        verticalOverhangRect.setWidth(-scrollOffset.x());
+        verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - scrollbarSpace.height());
         verticalOverhangRect.setX(frameRect().x());
         if (horizontalOverhangRect.y() == frameRect().y())
             verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
         else
             verticalOverhangRect.setY(frameRect().y());
-    } else if (contentsWidth() && physicalScrollX > contentsWidth() - visibleWidth()) {
-        int width = physicalScrollX - (contentsWidth() - visibleWidth());
+    } else if (contentsWidth() && scrollOffset.x() > contentsWidth() - visibleWidth()) {
+        int width = scrollOffset.x() - (contentsWidth() - visibleWidth());
         verticalOverhangRect.setWidth(width);
-        verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
-        verticalOverhangRect.setX(frameRect().maxX() - width - verticalScrollbarWidth);
+        verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - scrollbarSpace.height());
+        verticalOverhangRect.setX(frameRect().maxX() - width - scrollbarSpace.width());
         if (horizontalOverhangRect.y() == frameRect().y())
             verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
         else
@@ -1290,12 +1320,12 @@ void ScrollView::updateOverhangAreas()
         window->invalidateContentsAndRootView(verticalOverhangRect);
 }
 
-void ScrollView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
+void ScrollView::paintOverhangAreas(GraphicsContext& context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
 {
-    ScrollbarTheme::theme()->paintOverhangAreas(this, context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
+    ScrollbarTheme::theme().paintOverhangAreas(*this, context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
 }
 
-void ScrollView::calculateAndPaintOverhangAreas(GraphicsContext* context, const IntRect& dirtyRect)
+void ScrollView::calculateAndPaintOverhangAreas(GraphicsContext& context, const IntRect& dirtyRect)
 {
     IntRect horizontalOverhangRect;
     IntRect verticalOverhangRect;
@@ -1333,36 +1363,36 @@ bool ScrollView::scrollbarCornerPresent() const
         || (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
 }
 
-IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
+IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntRect& localRect) const
 {
     // Scrollbars won't be transformed within us
     IntRect newRect = localRect;
-    newRect.moveBy(scrollbar->location());
+    newRect.moveBy(scrollbar.location());
     return newRect;
 }
 
-IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
+IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntRect& parentRect) const
 {
     IntRect newRect = parentRect;
     // Scrollbars won't be transformed within us
-    newRect.moveBy(-scrollbar->location());
+    newRect.moveBy(-scrollbar.location());
     return newRect;
 }
 
 // FIXME: test these on windows
-IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
+IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntPoint& localPoint) const
 {
     // Scrollbars won't be transformed within us
     IntPoint newPoint = localPoint;
-    newPoint.moveBy(scrollbar->location());
+    newPoint.moveBy(scrollbar.location());
     return newPoint;
 }
 
-IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
+IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntPoint& parentPoint) const
 {
     IntPoint newPoint = parentPoint;
     // Scrollbars won't be transformed within us
-    newPoint.moveBy(-scrollbar->location());
+    newPoint.moveBy(-scrollbar.location());
     return newPoint;
 }
 
@@ -1376,9 +1406,8 @@ void ScrollView::setParentVisible(bool visible)
     if (!isSelfVisible())
         return;
         
-    HashSet<RefPtr<Widget>>::iterator end = m_children.end();
-    for (HashSet<RefPtr<Widget>>::iterator it = m_children.begin(); it != end; ++it)
-        (*it)->setParentVisible(visible);
+    for (auto& child : m_children)
+        child->setParentVisible(visible);
 }
 
 void ScrollView::show()
@@ -1386,9 +1415,8 @@ void ScrollView::show()
     if (!isSelfVisible()) {
         setSelfVisible(true);
         if (isParentVisible()) {
-            HashSet<RefPtr<Widget>>::iterator end = m_children.end();
-            for (HashSet<RefPtr<Widget>>::iterator it = m_children.begin(); it != end; ++it)
-                (*it)->setParentVisible(true);
+            for (auto& child : m_children)
+                child->setParentVisible(true);
         }
     }
 
@@ -1399,9 +1427,8 @@ void ScrollView::hide()
 {
     if (isSelfVisible()) {
         if (isParentVisible()) {
-            HashSet<RefPtr<Widget>>::iterator end = m_children.end();
-            for (HashSet<RefPtr<Widget>>::iterator it = m_children.begin(); it != end; ++it)
-                (*it)->setParentVisible(false);
+            for (auto& child : m_children)
+                child->setParentVisible(false);
         }
         setSelfVisible(false);
     }
@@ -1456,7 +1483,24 @@ void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePositionAtAl
     
     // Update if the scroll origin changes, since our position will be different if the content size did not change.
     if (updatePositionAtAll && updatePositionSynchronously)
-        updateScrollbars(scrollOffset());
+        updateScrollbars(scrollPosition());
+}
+
+void ScrollView::styleDidChange()
+{
+    if (m_horizontalScrollbar)
+        m_horizontalScrollbar->styleChanged();
+
+    if (m_verticalScrollbar)
+        m_verticalScrollbar->styleChanged();
+}
+
+IntPoint ScrollView::locationOfContents() const
+{
+    IntPoint result = location();
+    if (shouldPlaceBlockDirectionScrollbarOnLeft() && m_verticalScrollbar)
+        result.move(m_verticalScrollbar->occupiedWidth(), 0);
+    return result;
 }
 
 #if !PLATFORM(COCOA)
@@ -1513,11 +1557,30 @@ IntRect ScrollView::platformVisibleContentRect(bool) const
     return IntRect();
 }
 
+float ScrollView::platformTopContentInset() const
+{
+    return 0;
+}
+
+void ScrollView::platformSetTopContentInset(float)
+{
+}
+
 IntSize ScrollView::platformVisibleContentSize(bool) const
 {
     return IntSize();
 }
 
+IntRect ScrollView::platformVisibleContentRectIncludingObscuredArea(bool) const
+{
+    return IntRect();
+}
+
+IntSize ScrollView::platformVisibleContentSizeIncludingObscuredArea(bool) const
+{
+    return IntSize();
+}
+
 void ScrollView::platformSetContentsSize()
 {
 }