FloatPoint RotationViewportAnchor::getInnerOrigin( const FloatSize& innerSize) const { if (!m_anchorNode || !m_anchorNode->isConnected()) return m_visualViewportInDocument; const LayoutRect currentNodeBounds = m_anchorNode->boundingBox(); if (m_anchorNodeBounds == currentNodeBounds) return m_visualViewportInDocument; RootFrameViewport* rootFrameViewport = m_rootFrameView->getRootFrameViewport(); const LayoutRect currentNodeBoundsInLayoutViewport = rootFrameViewport->rootContentsToLayoutViewportContents( *m_rootFrameView.get(), currentNodeBounds); // Compute the new anchor point relative to the node position FloatSize anchorOffsetFromNode(currentNodeBoundsInLayoutViewport.size()); anchorOffsetFromNode.scale(m_anchorInNodeCoords.width(), m_anchorInNodeCoords.height()); FloatPoint anchorPoint = FloatPoint(currentNodeBoundsInLayoutViewport.location()) + anchorOffsetFromNode; // Compute the new origin point relative to the new anchor point FloatSize anchorOffsetFromOrigin = innerSize; anchorOffsetFromOrigin.scale(m_anchorInInnerViewCoords.width(), m_anchorInInnerViewCoords.height()); return anchorPoint - anchorOffsetFromOrigin; }
void RotationViewportAnchor::setAnchor() { RootFrameViewport* rootFrameViewport = m_rootFrameView->getRootFrameViewport(); DCHECK(rootFrameViewport); m_oldPageScaleFactor = m_visualViewport->scale(); m_oldMinimumPageScaleFactor = m_pageScaleConstraintsSet.finalConstraints().minimumScale; // Save the absolute location in case we won't find the anchor node, we'll // fall back to that. m_visualViewportInDocument = FloatPoint(rootFrameViewport->visibleContentRect().location()); m_anchorNode.clear(); m_anchorNodeBounds = LayoutRect(); m_anchorInNodeCoords = FloatSize(); m_normalizedVisualViewportOffset = FloatSize(); IntRect innerViewRect = rootFrameViewport->visibleContentRect(); // Preserve origins at the absolute screen origin. if (innerViewRect.location() == IntPoint::zero() || innerViewRect.isEmpty()) return; IntRect outerViewRect = layoutViewport().visibleContentRect(IncludeScrollbars); // Normalize by the size of the outer rect DCHECK(!outerViewRect.isEmpty()); m_normalizedVisualViewportOffset = m_visualViewport->getScrollOffset(); m_normalizedVisualViewportOffset.scale(1.0 / outerViewRect.width(), 1.0 / outerViewRect.height()); // Note, we specifically use the unscaled visual viewport size here as the // conversion into content-space below will apply the scale. FloatPoint anchorOffset(m_visualViewport->size()); anchorOffset.scale(m_anchorInInnerViewCoords.width(), m_anchorInInnerViewCoords.height()); // Note, we specifically convert to the rootFrameView contents here, rather // than the layout viewport. That's because hit testing works from the // FrameView's content coordinates even if it's not the layout viewport. const FloatPoint anchorPointInContents = m_rootFrameView->frameToContents( m_visualViewport->viewportToRootFrame(anchorOffset)); Node* node = findNonEmptyAnchorNode(flooredIntPoint(anchorPointInContents), innerViewRect, m_rootFrameView->frame().eventHandler()); if (!node) return; m_anchorNode = node; m_anchorNodeBounds = node->boundingBox(); m_anchorInNodeCoords = anchorPointInContents - FloatPoint(m_anchorNodeBounds.location()); m_anchorInNodeCoords.scale(1.f / m_anchorNodeBounds.width(), 1.f / m_anchorNodeBounds.height()); }
void VisualViewport::notifyRootFrameViewport() const { if (!mainFrame() || !mainFrame()->view()) return; RootFrameViewport* rootFrameViewport = mainFrame()->view()->getRootFrameViewport(); if (!rootFrameViewport) return; rootFrameViewport->didUpdateVisualViewport(); }
IntRect VisualViewport::visibleContentRect( IncludeScrollbarsInRect scrollbarInclusion) const { // TODO(ymalik): We're losing precision here and below. visibleRect should // be replaced with visibleContentRect. IntRect rect = IntRect(visibleRect()); if (scrollbarInclusion == ExcludeScrollbars) { RootFrameViewport* rootFrameViewport = mainFrame()->view()->getRootFrameViewport(); DCHECK(rootFrameViewport); rect.contract(rootFrameViewport->verticalScrollbarWidth() / m_scale, rootFrameViewport->horizontalScrollbarHeight() / m_scale); } return rect; }
// Tests that setting an alternate layout viewport scrolls the alternate // instead of the original. TEST_F(RootFrameViewportTest, SetAlternateLayoutViewport) { IntSize viewportSize(100, 100); RootFrameViewStub* layoutViewport = RootFrameViewStub::create(viewportSize, IntSize(200, 300)); VisualViewportStub* visualViewport = VisualViewportStub::create(viewportSize, viewportSize); RootFrameViewStub* alternateScroller = RootFrameViewStub::create(viewportSize, IntSize(600, 500)); RootFrameViewport* rootFrameViewport = RootFrameViewport::create(*visualViewport, *layoutViewport); visualViewport->setScale(2); rootFrameViewport->setScrollOffset(ScrollOffset(100, 100), UserScroll); EXPECT_SIZE_EQ(ScrollOffset(50, 50), visualViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(50, 50), layoutViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(100, 100), rootFrameViewport->getScrollOffset()); rootFrameViewport->setLayoutViewport(*alternateScroller); EXPECT_SIZE_EQ(ScrollOffset(50, 50), visualViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(0, 0), alternateScroller->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(50, 50), rootFrameViewport->getScrollOffset()); rootFrameViewport->setScrollOffset(ScrollOffset(200, 200), UserScroll); EXPECT_SIZE_EQ(ScrollOffset(50, 50), visualViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(150, 150), alternateScroller->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(200, 200), rootFrameViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(50, 50), layoutViewport->getScrollOffset()); EXPECT_SIZE_EQ(ScrollOffset(550, 450), rootFrameViewport->maximumScrollOffset()); }
ScrollableArea& RotationViewportAnchor::layoutViewport() const { RootFrameViewport* rootFrameViewport = m_rootFrameView->getRootFrameViewport(); DCHECK(rootFrameViewport); return rootFrameViewport->layoutViewport(); }
void RotationViewportAnchor::setAnchor() { RootFrameViewport* rootFrameViewport = m_rootFrameView->getRootFrameViewport(); DCHECK(rootFrameViewport); IntRect outerViewRect = layoutViewport().visibleContentRect(IncludeScrollbars); IntRect innerViewRect = rootFrameViewport->visibleContentRect(); m_oldPageScaleFactor = m_visualViewport->scale(); m_oldMinimumPageScaleFactor = m_pageScaleConstraintsSet.finalConstraints().minimumScale; // Save the absolute location in case we won't find the anchor node, we'll // fall back to that. m_visualViewportInDocument = FloatPoint(rootFrameViewport->visibleContentRect().location()); m_anchorNode.clear(); m_anchorNodeBounds = LayoutRect(); m_anchorInNodeCoords = FloatSize(); m_normalizedVisualViewportOffset = FloatSize(); if (innerViewRect.isEmpty()) return; // Preserve origins at the absolute screen origin if (innerViewRect.location() == IntPoint::zero()) return; // Inner rectangle should be within the outer one. DCHECK(outerViewRect.contains(innerViewRect)); // Outer rectangle is used as a scale, we need positive width and height. DCHECK(!outerViewRect.isEmpty()); m_normalizedVisualViewportOffset = FloatSize(innerViewRect.location() - outerViewRect.location()); // Normalize by the size of the outer rect m_normalizedVisualViewportOffset.scale(1.0 / outerViewRect.width(), 1.0 / outerViewRect.height()); FloatPoint anchorOffset(innerViewRect.size()); anchorOffset.scale(m_anchorInInnerViewCoords.width(), m_anchorInInnerViewCoords.height()); const FloatPoint anchorPointInContents = m_rootFrameView->rootFrameToContents( m_visualViewport->viewportToRootFrame(anchorOffset)); Node* node = findNonEmptyAnchorNode(flooredIntPoint(anchorPointInContents), innerViewRect, m_rootFrameView->frame().eventHandler()); if (!node) return; m_anchorNode = node; m_anchorNodeBounds = node->boundingBox(); m_anchorInNodeCoords = anchorPointInContents - FloatPoint(m_anchorNodeBounds.location()); m_anchorInNodeCoords.scale(1.f / m_anchorNodeBounds.width(), 1.f / m_anchorNodeBounds.height()); }