Chromium Code Reviews| 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 |