summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h
blob: 3ca6c592d3bb59e0ed53de0583491dc338a31d34 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// 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 THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_SCROLLING_ROOT_SCROLLER_CONTROLLER_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_SCROLLING_ROOT_SCROLLER_CONTROLLER_H_

#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/heap/handle.h"

namespace blink {

class Document;
class Element;
class HTMLFrameOwnerElement;
class PaintLayer;

// Manages the root scroller associated with a given document. The root
// scroller causes browser controls movement, overscroll effects and prevents
// chaining scrolls up further in the DOM. It can be set from script using
// document.setRootScroller. High-level details are available in README.md.
//
// 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
// scroller using document.setRootScroller. If the page didn't set a root
// scroller this will be nullptr. The "effective" root scroller is the current
// Node we're using internally to apply viewport scrolling actions.  Both these
// elements come from this controller's associated Document. The final "global"
// root scroller, the one whose scrolling hides browser controls, may be in a
// different frame.
//
// If the currently set m_rootScroller is a valid element to become the root
// scroller, it will be promoted to the effective root scroller. If it is not
// valid, the effective root scroller will fall back to the document Node. The
// rules for what makes an element a valid root scroller are set in
// isValidRootScroller(). The validity of the current root scroller is
// re-checked after layout as part of the document lifecycle.
class CORE_EXPORT RootScrollerController
    : public GarbageCollected<RootScrollerController> {
 public:
  // Creates a RootScrollerController for the given document. Note: instances
  // of this class need to be made aware of layout updates.
  static RootScrollerController* Create(Document&);

  RootScrollerController(Document&);

  void Trace(blink::Visitor*);

  // 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
  // the effective root scroller.
  void Set(Element*);

  // Returns the element currently set as the root scroller from script. This
  // differs from the effective root scroller since the set Element may not
  // currently be a valid root scroller. e.g. If the page sets an Element
  // with `display: none`, get() will return that element, even though the
  // effective root scroller will remain the document Node.
  Element* Get() const;

  // This returns the Element that's actually being used to control viewport
  // actions right now. This is different from get() if a root scroller hasn't
  // been set, or if the set root scroller isn't currently a valid scroller.
  // See README.md for the difference between the root scroller from Get(), the
  // effective rootScroller, and the global RootScroller in
  // TopDocumentRootScrollerController.
  Node& EffectiveRootScroller() const;

  // This class needs to be informed when the FrameView of its Document changes
  // size. This may occur without a layout (e.g. URL bar hiding) so we can't
  // rely on DidUpdateMainFrameLayout.
  void DidResizeFrameView();

  // Called when an iframe in this document has an updated FrameView (e.g.
  // FrameView removed, swapped, etc.) so that we can recompute the effective
  // root scroller and set the appropriate properties on the view.
  void DidUpdateIFrameFrameView(HTMLFrameOwnerElement&);

  // Returns the PaintLayer associated with the currently effective root
  // scroller.
  PaintLayer* RootScrollerPaintLayer() const;

  void ElementRemoved(const Element&);

  // In the "implicit root scroller" mode, we might promote an element to
  // become the effective root scroller even if the page doesn't set it as so
  // to improve the user experience.  In this mode, as elements layout they'll
  // call this method and, if they meet the root scroller restrictions, will be
  // added to the implicit candidate set. After layout is done we'll go
  // through that set and select the best candidate.
  void ConsiderForImplicit(Node&);

  // Called as part of the main document lifecycle. This will iterate the frame
  // tree in post order and select the effective root scroller in each frame.
  void PerformRootScrollerSelection();

 private:
  // Ensures the effective root scroller is currently valid and replaces it
  // with the default if not.
  void RecomputeEffectiveRootScroller();

  // Determines whether the given element meets the criteria to become the
  // effective root scroller.
  bool IsValidRootScroller(const Element&) const;

  // Determines whether the given element meets the criteria to be implicitly
  // set as the root scroller (in addition to being a valid root scroller).
  bool IsValidImplicit(const Element&) const;

  // Determines whether the given element is eligable as a candidate to be
  // implicitly promoted. Intuitively, thiis is any "live" scroller on the page.
  // We add these to a list of candidates and after layout we go through the
  // list and promote the best candidate that satisfies the more exhaustive
  // conditions set by IsValidImplicit above. At that time we also prune the
  // list of any elements that no longer satisfy IsValidImplicitCandidate.
  bool IsValidImplicitCandidate(const Element&) const;

  // Set certain properties to the effective root scroller. Called when a Node
  // becomes or unbecomes the effective root scroller. Calling this method can
  // leave the node's frame with a dirty layout due to the fact that layout size
  // depends on whether the element is the effective root scroller or not.
  void ApplyRootScrollerProperties(Node&);

  void UpdateIFrameGeometryAndLayoutSize(HTMLFrameOwnerElement&) const;

  // Called after layout, runs through implicit candidates, removing ones that
  // are no longer meet the root scroller restrictions. Of the remaining ones,
  // will choose the best and set it as the implicit_root_scroller_.
  void ProcessImplicitCandidates();

  // Calls function for each non-throttled frame's RootScrollerController in
  // post tree order.
  template <typename Function>
  void ForAllNonThrottledLocalControllers(const Function&);

  // The owning Document whose root scroller this object manages.
  WeakMember<Document> document_;

  // The Element that was set from script as rootScroller for this Document.
  // 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> root_scroller_;

  // The Node currently being used as the root scroller in this Document.
  // If the m_rootScroller is valid this will point to it. Otherwise, it'll
  // use the document Node. It'll never be nullptr since the Document owns the
  // RootScrollerController.
  Member<Node> effective_root_scroller_;

  // Candidate Elements that we should examine after layout to determine which
  // should be root scroller. This is used when "implicit root scroller" is
  // enabled, where a valid Element can become the root scroller without being
  // explicitly set using document.setRootScroller.
  HeapHashSet<WeakMember<Element>> implicit_candidates_;

  WeakMember<Element> implicit_root_scroller_;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_SCROLLING_ROOT_SCROLLER_CONTROLLER_H_