Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: third_party/WebKit/Source/core/layout/ScrollAnchor.cpp

Issue 1971423002: Implement bounce suppression in ScrollAnchor. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address ymalik review comments Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/Source/core/layout/ScrollAnchor.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/layout/ScrollAnchor.h" 5 #include "core/layout/ScrollAnchor.h"
6 6
7 #include "core/frame/FrameView.h" 7 #include "core/frame/FrameView.h"
8 #include "core/frame/UseCounter.h" 8 #include "core/frame/UseCounter.h"
9 #include "core/layout/LayoutView.h" 9 #include "core/layout/LayoutView.h"
10 #include "core/layout/line/InlineTextBox.h" 10 #include "core/layout/line/InlineTextBox.h"
11 #include "core/paint/PaintLayerScrollableArea.h" 11 #include "core/paint/PaintLayerScrollableArea.h"
12 #include "platform/Histogram.h" 12 #include "platform/Histogram.h"
13 13
14 namespace blink { 14 namespace blink {
15 15
16 using Corner = ScrollAnchor::Corner; 16 using Corner = ScrollAnchor::Corner;
17 17
18 ScrollAnchor::ScrollAnchor(ScrollableArea* scroller) 18 ScrollAnchor::ScrollAnchor(ScrollableArea* scroller)
19 : m_scroller(scroller) 19 : m_scroller(scroller)
20 , m_anchorObject(nullptr) 20 , m_hasBounced(false)
21 , m_corner(Corner::TopLeft)
22 { 21 {
23 ASSERT(m_scroller); 22 ASSERT(m_scroller);
24 ASSERT(m_scroller->isFrameView() || m_scroller->isPaintLayerScrollableArea() ); 23 ASSERT(m_scroller->isFrameView() || m_scroller->isPaintLayerScrollableArea() );
25 } 24 }
26 25
27 ScrollAnchor::~ScrollAnchor() 26 ScrollAnchor::~ScrollAnchor()
28 { 27 {
29 } 28 }
30 29
31 // TODO(pilgrim) replace all instances of scrollerLayoutBox with scrollerLayoutB oxItem 30 // TODO(pilgrim) replace all instances of scrollerLayoutBox with scrollerLayoutB oxItem
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
143 void ScrollAnchor::findAnchor() 142 void ScrollAnchor::findAnchor()
144 { 143 {
145 TRACE_EVENT0("blink", "ScrollAnchor::findAnchor"); 144 TRACE_EVENT0("blink", "ScrollAnchor::findAnchor");
146 SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Layout.ScrollAnchor.TimeToFindAnchor"); 145 SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Layout.ScrollAnchor.TimeToFindAnchor");
147 146
148 LayoutObject* stayWithin = scrollerLayoutBox(m_scroller); 147 LayoutObject* stayWithin = scrollerLayoutBox(m_scroller);
149 LayoutObject* candidate = stayWithin->nextInPreOrder(stayWithin); 148 LayoutObject* candidate = stayWithin->nextInPreOrder(stayWithin);
150 while (candidate) { 149 while (candidate) {
151 ExamineResult result = examine(candidate); 150 ExamineResult result = examine(candidate);
152 if (result.viable) { 151 if (result.viable) {
153 m_anchorObject = candidate; 152 m_current.m_anchorObject = candidate;
154 m_corner = result.corner; 153 m_current.m_corner = result.corner;
155 } 154 }
156 switch (result.status) { 155 switch (result.status) {
157 case Skip: 156 case Skip:
158 candidate = candidate->nextInPreOrderAfterChildren(stayWithin); 157 candidate = candidate->nextInPreOrderAfterChildren(stayWithin);
159 break; 158 break;
160 case Constrain: 159 case Constrain:
161 stayWithin = candidate; 160 stayWithin = candidate;
162 // fall through 161 // fall through
163 case Continue: 162 case Continue:
164 candidate = candidate->nextInPreOrder(stayWithin); 163 candidate = candidate->nextInPreOrder(stayWithin);
165 break; 164 break;
166 case Return: 165 case Return:
167 return; 166 return;
168 } 167 }
169 } 168 }
170 } 169 }
171 170
172 void ScrollAnchor::save() 171 void ScrollAnchor::save()
173 { 172 {
174 if (m_scroller->scrollPosition() == IntPoint::zero()) { 173 if (m_scroller->scrollPosition() == IntPoint::zero()) {
175 clear(); 174 clear();
176 return; 175 return;
177 } 176 }
178 if (m_anchorObject) 177 if (m_current)
179 return; 178 return;
180 179
181 findAnchor(); 180 findAnchor();
182 if (!m_anchorObject) 181 if (!m_current)
183 return; 182 return;
184 183
185 m_anchorObject->setIsScrollAnchorObject(); 184 m_current.m_anchorObject->setIsScrollAnchorObject();
186 m_savedRelativeOffset = computeRelativeOffset(m_anchorObject, m_scroller, m_ corner); 185 m_current.m_savedRelativeOffset = computeRelativeOffset(
186 m_current.m_anchorObject, m_scroller, m_current.m_corner);
187
188 if (m_lastAdjusted) {
189 // We need to update m_lastAdjusted.m_savedRelativeOffset, since it is
190 // relative to the visible rect and the user may have scrolled since the
191 // last adjustment.
192 if (!candidateMovesWithScroller(m_lastAdjusted.m_anchorObject, m_scrolle r)) {
193 m_lastAdjusted.clear();
194 } else if (m_lastAdjusted.m_anchorObject == m_current.m_anchorObject
195 && m_lastAdjusted.m_corner == m_current.m_corner) {
196 m_lastAdjusted.m_savedRelativeOffset = m_current.m_savedRelativeOffs et;
197 } else {
198 m_lastAdjusted.m_savedRelativeOffset = computeRelativeOffset(
199 m_lastAdjusted.m_anchorObject, m_scroller, m_lastAdjusted.m_corn er);
200 }
201 }
187 } 202 }
188 203
189 void ScrollAnchor::restore() 204 IntSize ScrollAnchor::computeAdjustment(const AnchorPoint& anchorPoint) const
190 { 205 {
191 if (!m_anchorObject)
192 return;
193
194 // The anchor node can report fractional positions, but it is DIP-snapped wh en 206 // The anchor node can report fractional positions, but it is DIP-snapped wh en
195 // painting (crbug.com/610805), so we must round the offsets to determine th e 207 // painting (crbug.com/610805), so we must round the offsets to determine th e
196 // visual delta. If we scroll by the delta in LayoutUnits, the snapping of t he 208 // visual delta. If we scroll by the delta in LayoutUnits, the snapping of t he
197 // anchor node may round differently from the snapping of the scroll positio n. 209 // anchor node may round differently from the snapping of the scroll positio n.
198 // (For example, anchor moving from 2.4px -> 2.6px is really 2px -> 3px, so we 210 // (For example, anchor moving from 2.4px -> 2.6px is really 2px -> 3px, so we
199 // should scroll by 1px instead of 0.2px.) This is true regardless of whethe r 211 // should scroll by 1px instead of 0.2px.) This is true regardless of whethe r
200 // the ScrollableArea actually uses fractional scroll positions. 212 // the ScrollableArea actually uses fractional scroll positions.
201 IntSize adjustment = 213 return roundedIntSize(computeRelativeOffset(
202 roundedIntSize(computeRelativeOffset(m_anchorObject, m_scroller, m_corne r)) - 214 anchorPoint.m_anchorObject, m_scroller, anchorPoint.m_corner)) -
203 roundedIntSize(m_savedRelativeOffset); 215 roundedIntSize(anchorPoint.m_savedRelativeOffset);
204 if (!adjustment.isZero()) { 216 }
205 DoublePoint desiredPos = m_scroller->scrollPositionDouble() + adjustment ; 217
206 ScrollAnimatorBase* animator = m_scroller->existingScrollAnimator(); 218 void ScrollAnchor::restore()
207 if (!animator || !animator->hasRunningAnimation()) { 219 {
208 m_scroller->setScrollPosition(desiredPos, AnchoringScroll); 220 if (m_lastAdjusted && m_lastAdjusted.m_anchorObject != m_current.m_anchorObj ect
209 } else { 221 && !m_hasBounced && computeAdjustment(m_lastAdjusted) == -m_lastAdjustme nt) {
210 // If in the middle of a scroll animation, stop the animation, make 222 // If previous anchor point has bounced, follow the bounce.
211 // the adjustment, and continue the animation on the pending delta. 223 clear();
212 FloatSize pendingDelta = animator->desiredTargetPosition() - FloatPo int(m_scroller->scrollPositionDouble()); 224 adjust(-m_lastAdjustment);
213 animator->cancelAnimation(); 225 return;
214 m_scroller->setScrollPosition(desiredPos, AnchoringScroll);
215 animator->userScroll(ScrollByPixel, pendingDelta);
216 }
217 // Update UMA metric.
218 DEFINE_STATIC_LOCAL(EnumerationHistogram, adjustedOffsetHistogram,
219 ("Layout.ScrollAnchor.AdjustedScrollOffset", 2));
220 adjustedOffsetHistogram.count(1);
221 UseCounter::count(scrollerLayoutBox(m_scroller)->document(), UseCounter: :ScrollAnchored);
222 } 226 }
227 if (!m_current)
228 return;
229 IntSize adjustment = computeAdjustment(m_current);
230 if (adjustment.isZero())
231 return;
232 if (adjustment == -m_lastAdjustment && m_hasBounced) {
233 // Don't bounce more than once.
234 clear();
235 m_hasBounced = false;
236 m_lastAdjustment = IntSize();
237 m_lastAdjusted.clear();
238 return;
239 }
240 adjust(adjustment);
241 }
242
243 void ScrollAnchor::adjust(IntSize adjustment)
244 {
245 DoublePoint desiredPos = m_scroller->scrollPositionDouble() + adjustment;
246 ScrollAnimatorBase* animator = m_scroller->existingScrollAnimator();
247 if (!animator || !animator->hasRunningAnimation()) {
248 m_scroller->setScrollPosition(desiredPos, AnchoringScroll);
249 } else {
250 // If in the middle of a scroll animation, stop the animation, make
251 // the adjustment, and continue the animation on the pending delta.
252 // TODO(skobes): This is not quite right, we are starting a new curve wi thout
253 // saving our progress on the existing curve.
254 FloatSize pendingDelta = animator->desiredTargetPosition() -
255 FloatPoint(m_scroller->scrollPositionDouble());
256 animator->cancelAnimation();
257 m_scroller->setScrollPosition(desiredPos, AnchoringScroll);
258 animator->userScroll(ScrollByPixel, pendingDelta);
259 }
260
261 if (m_current && m_lastAdjusted.m_anchorObject != m_current.m_anchorObject) {
262 m_lastAdjusted.clear();
263 m_lastAdjusted = m_current;
264 }
265 m_hasBounced = (m_lastAdjustment == -adjustment);
266 m_lastAdjustment = adjustment;
267
268 // Update UMA metric.
269 DEFINE_STATIC_LOCAL(EnumerationHistogram, adjustedOffsetHistogram,
270 ("Layout.ScrollAnchor.AdjustedScrollOffset", 2));
271 adjustedOffsetHistogram.count(1);
272 UseCounter::count(scrollerLayoutBox(m_scroller)->document(), UseCounter::Scr ollAnchored);
223 } 273 }
224 274
225 void ScrollAnchor::clear() 275 void ScrollAnchor::clear()
226 { 276 {
277 m_current.clear();
278 }
279
280 void ScrollAnchor::AnchorPoint::clear()
281 {
227 LayoutObject* anchorObject = m_anchorObject; 282 LayoutObject* anchorObject = m_anchorObject;
228 m_anchorObject = nullptr; 283 m_anchorObject = nullptr;
229 284
230 if (anchorObject) 285 if (anchorObject)
231 anchorObject->maybeClearIsScrollAnchorObject(); 286 anchorObject->maybeClearIsScrollAnchorObject();
232 } 287 }
233 288
289 bool ScrollAnchor::refersTo(const LayoutObject* layoutObject) const
290 {
291 return m_current.m_anchorObject == layoutObject
292 || m_lastAdjusted.m_anchorObject == layoutObject;
293 }
294
295 void ScrollAnchor::notifyRemoved(LayoutObject* layoutObject)
296 {
297 if (m_current.m_anchorObject == layoutObject)
298 m_current.clear();
299 if (m_lastAdjusted.m_anchorObject == layoutObject)
300 m_lastAdjusted.clear();
301 }
302
234 } // namespace blink 303 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/layout/ScrollAnchor.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698