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

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: 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
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 the
190 // user may have scrolled since the last adjustment.
ymalik 2016/05/16 17:34:40 Could you please elaborate moe on why we update m_
skobes 2016/05/18 01:24:30 Clarified the comment. The issue is that the offs
191 if (!candidateMovesWithScroller(m_lastAdjusted.m_anchorObject, m_scrolle r)) {
192 m_lastAdjusted.clear();
193 } else if (m_lastAdjusted.m_anchorObject == m_current.m_anchorObject
194 && m_lastAdjusted.m_corner == m_current.m_corner) {
ymalik 2016/05/16 17:34:40 Should we override the == operator in AnchorPoint?
skobes 2016/05/18 01:24:30 I don't think operator== should ignore m_savedRela
195 m_lastAdjusted.m_savedRelativeOffset = m_current.m_savedRelativeOffs et;
196 } else {
197 m_lastAdjusted.m_savedRelativeOffset = computeRelativeOffset(
198 m_lastAdjusted.m_anchorObject, m_scroller, m_lastAdjusted.m_corn er);
199 }
200 }
187 } 201 }
188 202
189 void ScrollAnchor::restore() 203 IntSize ScrollAnchor::computeAdjustment(const AnchorPoint& anchorPoint) const
190 { 204 {
191 if (!m_anchorObject)
192 return;
193
194 // The anchor node can report fractional positions, but it is DIP-snapped wh en 205 // 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 206 // 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 207 // 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. 208 // 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 209 // (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 210 // should scroll by 1px instead of 0.2px.) This is true regardless of whethe r
200 // the ScrollableArea actually uses fractional scroll positions. 211 // the ScrollableArea actually uses fractional scroll positions.
201 IntSize adjustment = 212 return roundedIntSize(computeRelativeOffset(
202 roundedIntSize(computeRelativeOffset(m_anchorObject, m_scroller, m_corne r)) - 213 anchorPoint.m_anchorObject, m_scroller, anchorPoint.m_corner)) -
203 roundedIntSize(m_savedRelativeOffset); 214 roundedIntSize(anchorPoint.m_savedRelativeOffset);
204 if (!adjustment.isZero()) { 215 }
205 DoublePoint desiredPos = m_scroller->scrollPositionDouble() + adjustment ; 216
206 ScrollAnimatorBase* animator = m_scroller->existingScrollAnimator(); 217 void ScrollAnchor::restore()
207 if (!animator || !animator->hasRunningAnimation()) { 218 {
208 m_scroller->setScrollPosition(desiredPos, AnchoringScroll); 219 if (m_lastAdjusted && m_lastAdjusted.m_anchorObject != m_current.m_anchorObj ect
209 } else { 220 && !m_hasBounced && computeAdjustment(m_lastAdjusted) == -m_lastAdjustme nt) {
210 // If in the middle of a scroll animation, stop the animation, make 221 // If previous anchor point has bounced, follow the bounce.
211 // the adjustment, and continue the animation on the pending delta. 222 clear();
212 FloatSize pendingDelta = animator->desiredTargetPosition() - FloatPo int(m_scroller->scrollPositionDouble()); 223 adjust(-m_lastAdjustment);
213 animator->cancelAnimation(); 224 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 } 225 }
226 if (!m_current)
227 return;
228 IntSize adjustment = computeAdjustment(m_current);
229 if (adjustment.isZero())
230 return;
231 if (adjustment == -m_lastAdjustment && m_hasBounced) {
232 // Don't bounce more than once.
233 clear();
234 m_hasBounced = false;
235 m_lastAdjustment = IntSize();
236 m_lastAdjusted.clear();
237 return;
238 }
239 adjust(adjustment);
240 }
241
242 void ScrollAnchor::adjust(IntSize adjustment)
243 {
244 DoublePoint desiredPos = m_scroller->scrollPositionDouble() + adjustment;
245 ScrollAnimatorBase* animator = m_scroller->existingScrollAnimator();
246 if (!animator || !animator->hasRunningAnimation()) {
247 m_scroller->setScrollPosition(desiredPos, AnchoringScroll);
248 } else {
249 // If in the middle of a scroll animation, stop the animation, make
250 // the adjustment, and continue the animation on the pending delta.
251 // TODO(skobes): This is not quite right, we are starting a new curve wi thout
252 // saving our progress on the existing curve.
253 FloatSize pendingDelta = animator->desiredTargetPosition() -
254 FloatPoint(m_scroller->scrollPositionDouble());
255 animator->cancelAnimation();
256 m_scroller->setScrollPosition(desiredPos, AnchoringScroll);
257 animator->userScroll(ScrollByPixel, pendingDelta);
258 }
259
260 if (m_current && m_lastAdjusted.m_anchorObject != m_current.m_anchorObject) {
261 m_lastAdjusted.clear();
262 m_lastAdjusted = m_current;
263 }
264 m_hasBounced = (m_lastAdjustment == -adjustment);
265 m_lastAdjustment = adjustment;
266
267 // Update UMA metric.
268 DEFINE_STATIC_LOCAL(EnumerationHistogram, adjustedOffsetHistogram,
269 ("Layout.ScrollAnchor.AdjustedScrollOffset", 2));
270 adjustedOffsetHistogram.count(1);
271 UseCounter::count(scrollerLayoutBox(m_scroller)->document(), UseCounter::Scr ollAnchored);
223 } 272 }
224 273
225 void ScrollAnchor::clear() 274 void ScrollAnchor::clear()
226 { 275 {
276 m_current.clear();
277 }
278
279 void ScrollAnchor::AnchorPoint::clear()
280 {
227 LayoutObject* anchorObject = m_anchorObject; 281 LayoutObject* anchorObject = m_anchorObject;
228 m_anchorObject = nullptr; 282 m_anchorObject = nullptr;
229 283
230 if (anchorObject) 284 if (anchorObject)
231 anchorObject->maybeClearIsScrollAnchorObject(); 285 anchorObject->maybeClearIsScrollAnchorObject();
232 } 286 }
233 287
288 bool ScrollAnchor::refersTo(const LayoutObject* layoutObject) const
289 {
290 return m_current.m_anchorObject == layoutObject
291 || m_lastAdjusted.m_anchorObject == layoutObject;
292 }
293
294 void ScrollAnchor::notifyRemoved(LayoutObject* layoutObject)
295 {
296 if (m_current.m_anchorObject == layoutObject)
297 m_current.clear();
298 if (m_lastAdjusted.m_anchorObject == layoutObject)
299 m_lastAdjusted.clear();
300 }
301
234 } // namespace blink 302 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698