bool VisualViewport::magnifyScaleAroundAnchor(float magnifyDelta, const FloatPoint& anchor) { const float oldPageScale = scale(); const float newPageScale = frameHost().chromeClient().clampPageScaleFactorToLimits( magnifyDelta * oldPageScale); if (newPageScale == oldPageScale) return false; if (!mainFrame() || !mainFrame()->view()) return false; // Keep the center-of-pinch anchor in a stable position over the course // of the magnify. FloatPoint anchorAtOldScale = anchor.scaledBy(1.f / oldPageScale); FloatPoint anchorAtNewScale = anchor.scaledBy(1.f / newPageScale); FloatSize anchorDelta = anchorAtOldScale - anchorAtNewScale; // First try to use the anchor's delta to scroll the FrameView. FloatSize anchorDeltaUnusedByScroll = anchorDelta; if (!frameHost().settings().invertViewportScrollOrder()) { FrameView* view = mainFrame()->view(); DoublePoint oldPosition = view->scrollPositionDouble(); view->scrollBy(DoubleSize(anchorDelta.width(), anchorDelta.height()), UserScroll); DoublePoint newPosition = view->scrollPositionDouble(); anchorDeltaUnusedByScroll -= toFloatSize(newPosition - oldPosition); } // Manually bubble any remaining anchor delta up to the visual viewport. FloatPoint newLocation(location() + anchorDeltaUnusedByScroll); setScaleAndLocation(newPageScale, newLocation); return true; }
// Modifies the top of the graphics layer tree to add layers needed to support // the inner/outer viewport fixed-position model for pinch zoom. When finished, // the tree will look like this (with * denoting added layers): // // *rootTransformLayer // +- *innerViewportContainerLayer (fixed pos container) // | +- *overscrollElasticityLayer // | +- *pageScaleLayer // | +- *innerViewportScrollLayer // | +-- overflowControlsHostLayer (root layer) // | +-- outerViewportContainerLayer (fixed pos container) [frame container layer in PaintLayerCompositor] // | | +-- outerViewportScrollLayer [frame scroll layer in PaintLayerCompositor] // | | +-- content layers ... // +- horizontalScrollbarLayer // +- verticalScrollbarLayer // +- scroll corner (non-overlay only) // void VisualViewport::attachToLayerTree(GraphicsLayer* currentLayerTreeRoot, GraphicsLayerFactory* graphicsLayerFactory) { TRACE_EVENT1("blink", "VisualViewport::attachToLayerTree", "currentLayerTreeRoot", (bool)currentLayerTreeRoot); if (!currentLayerTreeRoot) { if (m_innerViewportScrollLayer) m_innerViewportScrollLayer->removeAllChildren(); return; } if (currentLayerTreeRoot->parent() && currentLayerTreeRoot->parent() == m_innerViewportScrollLayer) return; if (!m_innerViewportScrollLayer) { ASSERT(!m_overlayScrollbarHorizontal && !m_overlayScrollbarVertical && !m_overscrollElasticityLayer && !m_pageScaleLayer && !m_innerViewportContainerLayer); // FIXME: The root transform layer should only be created on demand. m_rootTransformLayer = GraphicsLayer::create(graphicsLayerFactory, this); m_innerViewportContainerLayer = GraphicsLayer::create(graphicsLayerFactory, this); m_overscrollElasticityLayer = GraphicsLayer::create(graphicsLayerFactory, this); m_pageScaleLayer = GraphicsLayer::create(graphicsLayerFactory, this); m_innerViewportScrollLayer = GraphicsLayer::create(graphicsLayerFactory, this); m_overlayScrollbarHorizontal = GraphicsLayer::create(graphicsLayerFactory, this); m_overlayScrollbarVertical = GraphicsLayer::create(graphicsLayerFactory, this); ScrollingCoordinator* coordinator = frameHost().page().scrollingCoordinator(); ASSERT(coordinator); coordinator->setLayerIsContainerForFixedPositionLayers(m_innerViewportScrollLayer.get(), true); // Set masks to bounds so the compositor doesn't clobber a manually // set inner viewport container layer size. m_innerViewportContainerLayer->setMasksToBounds(frameHost().settings().mainFrameClipsContent()); m_innerViewportContainerLayer->setSize(m_size); m_innerViewportScrollLayer->platformLayer()->setScrollClipLayer( m_innerViewportContainerLayer->platformLayer()); m_innerViewportScrollLayer->platformLayer()->setUserScrollable(true, true); m_rootTransformLayer->addChild(m_innerViewportContainerLayer.get()); m_innerViewportContainerLayer->addChild(m_overscrollElasticityLayer.get()); m_overscrollElasticityLayer->addChild(m_pageScaleLayer.get()); m_pageScaleLayer->addChild(m_innerViewportScrollLayer.get()); // Ensure this class is set as the scroll layer's ScrollableArea. coordinator->scrollableAreaScrollLayerDidChange(this); initializeScrollbars(); } m_innerViewportScrollLayer->removeAllChildren(); m_innerViewportScrollLayer->addChild(currentLayerTreeRoot); }
bool VisualViewport::magnifyScaleAroundAnchor(float magnifyDelta, const FloatPoint& anchor) { const float oldPageScale = scale(); const float newPageScale = frameHost().chromeClient().clampPageScaleFactorToLimits(magnifyDelta * oldPageScale); if (newPageScale == oldPageScale) return false; if (!mainFrame() || !mainFrame()->view()) return false; // Keep the center-of-pinch anchor in a stable position over the course // of the magnify. FloatPoint anchorAtOldScale = anchor.scaledBy(1.f / oldPageScale); FloatPoint anchorAtNewScale = anchor.scaledBy(1.f / newPageScale); FloatSize anchorDelta = anchorAtOldScale - anchorAtNewScale; // First try to use the anchor's delta to scroll the FrameView. FloatSize anchorDeltaUnusedByScroll = anchorDelta; // Manually bubble any remaining anchor delta up to the visual viewport. FloatPoint newLocation(FloatPoint(getScrollOffset()) + anchorDeltaUnusedByScroll); setScaleAndLocation(newPageScale, newLocation); return true; }
void Page::setPageScaleFactor(float scale, const IntPoint& origin) { if (!mainFrame()->isLocalFrame()) return; FrameView* view = deprecatedLocalMainFrame()->view(); PinchViewport& viewport = frameHost().pinchViewport(); if (scale != viewport.scale()) { viewport.setScale(scale); if (view && !settings().pinchVirtualViewportEnabled()) view->setVisibleContentScaleFactor(scale); deprecatedLocalMainFrame()->deviceOrPageScaleFactorChanged(); m_chrome->client().deviceOrPageScaleFactorChanged(); // FIXME: In virtual-viewport pinch mode, scale doesn't change the fixed-pos viewport; // remove once it's the only pinch mode in town. if (view) view->viewportConstrainedVisibleContentSizeChanged(true, true); deprecatedLocalMainFrame()->loader().saveScrollState(); } if (view && view->scrollPosition() != origin) view->notifyScrollPositionChanged(origin); }
bool VisualViewport::didSetScaleOrLocation(float scale, const FloatPoint& location) { if (!mainFrame()) return false; bool valuesChanged = false; if (scale != m_scale) { m_scale = scale; valuesChanged = true; frameHost().chromeClient().pageScaleFactorChanged(); enqueueResizeEvent(); } ScrollOffset clampedOffset = clampScrollOffset(toScrollOffset(location)); if (clampedOffset != m_offset) { m_offset = clampedOffset; scrollAnimator().setCurrentOffset(m_offset); // SVG runs with accelerated compositing disabled so no // ScrollingCoordinator. if (ScrollingCoordinator* coordinator = frameHost().page().scrollingCoordinator()) coordinator->scrollableAreaScrollLayerDidChange(this); if (!frameHost().settings().inertVisualViewport()) { if (Document* document = mainFrame()->document()) document->enqueueScrollEventForNode(document); } enqueueScrollEvent(); mainFrame()->view()->didChangeScrollOffset(); valuesChanged = true; } if (!valuesChanged) return false; InspectorInstrumentation::didUpdateLayout(mainFrame()); mainFrame()->loader().saveScrollState(); clampToBoundaries(); return true; }
void VisualViewport::setupScrollbar(WebScrollbar::Orientation orientation) { bool isHorizontal = orientation == WebScrollbar::Horizontal; GraphicsLayer* scrollbarGraphicsLayer = isHorizontal ? m_overlayScrollbarHorizontal.get() : m_overlayScrollbarVertical.get(); std::unique_ptr<WebScrollbarLayer>& webScrollbarLayer = isHorizontal ? m_webOverlayScrollbarHorizontal : m_webOverlayScrollbarVertical; ScrollbarThemeOverlay& theme = ScrollbarThemeOverlay::mobileTheme(); int thumbThickness = theme.thumbThickness(); int scrollbarThickness = theme.scrollbarThickness(RegularScrollbar); int scrollbarMargin = theme.scrollbarMargin(); if (!webScrollbarLayer) { ScrollingCoordinator* coordinator = frameHost().page().scrollingCoordinator(); ASSERT(coordinator); ScrollbarOrientation webcoreOrientation = isHorizontal ? HorizontalScrollbar : VerticalScrollbar; webScrollbarLayer = coordinator->createSolidColorScrollbarLayer( webcoreOrientation, thumbThickness, scrollbarMargin, false); // The compositor will control the scrollbar's visibility. Set to invisible // by default so scrollbars don't show up in layout tests. webScrollbarLayer->layer()->setOpacity(0); scrollbarGraphicsLayer->setContentsToPlatformLayer( webScrollbarLayer->layer()); scrollbarGraphicsLayer->setDrawsContent(false); } int xPosition = isHorizontal ? 0 : m_innerViewportContainerLayer->size().width() - scrollbarThickness; int yPosition = isHorizontal ? m_innerViewportContainerLayer->size().height() - scrollbarThickness : 0; int width = isHorizontal ? m_innerViewportContainerLayer->size().width() - scrollbarThickness : scrollbarThickness; int height = isHorizontal ? scrollbarThickness : m_innerViewportContainerLayer->size().height() - scrollbarThickness; // Use the GraphicsLayer to position the scrollbars. scrollbarGraphicsLayer->setPosition(IntPoint(xPosition, yPosition)); scrollbarGraphicsLayer->setSize(FloatSize(width, height)); scrollbarGraphicsLayer->setContentsRect(IntRect(0, 0, width, height)); }
void VisualViewport::registerLayersWithTreeView(WebLayerTreeView* layerTreeView) const { TRACE_EVENT0("blink", "VisualViewport::registerLayersWithTreeView"); ASSERT(layerTreeView); if (!mainFrame()) return; ASSERT(frameHost().page().deprecatedLocalMainFrame()->contentLayoutObject()); PaintLayerCompositor* compositor = frameHost().page().deprecatedLocalMainFrame()->contentLayoutObject()->compositor(); // Get the outer viewport scroll layer. WebLayer* scrollLayer = compositor->scrollLayer() ? compositor->scrollLayer()->platformLayer() : 0; m_webOverlayScrollbarHorizontal->setScrollLayer(scrollLayer); m_webOverlayScrollbarVertical->setScrollLayer(scrollLayer); ASSERT(compositor); layerTreeView->registerViewportLayers( m_overscrollElasticityLayer->platformLayer(), m_pageScaleLayer->platformLayer(), m_innerViewportScrollLayer->platformLayer(), scrollLayer); }
bool VisualViewport::shouldDisableDesktopWorkarounds() const { if (!mainFrame() || !mainFrame()->view()) return false; if (!mainFrame()->settings()->viewportEnabled()) return false; // A document is considered adapted to small screen UAs if one of these holds: // 1. The author specified viewport has a constrained width that is equal to // the initial viewport width. // 2. The author has disabled viewport zoom. const PageScaleConstraints& constraints = frameHost().pageScaleConstraintsSet().pageDefinedConstraints(); return mainFrame()->view()->layoutSize().width() == m_size.width() || (constraints.minimumScale == constraints.maximumScale && constraints.minimumScale != -1); }
void VisualViewport::initializeScrollbars() { // Do nothing if not attached to layer tree yet - will initialize upon attach. if (!m_innerViewportContainerLayer) return; if (visualViewportSuppliesScrollbars() && !frameHost().settings().hideScrollbars()) { if (!m_overlayScrollbarHorizontal->parent()) m_innerViewportContainerLayer->addChild( m_overlayScrollbarHorizontal.get()); if (!m_overlayScrollbarVertical->parent()) m_innerViewportContainerLayer->addChild(m_overlayScrollbarVertical.get()); } else { m_overlayScrollbarHorizontal->removeFromParent(); m_overlayScrollbarVertical->removeFromParent(); } setupScrollbar(WebScrollbar::Horizontal); setupScrollbar(WebScrollbar::Vertical); }
DoublePoint VisualViewport::maximumScrollPositionDouble() const { if (!mainFrame()) return IntPoint(); // FIXME: We probably shouldn't be storing the bounds in a float. crbug.com/422331. FloatSize frameViewSize(contentsSize()); if (m_topControlsAdjustment) { float minScale = frameHost().pageScaleConstraintsSet().finalConstraints().minimumScale; frameViewSize.expand(0, m_topControlsAdjustment / minScale); } frameViewSize.scale(m_scale); frameViewSize = flooredIntSize(frameViewSize); FloatSize viewportSize(m_size); viewportSize.expand(0, m_topControlsAdjustment); FloatSize maxPosition = frameViewSize - viewportSize; maxPosition.scale(1 / m_scale); return DoublePoint(maxPosition); }
ScrollOffset VisualViewport::maximumScrollOffset() const { if (!mainFrame()) return ScrollOffset(); // TODO(bokan): We probably shouldn't be storing the bounds in a float. // crbug.com/470718. FloatSize frameViewSize(contentsSize()); if (m_browserControlsAdjustment) { float minScale = frameHost().pageScaleConstraintsSet().finalConstraints().minimumScale; frameViewSize.expand(0, m_browserControlsAdjustment / minScale); } frameViewSize.scale(m_scale); frameViewSize = FloatSize(flooredIntSize(frameViewSize)); FloatSize viewportSize(m_size); viewportSize.expand(0, ceilf(m_browserControlsAdjustment)); FloatSize maxPosition = frameViewSize - viewportSize; maxPosition.scale(1 / m_scale); return ScrollOffset(maxPosition); }
float Page::pageScaleFactor() const { return frameHost().pinchViewport().scale(); }
LocalFrame* VisualViewport::mainFrame() const { return frameHost().page().mainFrame() && frameHost().page().mainFrame()->isLocalFrame() ? frameHost().page().deprecatedLocalMainFrame() : 0; }
HostWindow* VisualViewport::hostWindow() const { return &frameHost().chromeClient(); }
bool VisualViewport::visualViewportSuppliesScrollbars() const { return frameHost().settings().viewportMetaEnabled(); }
bool VisualViewport::scrollAnimatorEnabled() const { return frameHost().settings().scrollAnimatorEnabled(); }
CompositorAnimationTimeline* VisualViewport::compositorAnimationTimeline() const { ScrollingCoordinator* c = frameHost().page().scrollingCoordinator(); return c ? c->compositorAnimationTimeline() : nullptr; }