OLD | NEW |
---|---|
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/LayoutBlockFlow.h" | 9 #include "core/layout/LayoutBlockFlow.h" |
10 #include "core/layout/api/LayoutBoxItem.h" | 10 #include "core/layout/api/LayoutBoxItem.h" |
11 #include "core/layout/line/InlineTextBox.h" | 11 #include "core/layout/line/InlineTextBox.h" |
12 #include "core/paint/PaintLayer.h" | 12 #include "core/paint/PaintLayer.h" |
13 #include "core/paint/PaintLayerScrollableArea.h" | 13 #include "core/paint/PaintLayerScrollableArea.h" |
14 #include "platform/Histogram.h" | 14 #include "platform/Histogram.h" |
15 | 15 |
16 namespace blink { | 16 namespace blink { |
17 | 17 |
18 using Corner = ScrollAnchor::Corner; | 18 using Corner = ScrollAnchor::Corner; |
19 | 19 |
20 ScrollAnchor::ScrollAnchor() | 20 ScrollAnchor::ScrollAnchor() |
21 : m_anchorObject(nullptr), | 21 : m_anchorObject(nullptr), |
22 m_corner(Corner::TopLeft), | 22 m_corner(Corner::TopLeft), |
23 m_scrollAnchorDisablingStyleChanged(false), | 23 m_scrollAnchorDisablingStyleChanged(false), |
24 m_saved(false) {} | 24 m_queued(false) {} |
25 | 25 |
26 ScrollAnchor::ScrollAnchor(ScrollableArea* scroller) : ScrollAnchor() { | 26 ScrollAnchor::ScrollAnchor(ScrollableArea* scroller) : ScrollAnchor() { |
27 setScroller(scroller); | 27 setScroller(scroller); |
28 } | 28 } |
29 | 29 |
30 ScrollAnchor::~ScrollAnchor() {} | 30 ScrollAnchor::~ScrollAnchor() {} |
31 | 31 |
32 void ScrollAnchor::setScroller(ScrollableArea* scroller) { | 32 void ScrollAnchor::setScroller(ScrollableArea* scroller) { |
33 DCHECK(m_scroller != scroller); | 33 DCHECK(m_scroller != scroller); |
34 DCHECK(scroller); | 34 DCHECK(scroller); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
100 } | 100 } |
101 LayoutBox* scrollerBox = scrollerLayoutBox(scroller); | 101 LayoutBox* scrollerBox = scrollerLayoutBox(scroller); |
102 | 102 |
103 LayoutRect relativeBounds = LayoutRect( | 103 LayoutRect relativeBounds = LayoutRect( |
104 layoutObject->localToAncestorQuad(FloatRect(localBounds), scrollerBox) | 104 layoutObject->localToAncestorQuad(FloatRect(localBounds), scrollerBox) |
105 .boundingBox()); | 105 .boundingBox()); |
106 // When root layer scrolling is off, the LayoutView will have no scroll | 106 // When root layer scrolling is off, the LayoutView will have no scroll |
107 // offset (since scrolling is handled by the FrameView) so | 107 // offset (since scrolling is handled by the FrameView) so |
108 // localToAncestorQuad returns document coords, so we must subtract scroll | 108 // localToAncestorQuad returns document coords, so we must subtract scroll |
109 // offset to get viewport coords. We discard the fractional part of the | 109 // offset to get viewport coords. We discard the fractional part of the |
110 // scroll offset so that the rounding in restore() matches the snapping of | 110 // scroll offset so that the rounding in adjust() matches the snapping of |
111 // the anchor node to the pixel grid of the layer it paints into. For | 111 // the anchor node to the pixel grid of the layer it paints into. For |
112 // non-FrameView scrollers, we rely on the flooring behavior of | 112 // non-FrameView scrollers, we rely on the flooring behavior of |
113 // LayoutBox::scrolledContentOffset. | 113 // LayoutBox::scrolledContentOffset. |
114 if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled() && | 114 if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled() && |
115 scrollerBox->isLayoutView()) | 115 scrollerBox->isLayoutView()) |
116 relativeBounds.moveBy(IntPoint(-scroller->scrollOffsetInt())); | 116 relativeBounds.moveBy(IntPoint(-scroller->scrollOffsetInt())); |
117 return relativeBounds; | 117 return relativeBounds; |
118 } | 118 } |
119 | 119 |
120 static LayoutPoint computeRelativeOffset(const LayoutObject* layoutObject, | 120 static LayoutPoint computeRelativeOffset(const LayoutObject* layoutObject, |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
205 while (true) { | 205 while (true) { |
206 DCHECK(current); | 206 DCHECK(current); |
207 if (current->scrollAnchorDisablingStyleChanged()) | 207 if (current->scrollAnchorDisablingStyleChanged()) |
208 return true; | 208 return true; |
209 if (current == scrollerBox) | 209 if (current == scrollerBox) |
210 return false; | 210 return false; |
211 current = current->parent(); | 211 current = current->parent(); |
212 } | 212 } |
213 } | 213 } |
214 | 214 |
215 void ScrollAnchor::save() { | 215 void ScrollAnchor::notifyBeforeLayout() { |
216 if (m_saved) | 216 if (m_queued) { |
217 m_scrollAnchorDisablingStyleChanged |= | |
218 computeScrollAnchorDisablingStyleChanged(); | |
217 return; | 219 return; |
218 m_saved = true; | 220 } |
219 DCHECK(m_scroller); | 221 DCHECK(m_scroller); |
220 ScrollOffset scrollOffset = m_scroller->scrollOffset(); | 222 ScrollOffset scrollOffset = m_scroller->scrollOffset(); |
221 float blockDirectionScrollOffset = | 223 float blockDirectionScrollOffset = |
222 scrollerLayoutBox(m_scroller)->isHorizontalWritingMode() | 224 scrollerLayoutBox(m_scroller)->isHorizontalWritingMode() |
223 ? scrollOffset.height() | 225 ? scrollOffset.height() |
224 : scrollOffset.width(); | 226 : scrollOffset.width(); |
225 if (blockDirectionScrollOffset == 0) { | 227 if (blockDirectionScrollOffset == 0) { |
226 clearSelf(); | 228 clearSelf(); |
227 return; | 229 return; |
228 } | 230 } |
229 | 231 |
230 if (!m_anchorObject) { | 232 if (!m_anchorObject) { |
231 findAnchor(); | 233 findAnchor(); |
232 if (!m_anchorObject) | 234 if (!m_anchorObject) |
233 return; | 235 return; |
234 | 236 |
235 m_anchorObject->setIsScrollAnchorObject(); | 237 m_anchorObject->setIsScrollAnchorObject(); |
236 m_savedRelativeOffset = | 238 m_savedRelativeOffset = |
237 computeRelativeOffset(m_anchorObject, m_scroller, m_corner); | 239 computeRelativeOffset(m_anchorObject, m_scroller, m_corner); |
238 } | 240 } |
239 | 241 |
240 // Note that we must compute this during save() since the scroller's | |
241 // descendants have finished layout (and had the bit cleared) by the | |
242 // time restore() is called. | |
243 m_scrollAnchorDisablingStyleChanged = | 242 m_scrollAnchorDisablingStyleChanged = |
244 computeScrollAnchorDisablingStyleChanged(); | 243 computeScrollAnchorDisablingStyleChanged(); |
244 | |
245 FrameView* frameView = scrollerLayoutBox(m_scroller)->frameView(); | |
246 ScrollableArea* owningScroller = | |
247 m_scroller->isRootFrameViewport() | |
248 ? &toRootFrameViewport(m_scroller)->layoutViewport() | |
249 : m_scroller.get(); | |
250 frameView->enqueueScrollAnchoringAdjustment(owningScroller); | |
ymalik
2016/10/13 13:05:39
I wonder if we should just enqueue m_scroller here
skobes
2016/10/13 17:21:37
I thought about this but it seemed a bit cleaner f
| |
251 m_queued = true; | |
245 } | 252 } |
246 | 253 |
247 IntSize ScrollAnchor::computeAdjustment() const { | 254 IntSize ScrollAnchor::computeAdjustment() const { |
248 // The anchor node can report fractional positions, but it is DIP-snapped when | 255 // The anchor node can report fractional positions, but it is DIP-snapped when |
249 // painting (crbug.com/610805), so we must round the offsets to determine the | 256 // painting (crbug.com/610805), so we must round the offsets to determine the |
250 // visual delta. If we scroll by the delta in LayoutUnits, the snapping of the | 257 // visual delta. If we scroll by the delta in LayoutUnits, the snapping of the |
251 // anchor node may round differently from the snapping of the scroll position. | 258 // anchor node may round differently from the snapping of the scroll position. |
252 // (For example, anchor moving from 2.4px -> 2.6px is really 2px -> 3px, so we | 259 // (For example, anchor moving from 2.4px -> 2.6px is really 2px -> 3px, so we |
253 // should scroll by 1px instead of 0.2px.) This is true regardless of whether | 260 // should scroll by 1px instead of 0.2px.) This is true regardless of whether |
254 // the ScrollableArea actually uses fractional scroll positions. | 261 // the ScrollableArea actually uses fractional scroll positions. |
255 IntSize delta = roundedIntSize(computeRelativeOffset(m_anchorObject, | 262 IntSize delta = roundedIntSize(computeRelativeOffset(m_anchorObject, |
256 m_scroller, m_corner)) - | 263 m_scroller, m_corner)) - |
257 roundedIntSize(m_savedRelativeOffset); | 264 roundedIntSize(m_savedRelativeOffset); |
258 | 265 |
259 // Only adjust on the block layout axis. | 266 // Only adjust on the block layout axis. |
260 if (scrollerLayoutBox(m_scroller)->isHorizontalWritingMode()) | 267 if (scrollerLayoutBox(m_scroller)->isHorizontalWritingMode()) |
261 delta.setWidth(0); | 268 delta.setWidth(0); |
262 else | 269 else |
263 delta.setHeight(0); | 270 delta.setHeight(0); |
264 return delta; | 271 return delta; |
265 } | 272 } |
266 | 273 |
267 void ScrollAnchor::restore() { | 274 void ScrollAnchor::adjust() { |
268 if (!m_saved) | 275 if (!m_queued) |
269 return; | 276 return; |
270 m_saved = false; | 277 m_queued = false; |
271 DCHECK(m_scroller); | 278 DCHECK(m_scroller); |
272 if (!m_anchorObject) | 279 if (!m_anchorObject) |
273 return; | 280 return; |
274 IntSize adjustment = computeAdjustment(); | 281 IntSize adjustment = computeAdjustment(); |
275 if (adjustment.isZero()) | 282 if (adjustment.isZero()) |
276 return; | 283 return; |
277 | 284 |
278 if (m_scrollAnchorDisablingStyleChanged) { | 285 if (m_scrollAnchorDisablingStyleChanged) { |
279 // Note that we only clear if the adjustment would have been non-zero. | 286 // Note that we only clear if the adjustment would have been non-zero. |
280 // This minimizes redundant calls to findAnchor. | 287 // This minimizes redundant calls to findAnchor. |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
334 bool ScrollAnchor::refersTo(const LayoutObject* layoutObject) const { | 341 bool ScrollAnchor::refersTo(const LayoutObject* layoutObject) const { |
335 return m_anchorObject == layoutObject; | 342 return m_anchorObject == layoutObject; |
336 } | 343 } |
337 | 344 |
338 void ScrollAnchor::notifyRemoved(LayoutObject* layoutObject) { | 345 void ScrollAnchor::notifyRemoved(LayoutObject* layoutObject) { |
339 if (m_anchorObject == layoutObject) | 346 if (m_anchorObject == layoutObject) |
340 clearSelf(); | 347 clearSelf(); |
341 } | 348 } |
342 | 349 |
343 } // namespace blink | 350 } // namespace blink |
OLD | NEW |