Commit f8bc5669 authored by bokan's avatar bokan Committed by Commit bot

Split RootScrollerController into top-document and child-document classes

Work on making RootScrollerController work across iframes revealed that there's
two responsibilities that need management. All frames need a
RootScrollerController to manage the Element currently set as root scroller and
determine which Element should eventually become the effective root scroller.
In addition, we need the RootScrollerController to manage the
ViewportApplyScroll and set it on the appropriate element. Since only one
Element on the whole page needs this callback set, it makes sense that only the
top document should have this responsibility. Experience with classes like
FrameView and EventHandler shows that this may be better served by subclassing
the top level object rather than having isMainFrame() checks intermingled with
the other code.

BUG=505516

Review-Url: https://codereview.chromium.org/2280453002
Cr-Commit-Position: refs/heads/master@{#414796}
parent 05ee9971
...@@ -2061,6 +2061,8 @@ ...@@ -2061,6 +2061,8 @@
'page/scrolling/ScrollStateCallback.h', 'page/scrolling/ScrollStateCallback.h',
'page/scrolling/StickyPositionScrollingConstraints.cpp', 'page/scrolling/StickyPositionScrollingConstraints.cpp',
'page/scrolling/StickyPositionScrollingConstraints.h', 'page/scrolling/StickyPositionScrollingConstraints.h',
'page/scrolling/TopDocumentRootScrollerController.cpp',
'page/scrolling/TopDocumentRootScrollerController.h',
'page/scrolling/ViewportScrollCallback.cpp', 'page/scrolling/ViewportScrollCallback.cpp',
'page/scrolling/ViewportScrollCallback.h', 'page/scrolling/ViewportScrollCallback.h',
'paint/BackgroundImageGeometry.cpp', 'paint/BackgroundImageGeometry.cpp',
......
...@@ -207,7 +207,7 @@ ...@@ -207,7 +207,7 @@
#include "core/page/scrolling/ScrollStateCallback.h" #include "core/page/scrolling/ScrollStateCallback.h"
#include "core/page/scrolling/ScrollingCoordinator.h" #include "core/page/scrolling/ScrollingCoordinator.h"
#include "core/page/scrolling/SnapCoordinator.h" #include "core/page/scrolling/SnapCoordinator.h"
#include "core/page/scrolling/ViewportScrollCallback.h" #include "core/page/scrolling/TopDocumentRootScrollerController.h"
#include "core/svg/SVGDocumentExtensions.h" #include "core/svg/SVGDocumentExtensions.h"
#include "core/svg/SVGScriptElement.h" #include "core/svg/SVGScriptElement.h"
#include "core/svg/SVGTitleElement.h" #include "core/svg/SVGTitleElement.h"
...@@ -473,7 +473,11 @@ Document::Document(const DocumentInit& initializer, DocumentClassFlags documentC ...@@ -473,7 +473,11 @@ Document::Document(const DocumentInit& initializer, DocumentClassFlags documentC
m_fetcher = ResourceFetcher::create(nullptr); m_fetcher = ResourceFetcher::create(nullptr);
} }
m_rootScrollerController = RootScrollerController::create(*this); // TODO(bokan): This will probably blow up if we don't have an m_frame here
// since we'll assume a child RootScrollerController. crbug.com/505516.
m_rootScrollerController = isInMainFrame()
? TopDocumentRootScrollerController::create(*this)
: RootScrollerController::create(*this);
// We depend on the url getting immediately set in subframes, but we // We depend on the url getting immediately set in subframes, but we
// also depend on the url NOT getting immediately set in opened windows. // also depend on the url NOT getting immediately set in opened windows.
......
...@@ -8,13 +8,7 @@ ...@@ -8,13 +8,7 @@
#include "core/dom/Element.h" #include "core/dom/Element.h"
#include "core/frame/FrameHost.h" #include "core/frame/FrameHost.h"
#include "core/frame/FrameView.h" #include "core/frame/FrameView.h"
#include "core/frame/TopControls.h"
#include "core/frame/VisualViewport.h"
#include "core/layout/LayoutBox.h" #include "core/layout/LayoutBox.h"
#include "core/page/ChromeClient.h"
#include "core/page/Page.h"
#include "core/page/scrolling/OverscrollController.h"
#include "core/page/scrolling/ViewportScrollCallback.h"
#include "core/paint/PaintLayerScrollableArea.h" #include "core/paint/PaintLayerScrollableArea.h"
#include "platform/graphics/GraphicsLayer.h" #include "platform/graphics/GraphicsLayer.h"
#include "platform/scroll/ScrollableArea.h" #include "platform/scroll/ScrollableArea.h"
...@@ -25,24 +19,6 @@ class RootFrameViewport; ...@@ -25,24 +19,6 @@ class RootFrameViewport;
namespace { namespace {
ScrollableArea* scrollableAreaFor(const Element& element)
{
if (!element.layoutObject() || !element.layoutObject()->isBox())
return nullptr;
LayoutBox* box = toLayoutBox(element.layoutObject());
// For a FrameView, we use the layoutViewport rather than the
// getScrollableArea() since that could be the RootFrameViewport. The
// rootScroller's ScrollableArea will be swapped in as the layout viewport
// in RootFrameViewport so we need to ensure we get the layout viewport.
if (box->isDocumentElement())
return element.document().view()->layoutViewportScrollableArea();
return static_cast<PaintInvalidationCapableScrollableArea*>(
box->getScrollableArea());
}
bool fillsViewport(const Element& element) bool fillsViewport(const Element& element)
{ {
DCHECK(element.layoutObject()); DCHECK(element.layoutObject());
...@@ -66,22 +42,14 @@ bool fillsViewport(const Element& element) ...@@ -66,22 +42,14 @@ bool fillsViewport(const Element& element)
&& boundingBox.size() == topDocument.layoutViewItem().size(); && boundingBox.size() == topDocument.layoutViewItem().size();
} }
bool isValidRootScroller(const Element& element) } // namespace
{
if (!element.layoutObject())
return false;
if (!scrollableAreaFor(element))
return false;
if (!fillsViewport(element))
return false;
return true; // static
RootScrollerController* RootScrollerController::create(Document& document)
{
return new RootScrollerController(document);
} }
} // namespace
RootScrollerController::RootScrollerController(Document& document) RootScrollerController::RootScrollerController(Document& document)
: m_document(&document) : m_document(&document)
{ {
...@@ -90,10 +58,8 @@ RootScrollerController::RootScrollerController(Document& document) ...@@ -90,10 +58,8 @@ RootScrollerController::RootScrollerController(Document& document)
DEFINE_TRACE(RootScrollerController) DEFINE_TRACE(RootScrollerController)
{ {
visitor->trace(m_document); visitor->trace(m_document);
visitor->trace(m_viewportApplyScroll);
visitor->trace(m_rootScroller); visitor->trace(m_rootScroller);
visitor->trace(m_effectiveRootScroller); visitor->trace(m_effectiveRootScroller);
visitor->trace(m_currentViewportApplyScrollHost);
} }
void RootScrollerController::set(Element* newRootScroller) void RootScrollerController::set(Element* newRootScroller)
...@@ -130,72 +96,47 @@ void RootScrollerController::updateEffectiveRootScroller() ...@@ -130,72 +96,47 @@ void RootScrollerController::updateEffectiveRootScroller()
return; return;
m_effectiveRootScroller = newEffectiveRootScroller; m_effectiveRootScroller = newEffectiveRootScroller;
if (m_document->isInMainFrame())
setViewportApplyScrollOnRootScroller();
} }
void RootScrollerController::setViewportApplyScrollOnRootScroller() ScrollableArea* RootScrollerController::scrollableAreaFor(
const Element& element) const
{ {
DCHECK(m_document->isInMainFrame()); if (!element.layoutObject() || !element.layoutObject()->isBox())
return nullptr;
if (!m_viewportApplyScroll || !m_effectiveRootScroller) LayoutBox* box = toLayoutBox(element.layoutObject());
return;
ScrollableArea* targetScroller = // For a FrameView, we use the layoutViewport rather than the
scrollableAreaFor(*m_effectiveRootScroller); // getScrollableArea() since that could be the RootFrameViewport. The
// rootScroller's ScrollableArea will be swapped in as the layout viewport
// in RootFrameViewport so we need to ensure we get the layout viewport.
if (box->isDocumentElement())
return element.document().view()->layoutViewportScrollableArea();
if (!targetScroller) return static_cast<PaintInvalidationCapableScrollableArea*>(
return; box->getScrollableArea());
}
if (m_currentViewportApplyScrollHost) bool RootScrollerController::isValidRootScroller(const Element& element) const
m_currentViewportApplyScrollHost->removeApplyScroll(); {
if (!element.layoutObject())
return false;
// Use disable-native-scroll since the ViewportScrollCallback needs to if (!scrollableAreaFor(element))
// apply scroll actions both before (TopControls) and after (overscroll) return false;
// scrolling the element so it will apply scroll to the element itself.
m_effectiveRootScroller->setApplyScroll(
m_viewportApplyScroll, "disable-native-scroll");
m_currentViewportApplyScrollHost = m_effectiveRootScroller; if (!fillsViewport(element))
return false;
// Ideally, scroll customization would pass the current element to scroll to return true;
// the apply scroll callback but this doesn't happen today so we set it
// through a back door here. This is also needed by the
// ViewportScrollCallback to swap the target into the layout viewport
// in RootFrameViewport.
m_viewportApplyScroll->setScroller(targetScroller);
} }
void RootScrollerController::didUpdateCompositing() void RootScrollerController::didUpdateCompositing()
{ {
FrameHost* frameHost = m_document->frameHost();
// Let the compositor-side counterpart know about this change.
if (frameHost && m_document->isInMainFrame())
frameHost->chromeClient().registerViewportLayers();
} }
void RootScrollerController::didAttachDocument() void RootScrollerController::didAttachDocument()
{ {
if (!m_document->isInMainFrame())
return;
FrameHost* frameHost = m_document->frameHost();
FrameView* frameView = m_document->view();
if (!frameHost || !frameView)
return;
RootFrameViewport* rootFrameViewport = frameView->getRootFrameViewport();
DCHECK(rootFrameViewport);
m_viewportApplyScroll = ViewportScrollCallback::create(
&frameHost->topControls(),
&frameHost->overscrollController(),
*rootFrameViewport);
updateEffectiveRootScroller();
} }
GraphicsLayer* RootScrollerController::rootScrollerLayer() GraphicsLayer* RootScrollerController::rootScrollerLayer()
...@@ -220,10 +161,13 @@ GraphicsLayer* RootScrollerController::rootScrollerLayer() ...@@ -220,10 +161,13 @@ GraphicsLayer* RootScrollerController::rootScrollerLayer()
bool RootScrollerController::isViewportScrollCallback( bool RootScrollerController::isViewportScrollCallback(
const ScrollStateCallback* callback) const const ScrollStateCallback* callback) const
{ {
if (!callback) // TopDocumentRootScrollerController must override this method to actually
return false; // do the comparison.
DCHECK(!m_document->isInMainFrame());
return callback == m_viewportApplyScroll.get(); RootScrollerController* topDocumentController =
m_document->topDocument().rootScrollerController();
return topDocumentController->isViewportScrollCallback(callback);
} }
Element* RootScrollerController::defaultEffectiveRootScroller() Element* RootScrollerController::defaultEffectiveRootScroller()
......
...@@ -13,41 +13,36 @@ namespace blink { ...@@ -13,41 +13,36 @@ namespace blink {
class Document; class Document;
class Element; class Element;
class GraphicsLayer; class GraphicsLayer;
class ScrollableArea;
class ScrollStateCallback; class ScrollStateCallback;
class ViewportScrollCallback;
// Manages the root scroller associated with a given document. The root scroller // Manages the root scroller associated with a given document. The root
// causes top controls movement, overscroll effects and prevents chaining // scroller causes top controls movement, overscroll effects and prevents
// scrolls up further in the DOM. It can be set from script using // chaining scrolls up further in the DOM. It can be set from script using
// document.setRootScroller. // document.setRootScroller.
// //
// There are two notions of a root scroller in this class: m_rootScroller and // There are two notions of a root scroller in this class: m_rootScroller and
// m_effectiveRootScroller. The former is the Element that was set as the root // m_effectiveRootScroller. The former is the Element that was set as the root
// scroller using document.setRootScroller. If the page didn't set a root // scroller using document.setRootScroller. If the page didn't set a root
// scroller this will be nullptr. The "effective" root scroller is the current // scroller this will be nullptr. The "effective" root scroller is the current
// element we're using internally to apply viewport scroll actions. i.e It's the // element we're using internally to apply viewport scrolling actions. The
// element with the ViewportScrollCallback set as its apply-scroll callback. // effective root scroller will only be null during document initialization.
// The effective root scroller will only be null during document initialization.
// //
// If the root scroller element is a valid element to become the root scroller, // If the currently set m_rootScroller is a valid element to become the root
// it will be promoted to the effective root scroller. If it is not valid, the // scroller, it will be promoted to the effective root scroller. If it is not
// effective root scroller will fall back to a default Element (see // valid, the effective root scroller will fall back to a default Element (see
// defaultEffectiveRootScroller()). The rules for what makes an element a valid // defaultEffectiveRootScroller()). The rules for what makes an element a valid
// root scroller are set in isValidRootScroller(). The validity of the current // root scroller are set in isValidRootScroller(). The validity of the current
// root scroller is re-checked after each layout. // root scroller is re-checked after each layout.
class CORE_EXPORT RootScrollerController class CORE_EXPORT RootScrollerController
: public GarbageCollected<RootScrollerController> { : public GarbageCollected<RootScrollerController> {
public: public:
// Creates a RootScrollerController for the given document. You should use // Creates a RootScrollerController for the given document. Note, instances
// setViewportScrollCallback to provide this class with a scroll callback // of this class need to be made aware of lifecycle events, see the
// that RootScrollerController will keep applied to the current RootScroller // didUpdateLayout, didUpdateCompositing, etc. type methods below.
// so that special actions can occur on scrolling. static RootScrollerController* create(Document&);
static RootScrollerController* create(Document& document)
{
return new RootScrollerController(document);
}
DECLARE_TRACE(); DECLARE_VIRTUAL_TRACE();
// Sets the element that will be used as the root scroller. This can be // Sets the element that will be used as the root scroller. This can be
// nullptr, in which case we'll use the default element (documentElement) as // nullptr, in which case we'll use the default element (documentElement) as
...@@ -74,36 +69,50 @@ public: ...@@ -74,36 +69,50 @@ public:
// This class needs to be informed of changes to compositing so that it can // This class needs to be informed of changes to compositing so that it can
// update the compositor when the effective root scroller changes. // update the compositor when the effective root scroller changes.
void didUpdateCompositing(); virtual void didUpdateCompositing();
// This class needs to be informed when the document has been attached to a // This class needs to be informed when the document has been attached to a
// FrameView so that we can initialize the viewport scroll callback. // FrameView so that we can initialize the viewport scroll callback.
void didAttachDocument(); virtual void didAttachDocument();
// Returns the GraphicsLayer for the current effective root scroller
// element.
GraphicsLayer* rootScrollerLayer(); GraphicsLayer* rootScrollerLayer();
// Returns true if the given ScrollStateCallback is the ViewportScrollCallback managed
// by this class.
// TODO(bokan): Temporarily needed to allow ScrollCustomization to // TODO(bokan): Temporarily needed to allow ScrollCustomization to
// differentiate between real custom callback and the built-in viewport // differentiate between real custom callback and the built-in viewport
// apply scroll. // apply scroll. crbug.com/623079.
bool isViewportScrollCallback(const ScrollStateCallback*) const; virtual bool isViewportScrollCallback(const ScrollStateCallback*) const;
private: protected:
RootScrollerController(Document&); RootScrollerController(Document&);
Element* defaultEffectiveRootScroller();
// Ensures the effective root scroller is currently valid and replaces it // Ensures the effective root scroller is currently valid and replaces it
// with the default if not. // with the default if not.
void updateEffectiveRootScroller(); virtual void updateEffectiveRootScroller();
// Called only from the top Document's RootScrollerController. Ensures that // Returns the ScrollableArea to use to scroll the given Element.
// the element that should be used as the root scroller on the page has the ScrollableArea* scrollableAreaFor(const Element&) const;
// m_viewportApplyScroll callback set on it.
void setViewportApplyScrollOnRootScroller();
// The owning Document whose root scroller this object manages.
WeakMember<Document> m_document; WeakMember<Document> m_document;
Member<ViewportScrollCallback> m_viewportApplyScroll;
private:
// Determines whether the given element meets the criteria to become the
// effective root scroller.
bool isValidRootScroller(const Element&) const;
// Returns the Element that should be used if the currently set
// m_rootScroller isn't valid to be a root scroller.
Element* defaultEffectiveRootScroller();
// The Element that was set from script as rootScroller. Depending on its
// validity to be the root scroller (e.g. a display: none element isn't a
// valid root scroller), this may not actually be the Element being used as
// the root scroller.
WeakMember<Element> m_rootScroller; WeakMember<Element> m_rootScroller;
// The element currently being used as the root scroller. If // The element currently being used as the root scroller. If
...@@ -112,10 +121,6 @@ private: ...@@ -112,10 +121,6 @@ private:
// initialization and will not be set until m_viewportApplyScroll is // initialization and will not be set until m_viewportApplyScroll is
// provided. // provided.
WeakMember<Element> m_effectiveRootScroller; WeakMember<Element> m_effectiveRootScroller;
// Tracks which element currently has the m_viewportApplyScroll set to it.
// This will only ever be set on the top Document's RootScrollerController.
WeakMember<Element> m_currentViewportApplyScrollHost;
}; };
} // namespace blink } // namespace blink
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/page/scrolling/TopDocumentRootScrollerController.h"
#include "core/dom/Document.h"
#include "core/dom/Element.h"
#include "core/frame/FrameHost.h"
#include "core/frame/FrameView.h"
#include "core/page/ChromeClient.h"
#include "core/page/scrolling/OverscrollController.h"
#include "core/page/scrolling/ViewportScrollCallback.h"
#include "platform/scroll/ScrollableArea.h"
namespace blink {
// static
TopDocumentRootScrollerController* TopDocumentRootScrollerController::create(
Document& document)
{
return new TopDocumentRootScrollerController(document);
}
TopDocumentRootScrollerController::TopDocumentRootScrollerController(
Document& document)
: RootScrollerController(document)
{
}
DEFINE_TRACE(TopDocumentRootScrollerController)
{
visitor->trace(m_viewportApplyScroll);
visitor->trace(m_currentViewportApplyScrollHost);
RootScrollerController::trace(visitor);
}
void TopDocumentRootScrollerController::updateEffectiveRootScroller()
{
RootScrollerController::updateEffectiveRootScroller();
setViewportApplyScrollOnRootScroller();
}
void TopDocumentRootScrollerController::setViewportApplyScrollOnRootScroller()
{
if (!m_viewportApplyScroll || !effectiveRootScroller())
return;
ScrollableArea* targetScroller =
scrollableAreaFor(*effectiveRootScroller());
if (!targetScroller)
return;
if (m_currentViewportApplyScrollHost)
m_currentViewportApplyScrollHost->removeApplyScroll();
// Use disable-native-scroll since the ViewportScrollCallback needs to
// apply scroll actions both before (TopControls) and after (overscroll)
// scrolling the element so it will apply scroll to the element itself.
effectiveRootScroller()->setApplyScroll(
m_viewportApplyScroll, "disable-native-scroll");
m_currentViewportApplyScrollHost = effectiveRootScroller();
// Ideally, scroll customization would pass the current element to scroll to
// the apply scroll callback but this doesn't happen today so we set it
// through a back door here. This is also needed by the
// ViewportScrollCallback to swap the target into the layout viewport
// in RootFrameViewport.
m_viewportApplyScroll->setScroller(targetScroller);
}
void TopDocumentRootScrollerController::didUpdateCompositing()
{
FrameHost* frameHost = m_document->frameHost();
// Let the compositor-side counterpart know about this change.
if (frameHost)
frameHost->chromeClient().registerViewportLayers();
}
void TopDocumentRootScrollerController::didAttachDocument()
{
FrameHost* frameHost = m_document->frameHost();
FrameView* frameView = m_document->view();
if (!frameHost || !frameView)
return;
RootFrameViewport* rootFrameViewport = frameView->getRootFrameViewport();
DCHECK(rootFrameViewport);
m_viewportApplyScroll = ViewportScrollCallback::create(
&frameHost->topControls(),
&frameHost->overscrollController(),
*rootFrameViewport);
updateEffectiveRootScroller();
}
bool TopDocumentRootScrollerController::isViewportScrollCallback(
const ScrollStateCallback* callback) const
{
if (!callback)
return false;
return callback == m_viewportApplyScroll.get();
}
} // namespace blink
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TopDocumentRootScrollerController_h
#define TopDocumentRootScrollerController_h
#include "core/CoreExport.h"
#include "core/page/scrolling/RootScrollerController.h"
#include "platform/heap/Handle.h"
namespace blink {
class Document;
class Element;
class GraphicsLayer;
class ScrollStateCallback;
class ViewportScrollCallback;
// The RootScrollerController used to manage the root scroller for the top
// level Document on a page. In addition to the regular RootScroller duties
// such as keeping track of which Element is set as root scroller and which is
// the effective root scroller, this class is also responsible for setting the
// ViewportApplyScroll on the one Element on a page that should apply viewport
// scrolling actions.
class CORE_EXPORT TopDocumentRootScrollerController
: public RootScrollerController {
public:
static TopDocumentRootScrollerController* create(Document&);
DECLARE_VIRTUAL_TRACE();
// This class needs to be informed of changes to compositing so that it can
// update the compositor when the effective root scroller changes.
void didUpdateCompositing() override;
// This class needs to be informed when the document has been attached to a
// FrameView so that we can initialize the viewport scroll callback.
void didAttachDocument() override;
// Returns true if the given ScrollStateCallback is the ViewportScrollCallback managed
// by this class.
// TODO(bokan): Temporarily needed to allow ScrollCustomization to
// differentiate between real custom callback and the built-in viewport
// apply scroll. crbug.com/623079.
bool isViewportScrollCallback(
const ScrollStateCallback*) const override;
protected:
TopDocumentRootScrollerController(Document&);
// Ensures the effective root scroller is currently valid and replaces it
// with the default if not.
void updateEffectiveRootScroller() override;
private:
// Ensures that the element that should be used as the root scroller on the
// page has the m_viewportApplyScroll callback set on it.
void setViewportApplyScrollOnRootScroller();
// The apply-scroll callback that moves top controls and produces
// overscroll effects. This class makes sure this callback is set on the
// appropriate root scroller element.
Member<ViewportScrollCallback> m_viewportApplyScroll;
// Tracks which element currently has the m_viewportApplyScroll set to it.
WeakMember<Element> m_currentViewportApplyScrollHost;
};
} // namespace blink
#endif // RootScrollerController_h
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment