blob: 62f07ddbc61bbb4f4c0ec1713ffdd3cb3951b64b [file] [log] [blame]
// Copyright 2015 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 ScrollAnchor_h
#define ScrollAnchor_h
#include "core/CoreExport.h"
#include "platform/geometry/LayoutPoint.h"
#include "platform/heap/Handle.h"
namespace blink {
class LayoutObject;
class ScrollableArea;
// Scrolls to compensate for layout movements (bit.ly/scroll-anchoring).
class CORE_EXPORT ScrollAnchor final {
DISALLOW_NEW();
public:
ScrollAnchor();
explicit ScrollAnchor(ScrollableArea*);
~ScrollAnchor();
// The scroller that is scrolled to componsate for layout movements. Note
// that the scroller can only be initialized once.
void setScroller(ScrollableArea*);
// Returns true if the underlying scroller is set.
bool hasScroller() const { return m_scroller; }
// The LayoutObject we are currently anchored to. Lazily computed during
// notifyBeforeLayout() and cached until the next call to clear().
LayoutObject* anchorObject() const { return m_anchorObject; }
// Indicates that this ScrollAnchor, and all ancestor ScrollAnchors, should
// compute new anchor nodes on their next notifyBeforeLayout().
void clear();
// Indicates that this ScrollAnchor should compute a new anchor node on the
// next call to notifyBeforeLayout().
void clearSelf();
// Records the anchor's location in relation to the scroller. Should be
// called when the scroller is about to be laid out.
void notifyBeforeLayout();
// Scrolls to compensate for any change in the anchor's relative location.
// Should be called at the end of the animation frame.
void adjust();
enum class Corner {
TopLeft = 0,
TopRight,
};
// Which corner of the anchor object we are currently anchored to.
// Only meaningful if anchorObject() is non-null.
Corner corner() const { return m_corner; }
// Checks if we hold any references to the specified object.
bool refersTo(const LayoutObject*) const;
// Notifies us that an object will be removed from the layout tree.
void notifyRemoved(LayoutObject*);
DEFINE_INLINE_TRACE() { visitor->trace(m_scroller); }
private:
void findAnchor();
bool computeScrollAnchorDisablingStyleChanged();
enum WalkStatus { Skip = 0, Constrain, Continue, Return };
struct ExamineResult {
ExamineResult(WalkStatus s)
: status(s), viable(false), corner(Corner::TopLeft) {}
ExamineResult(WalkStatus s, Corner c)
: status(s), viable(true), corner(c) {}
WalkStatus status;
bool viable;
Corner corner;
};
ExamineResult examine(const LayoutObject*) const;
IntSize computeAdjustment() const;
// The scroller to be adjusted by this ScrollAnchor. This is also the scroller
// that owns us, unless it is the RootFrameViewport in which case we are owned
// by the layout viewport.
Member<ScrollableArea> m_scroller;
// The LayoutObject we should anchor to.
LayoutObject* m_anchorObject;
// Which corner of m_anchorObject's bounding box to anchor to.
Corner m_corner;
// Location of m_layoutObject relative to scroller at time of
// notifyBeforeLayout().
LayoutPoint m_savedRelativeOffset;
// We suppress scroll anchoring after a style change on the anchor node or
// one of its ancestors, if that change might have caused the node to move.
// This bit tracks whether we have had a scroll-anchor-disabling style
// change since the last layout. It is recomputed in notifyBeforeLayout(),
// and used to suppress adjustment in adjust(). See http://bit.ly/sanaclap.
bool m_scrollAnchorDisablingStyleChanged;
// True iff an adjustment check has been queued with the FrameView but not yet
// performed.
bool m_queued;
};
} // namespace blink
#endif // ScrollAnchor_h