void RootFrameViewport::setScrollOffset(const DoublePoint& offset, ScrollType scrollType) { // Make sure we use the scroll positions as reported by each viewport's ScrollAnimator, since its // ScrollableArea's position may have the fractional part truncated off. DoublePoint oldPosition = scrollOffsetFromScrollAnimators(); DoubleSize delta = offset - oldPosition; if (delta.isZero()) return; ScrollableArea& primary = !m_invertScrollOrder ? layoutViewport() : visualViewport(); ScrollableArea& secondary = !m_invertScrollOrder ? visualViewport() : layoutViewport(); DoublePoint targetPosition = primary.clampScrollPosition(primary.scrollAnimator()->currentPosition() + delta); primary.setScrollPosition(targetPosition, scrollType); DoubleSize applied = scrollOffsetFromScrollAnimators() - oldPosition; delta -= applied; if (delta.isZero()) return; targetPosition = secondary.clampScrollPosition(secondary.scrollAnimator()->currentPosition() + delta); secondary.setScrollPosition(targetPosition, scrollType); }
void RootFrameViewport::distributeScrollBetweenViewports(const DoublePoint& offset, ScrollType scrollType, ScrollBehavior behavior) { // Make sure we use the scroll positions as reported by each viewport's ScrollAnimatorBase, since its // ScrollableArea's position may have the fractional part truncated off. DoublePoint oldPosition = scrollOffsetFromScrollAnimators(); DoubleSize delta = offset - oldPosition; if (delta.isZero()) return; DoublePoint targetPosition = visualViewport().clampScrollPosition( visualViewport().scrollAnimator().currentPosition() + delta); visualViewport().setScrollPosition(targetPosition, scrollType, behavior); // Scroll the secondary viewport if all of the scroll was not applied to the // primary viewport. DoublePoint updatedPosition = layoutViewport().scrollAnimator().currentPosition() + FloatPoint(targetPosition); DoubleSize applied = updatedPosition - oldPosition; delta -= applied; if (delta.isZero()) return; targetPosition = layoutViewport().clampScrollPosition(layoutViewport().scrollAnimator().currentPosition() + delta); layoutViewport().setScrollPosition(targetPosition, scrollType, behavior); }
LayoutRect RootFrameViewport::scrollIntoView(const LayoutRect& rectInContent, const ScrollAlignment& alignX, const ScrollAlignment& alignY, ScrollType scrollType) { // We want to move the rect into the viewport that excludes the scrollbars so // we intersect the visual viewport with the scrollbar-excluded frameView // content rect. However, we don't use visibleContentRect directly since it // floors the scroll offset. Instead, we use ScrollAnimatorBase::currentOffset // and construct a LayoutRect from that. LayoutRect frameRectInContent = LayoutRect(FloatPoint(layoutViewport().scrollAnimator().currentOffset()), FloatSize(layoutViewport().visibleContentRect().size())); LayoutRect visualRectInContent = LayoutRect(FloatPoint(scrollOffsetFromScrollAnimators()), FloatSize(visualViewport().visibleContentRect().size())); // Intersect layout and visual rects to exclude the scrollbar from the view // rect. LayoutRect viewRectInContent = intersection(visualRectInContent, frameRectInContent); LayoutRect targetViewport = ScrollAlignment::getRectToExpose( viewRectInContent, rectInContent, alignX, alignY); if (targetViewport != viewRectInContent) { setScrollOffset(ScrollOffset(targetViewport.x(), targetViewport.y()), scrollType); } // RootFrameViewport only changes the viewport relative to the document so we // can't change the input rect's location relative to the document origin. return rectInContent; }
LayoutRect RootFrameViewport::scrollIntoView(const LayoutRect& rectInContent, const ScrollAlignment& alignX, const ScrollAlignment& alignY) { // We want to move the rect into the viewport that excludes the scrollbars so we intersect // the visual viewport with the scrollbar-excluded frameView content rect. However, we don't // use visibleContentRect directly since it floors the scroll position. Instead, we use // FrameView::scrollPositionDouble and construct a LayoutRect from that (the FrameView size // is always integer sized. LayoutRect frameRectInContent = LayoutRect( layoutViewport().scrollPositionDouble(), layoutViewport().visibleContentRect().size()); LayoutRect visualRectInContent = LayoutRect( layoutViewport().scrollPositionDouble() + toDoubleSize(visualViewport().scrollPositionDouble()), visualViewport().visibleContentRect().size()); LayoutRect viewRectInContent = intersection(visualRectInContent, frameRectInContent); LayoutRect targetViewport = ScrollAlignment::getRectToExpose(viewRectInContent, rectInContent, alignX, alignY); // visualViewport.scrollIntoView will attempt to center the given rect within the viewport // so to prevent it from adjusting r's coordinates the rect must match the viewport's size // i.e. add the subtracted scrollbars from above back in. // FIXME: This is hacky and required because getRectToExpose doesn't naturally account // for the two viewports. crbug.com/449340. targetViewport.setSize(LayoutSize(visualViewport().visibleContentRect().size())); // Snap the visible rect to layout units to match the calculated target viewport rect. FloatRect visible = LayoutRect(visualViewport().scrollPositionDouble(), visualViewport().visibleContentRect().size()); float centeringOffsetX = (visible.width() - targetViewport.width()) / 2; float centeringOffsetY = (visible.height() - targetViewport.height()) / 2; DoublePoint targetOffset( targetViewport.x() - centeringOffsetX, targetViewport.y() - centeringOffsetY); setScrollPosition(targetOffset, ProgrammaticScroll); // RootFrameViewport only changes the viewport relative to the document so we can't change the input // rect's location relative to the document origin. return rectInContent; }
void RootFrameViewport::distributeScrollBetweenViewports( const ScrollOffset& offset, ScrollType scrollType, ScrollBehavior behavior, ViewportToScrollFirst scrollFirst) { // Make sure we use the scroll offsets as reported by each viewport's // ScrollAnimatorBase, since its ScrollableArea's offset may have the // fractional part truncated off. // TODO(szager): Now that scroll offsets are stored as floats, can we take the // scroll offset directly from the ScrollableArea's rather than the animators? ScrollOffset oldOffset = scrollOffsetFromScrollAnimators(); ScrollOffset delta = offset - oldOffset; if (delta.isZero()) return; ScrollableArea& primary = scrollFirst == VisualViewport ? visualViewport() : layoutViewport(); ScrollableArea& secondary = scrollFirst == VisualViewport ? layoutViewport() : visualViewport(); ScrollOffset targetOffset = primary.clampScrollOffset( primary.scrollAnimator().currentOffset() + delta); primary.setScrollOffset(targetOffset, scrollType, behavior); // Scroll the secondary viewport if all of the scroll was not applied to the // primary viewport. ScrollOffset updatedOffset = secondary.scrollAnimator().currentOffset() + FloatSize(targetOffset); ScrollOffset applied = updatedOffset - oldOffset; delta -= applied; if (delta.isZero()) return; targetOffset = secondary.clampScrollOffset( secondary.scrollAnimator().currentOffset() + delta); secondary.setScrollOffset(targetOffset, scrollType, behavior); }
void RootFrameViewport::restoreToAnchor(const ScrollOffset& targetOffset) { // Clamp the scroll offset of each viewport now so that we force any invalid // offsets to become valid so we can compute the correct deltas. visualViewport().setScrollOffset(visualViewport().getScrollOffset(), ProgrammaticScroll); layoutViewport().setScrollOffset(layoutViewport().getScrollOffset(), ProgrammaticScroll); ScrollOffset delta = targetOffset - getScrollOffset(); visualViewport().setScrollOffset(visualViewport().getScrollOffset() + delta, ProgrammaticScroll); delta = targetOffset - getScrollOffset(); // Since the main thread FrameView has integer scroll offsets, scroll it to // the next pixel and then we'll scroll the visual viewport again to // compensate for the sub-pixel offset. We need this "overscroll" to ensure // the pixel of which we want to be partially in appears fully inside the // FrameView since the VisualViewport is bounded by the FrameView. IntSize layoutDelta = IntSize( delta.width() < 0 ? floor(delta.width()) : ceil(delta.width()), delta.height() < 0 ? floor(delta.height()) : ceil(delta.height())); layoutViewport().setScrollOffset( ScrollOffset(layoutViewport().scrollOffsetInt() + layoutDelta), ProgrammaticScroll); delta = targetOffset - getScrollOffset(); visualViewport().setScrollOffset(visualViewport().getScrollOffset() + delta, ProgrammaticScroll); }
ScrollResult RootFrameViewport::handleWheel(const PlatformWheelEvent& event) { updateScrollAnimator(); ScrollableArea& primary = !m_invertScrollOrder ? layoutViewport() : visualViewport(); ScrollableArea& secondary = !m_invertScrollOrder ? visualViewport() : layoutViewport(); ScrollResult viewScrollResult = primary.handleWheel(event); // The visual viewport will only accept pixel scrolls. if (!event.canScroll() || event.granularity() == ScrollByPageWheelEvent) return viewScrollResult; // TODO(sataya.m) : The delta in PlatformWheelEvent is negative when scrolling the // wheel towards the user, so negate it to get the scroll delta that should be applied // to the page. unusedScrollDelta computed in the ScrollResult is also negative. Say // there is WheelEvent({0, -10} and page scroll by 2px and unusedScrollDelta computed // is {0, -8}. Due to which we have to negate the unusedScrollDelta to obtain the expected // animation.Please address http://crbug.com/504389. DoublePoint oldOffset = secondary.scrollPositionDouble(); DoublePoint locationDelta; if (viewScrollResult.didScroll()) { locationDelta = -DoublePoint(viewScrollResult.unusedScrollDeltaX, viewScrollResult.unusedScrollDeltaY); } else { if (event.railsMode() != PlatformEvent::RailsModeVertical) locationDelta.setX(-event.deltaX()); if (event.railsMode() != PlatformEvent::RailsModeHorizontal) locationDelta.setY(-event.deltaY()); } DoublePoint targetPosition = secondary.clampScrollPosition( secondary.scrollPositionDouble() + toDoubleSize(locationDelta)); secondary.setScrollPosition(targetPosition, UserScroll); DoublePoint usedLocationDelta(secondary.scrollPositionDouble() - oldOffset); bool didScrollX = viewScrollResult.didScrollX || usedLocationDelta.x(); bool didScrollY = viewScrollResult.didScrollY || usedLocationDelta.y(); return ScrollResult(didScrollX, didScrollY, -viewScrollResult.unusedScrollDeltaX - usedLocationDelta.x(), -viewScrollResult.unusedScrollDeltaY - usedLocationDelta.y()); }
ScrollResultOneDimensional RootFrameViewport::userScroll(ScrollDirectionPhysical direction, ScrollGranularity granularity, float delta) { updateScrollAnimator(); ScrollbarOrientation orientation; if (direction == ScrollUp || direction == ScrollDown) orientation = VerticalScrollbar; else orientation = HorizontalScrollbar; if (layoutViewport().userInputScrollable(orientation) && visualViewport().userInputScrollable(orientation)) return ScrollableArea::userScroll(direction, granularity, delta); if (visualViewport().userInputScrollable(orientation)) return visualViewport().userScroll(direction, granularity, delta); if (layoutViewport().userInputScrollable(orientation)) return layoutViewport().userScroll(direction, granularity, delta); return ScrollResultOneDimensional(false, delta); }
void RootFrameViewport::updateCompositorScrollAnimations() { ScrollableArea::updateCompositorScrollAnimations(); layoutViewport().updateCompositorScrollAnimations(); visualViewport().updateCompositorScrollAnimations(); }
void RootFrameViewport::serviceScrollAnimations(double monotonicTime) { ScrollableArea::serviceScrollAnimations(monotonicTime); layoutViewport().serviceScrollAnimations(monotonicTime); visualViewport().serviceScrollAnimations(monotonicTime); }
ScrollResult RootFrameViewport::userScroll(ScrollGranularity granularity, const FloatSize& delta) { // TODO(bokan/ymalik): Once smooth scrolling is permanently enabled we // should be able to remove this method override and use the base class // version: ScrollableArea::userScroll. updateScrollAnimator(); // Distribute the scroll between the visual and layout viewport. float stepX = scrollStep(granularity, HorizontalScrollbar); float stepY = scrollStep(granularity, VerticalScrollbar); FloatSize pixelDelta(delta); pixelDelta.scale(stepX, stepY); // Precompute the amount of possible scrolling since, when animated, // ScrollAnimator::userScroll will report having consumed the total given // scroll delta, regardless of how much will actually scroll, but we need to // know how much to leave for the layout viewport. FloatSize visualConsumedDelta = visualViewport().scrollAnimator().computeDeltaToConsume(pixelDelta); // Split the remaining delta between scrollable and unscrollable axes of the // layout viewport. We only pass a delta to the scrollable axes and remember // how much was held back so we can add it to the unused delta in the // result. FloatSize layoutDelta = pixelDelta - visualConsumedDelta; FloatSize scrollableAxisDelta( layoutViewport().userInputScrollable(HorizontalScrollbar) ? layoutDelta.width() : 0, layoutViewport().userInputScrollable(VerticalScrollbar) ? layoutDelta.height() : 0); // If there won't be any scrolling, bail early so we don't produce any side // effects like cancelling existing animations. if (visualConsumedDelta.isZero() && scrollableAxisDelta.isZero()) { return ScrollResult(false, false, pixelDelta.width(), pixelDelta.height()); } cancelProgrammaticScrollAnimation(); // TODO(bokan): Why do we call userScroll on the animators directly and // not through the ScrollableAreas? ScrollResult visualResult = visualViewport().scrollAnimator().userScroll( granularity, visualConsumedDelta); if (visualConsumedDelta == pixelDelta) return visualResult; ScrollResult layoutResult = layoutViewport().scrollAnimator().userScroll( granularity, scrollableAxisDelta); // Remember to add any delta not used because of !userInputScrollable to the // unusedScrollDelta in the result. FloatSize unscrollableAxisDelta = layoutDelta - scrollableAxisDelta; return ScrollResult( visualResult.didScrollX || layoutResult.didScrollX, visualResult.didScrollY || layoutResult.didScrollY, layoutResult.unusedScrollDeltaX + unscrollableAxisDelta.width(), layoutResult.unusedScrollDeltaY + unscrollableAxisDelta.height()); }
bool RootFrameViewport::userInputScrollable( ScrollbarOrientation orientation) const { return visualViewport().userInputScrollable(orientation) || layoutViewport().userInputScrollable(orientation); }
ScrollOffset RootFrameViewport::maximumScrollOffset() const { return layoutViewport().maximumScrollOffset() + visualViewport().maximumScrollOffset(); }
IntSize RootFrameViewport::minimumScrollOffsetInt() const { return IntSize(layoutViewport().minimumScrollOffsetInt() + visualViewport().minimumScrollOffsetInt()); }
void RootFrameViewport::clearScrollableArea() { ScrollableArea::clearScrollableArea(); layoutViewport().clearScrollableArea(); visualViewport().clearScrollableArea(); }
DoublePoint RootFrameViewport::maximumScrollPositionDouble() const { return layoutViewport().maximumScrollPositionDouble() + toDoubleSize(visualViewport().maximumScrollPositionDouble()); }
IntRect RootFrameViewport::visibleContentRect( IncludeScrollbarsInRect scrollbarInclusion) const { return IntRect( IntPoint(scrollOffsetInt()), visualViewport().visibleContentRect(scrollbarInclusion).size()); }
ScrollOffset RootFrameViewport::scrollOffsetFromScrollAnimators() const { return visualViewport().scrollAnimator().currentOffset() + layoutViewport().scrollAnimator().currentOffset(); }
DoubleRect RootFrameViewport::visibleContentRectDouble(IncludeScrollbarsInRect scrollbarInclusion) const { return DoubleRect(scrollPositionDouble(), visualViewport().visibleContentRectDouble(scrollbarInclusion).size()); }
void RootFrameViewport::cancelProgrammaticScrollAnimation() { ScrollableArea::cancelProgrammaticScrollAnimation(); layoutViewport().cancelProgrammaticScrollAnimation(); visualViewport().cancelProgrammaticScrollAnimation(); }
IntPoint RootFrameViewport::maximumScrollPosition() const { return layoutViewport().maximumScrollPosition() + visualViewport().maximumScrollPosition(); }
Widget* RootFrameViewport::getWidget() { return visualViewport().getWidget(); }
DoublePoint RootFrameViewport::scrollOffsetFromScrollAnimators() const { return visualViewport().scrollAnimator()->currentPosition() + layoutViewport().scrollAnimator()->currentPosition(); }
ScrollOffset RootFrameViewport::getScrollOffset() const { return layoutViewport().getScrollOffset() + visualViewport().getScrollOffset(); }
IntPoint RootFrameViewport::minimumScrollPosition() const { return IntPoint(layoutViewport().minimumScrollPosition() - visualViewport().minimumScrollPosition()); }