bool WheelScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) { TimeStamp now = AsyncPanZoomController::GetFrameTime(); CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom(); // If the animation is finished, make sure the final position is correct by // using one last displacement. Otherwise, compute the delta via the timing // function as normal. bool finished = IsFinished(now); nsPoint sampledDest = finished ? mDestination : PositionAt(now); ParentLayerPoint displacement = (CSSPoint::FromAppUnits(sampledDest) - aFrameMetrics.GetScrollOffset()) * zoom; // Note: we ignore overscroll for wheel animations. ParentLayerPoint adjustedOffset, overscroll; mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x); mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y, !aFrameMetrics.AllowVerticalScrollWithWheel()); // If we expected to scroll, but there's no more scroll range on either axis, // then end the animation early. Note that the initial displacement could be 0 // if the compositor ran very quickly (<1ms) after the animation was created. // When that happens we want to make sure the animation continues. if (!IsZero(displacement) && IsZero(adjustedOffset)) { // Nothing more to do - end the animation. return false; } aFrameMetrics.ScrollBy(adjustedOffset / zoom); return !finished; }
bool WheelScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) { TimeStamp now = AsyncPanZoomController::GetFrameTime(); CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom(); // If the animation is finished, make sure the final position is correct by // using one last displacement. Otherwise, compute the delta via the timing // function as normal. bool finished = IsFinished(now); nsPoint sampledDest = finished ? mDestination : PositionAt(now); ParentLayerPoint displacement = (CSSPoint::FromAppUnits(sampledDest) - aFrameMetrics.GetScrollOffset()) * zoom; // Note: we ignore overscroll for wheel animations. ParentLayerPoint adjustedOffset, overscroll; mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x); mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y, !aFrameMetrics.AllowVerticalScrollWithWheel()); if (IsZero(adjustedOffset)) { // Nothing more to do - end the animation. return false; } aFrameMetrics.ScrollBy(adjustedOffset / zoom); return !finished; }
static void SetDisplayPortMargins(nsIDOMWindowUtils* aUtils, nsIContent* aContent, FrameMetrics& aMetrics) { if (!aContent) { return; } nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent); if (!element) { return; } ScreenMargin margins = aMetrics.GetDisplayPortMargins(); aUtils->SetDisplayPortMarginsForElement(margins.left, margins.top, margins.right, margins.bottom, element, 0); CSSRect baseCSS = aMetrics.CalculateCompositedRectInCssPixels(); nsRect base(0, 0, baseCSS.width * nsPresContext::AppUnitsPerCSSPixel(), baseCSS.height * nsPresContext::AppUnitsPerCSSPixel()); nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent, base); }
void APZCCallbackHelper::UpdateRootFrame(nsIDOMWindowUtils* aUtils, FrameMetrics& aMetrics) { // Precondition checks MOZ_ASSERT(aUtils); if (aMetrics.mScrollId == FrameMetrics::NULL_SCROLL_ID) { return; } // Set the scroll port size, which determines the scroll range. For example if // a 500-pixel document is shown in a 100-pixel frame, the scroll port length would // be 100, and gecko would limit the maximum scroll offset to 400 (so as to prevent // overscroll). Note that if the content here was zoomed to 2x, the document would // be 1000 pixels long but the frame would still be 100 pixels, and so the maximum // scroll range would be 900. Therefore this calculation depends on the zoom applied // to the content relative to the container. CSSSize scrollPort = aMetrics.CalculateCompositedRectInCssPixels().Size(); aUtils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height); // Scroll the window to the desired spot nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.mScrollId); CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.mScrollOffset); // Correct the display port due to the difference between mScrollOffset and the // actual scroll offset, possibly align it to tile boundaries (if tiled layers are // enabled), and clamp it to the scrollable rect. MaybeAlignAndClampDisplayPort(aMetrics, actualScrollOffset); aMetrics.mScrollOffset = actualScrollOffset; // The mZoom variable on the frame metrics stores the CSS-to-screen scale for this // frame. This scale includes all of the (cumulative) resolutions set on the presShells // from the root down to this frame. However, when setting the resolution, we only // want the piece of the resolution that corresponds to this presShell, rather than // all of the cumulative stuff, so we need to divide out the parent resolutions. // Finally, we multiply by a ScreenToLayerScale of 1.0f because the goal here is to // take the async zoom calculated by the APZC and tell gecko about it (turning it into // a "sync" zoom) which will update the resolution at which the layer is painted. mozilla::layers::ParentLayerToLayerScale presShellResolution = aMetrics.mZoom / aMetrics.mDevPixelsPerCSSPixel / aMetrics.GetParentResolution() * ScreenToLayerScale(1.0f); aUtils->SetResolution(presShellResolution.scale, presShellResolution.scale); // Finally, we set the displayport. nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aMetrics.mScrollId); if (!content) { return; } nsCOMPtr<nsIDOMElement> element = do_QueryInterface(content); if (!element) { return; } aUtils->SetDisplayPortForElement(aMetrics.mDisplayPort.x, aMetrics.mDisplayPort.y, aMetrics.mDisplayPort.width, aMetrics.mDisplayPort.height, element); }
bool ClientLayerManager::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, FrameMetrics& aMetrics, bool aDrawingCritical) { #ifdef MOZ_WIDGET_ANDROID MOZ_ASSERT(aMetrics.IsScrollable()); // This is derived from the code in // gfx/layers/ipc/CompositorParent.cpp::TransformShadowTree. CSSToLayerScale paintScale = aMetrics.LayersPixelsPerCSSPixel(); const CSSRect& metricsDisplayPort = (aDrawingCritical && !aMetrics.mCriticalDisplayPort.IsEmpty()) ? aMetrics.mCriticalDisplayPort : aMetrics.mDisplayPort; LayerRect displayPort = (metricsDisplayPort + aMetrics.GetScrollOffset()) * paintScale; ScreenPoint scrollOffset; CSSToScreenScale zoom; bool ret = AndroidBridge::Bridge()->ProgressiveUpdateCallback( aHasPendingNewThebesContent, displayPort, paintScale.scale, aDrawingCritical, scrollOffset, zoom); aMetrics.SetScrollOffset(scrollOffset / zoom); aMetrics.SetZoom(zoom); return ret; #else return false; #endif }
FrameMetrics::ViewID CompositorChild::SharedFrameMetricsData::GetViewID() { FrameMetrics* frame = static_cast<FrameMetrics*>(mBuffer->memory()); MOZ_ASSERT(frame); // Not locking to read of mScrollId since it should not change after being // initially set. return frame->GetScrollId(); }
void APZCCallbackHelper::UpdateCallbackTransform(const FrameMetrics& aApzcMetrics, const FrameMetrics& aActualMetrics) { nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aApzcMetrics.GetScrollId()); if (!content) { return; } CSSPoint scrollDelta = aApzcMetrics.GetScrollOffset() - aActualMetrics.GetScrollOffset(); content->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(scrollDelta), nsINode::DeleteProperty<CSSPoint>); }
void ChromeProcessController::RequestContentRepaint(const FrameMetrics& aFrameMetrics) { MOZ_ASSERT(IsRepaintThread()); FrameMetrics metrics = aFrameMetrics; if (metrics.IsRootContent()) { APZCCallbackHelper::UpdateRootFrame(metrics); } else { APZCCallbackHelper::UpdateSubFrame(metrics); } }
// Recursively create a new array of scrollables, preserving any scrollables // that are still in the layer tree. // // aXScale and aYScale are used to calculate any values that need to be in // chrome-document CSS pixels and aren't part of the rendering loop, such as // the initial scroll offset for a new view. static void BuildViewMap(ViewMap& oldContentViews, ViewMap& newContentViews, nsFrameLoader* aFrameLoader, Layer* aLayer, float aXScale = 1, float aYScale = 1) { ContainerLayer* container = aLayer->AsContainerLayer(); if (!container) return; const FrameMetrics metrics = container->GetFrameMetrics(); const ViewID scrollId = metrics.mScrollId; const gfx3DMatrix transform = aLayer->GetTransform(); aXScale *= GetXScale(transform); aYScale *= GetYScale(transform); if (metrics.IsScrollable()) { nscoord auPerDevPixel = aFrameLoader->GetPrimaryFrameOfOwningContent() ->PresContext()->AppUnitsPerDevPixel(); nsContentView* view = FindViewForId(oldContentViews, scrollId); if (view) { // View already exists. Be sure to propagate scales for any values // that need to be calculated something in chrome-doc CSS pixels. ViewConfig config = view->GetViewConfig(); aXScale *= config.mXScale; aYScale *= config.mYScale; view->mFrameLoader = aFrameLoader; } else { // View doesn't exist, so generate one. We start the view scroll offset at // the same position as the framemetric's scroll offset from the layer. // The default scale is 1, so no need to propagate scale down. ViewConfig config; config.mScrollOffset = nsPoint( NSIntPixelsToAppUnits(metrics.mViewportScrollOffset.x, auPerDevPixel) * aXScale, NSIntPixelsToAppUnits(metrics.mViewportScrollOffset.y, auPerDevPixel) * aYScale); view = new nsContentView(aFrameLoader, scrollId, config); } view->mViewportSize = nsSize( NSIntPixelsToAppUnits(metrics.mViewport.width, auPerDevPixel) * aXScale, NSIntPixelsToAppUnits(metrics.mViewport.height, auPerDevPixel) * aYScale); view->mContentSize = nsSize( NSIntPixelsToAppUnits(metrics.mContentSize.width, auPerDevPixel) * aXScale, NSIntPixelsToAppUnits(metrics.mContentSize.height, auPerDevPixel) * aYScale); newContentViews[scrollId] = view; } for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { BuildViewMap(oldContentViews, newContentViews, aFrameLoader, child, aXScale, aYScale); } }
/** * Scroll the scroll frame associated with |aContent| to the scroll position * requested in |aMetrics|. * The scroll offset in |aMetrics| is updated to reflect the actual scroll * position. * The displayport stored in |aMetrics| and the callback-transform stored on * the content are updated to reflect any difference between the requested * and actual scroll positions. */ static void ScrollFrame(nsIContent* aContent, FrameMetrics& aMetrics) { // Scroll the window to the desired spot nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId()); if (sf) { sf->SetScrollableByAPZ(!aMetrics.IsScrollInfoLayer()); } bool scrollUpdated = false; CSSPoint apzScrollOffset = aMetrics.GetScrollOffset(); CSSPoint actualScrollOffset = ScrollFrameTo(sf, apzScrollOffset, scrollUpdated); if (scrollUpdated) { if (aMetrics.IsScrollInfoLayer()) { // In cases where the APZ scroll offset is different from the content scroll // offset, we want to interpret the margins as relative to the APZ scroll // offset except when the frame is not scrollable by APZ. Therefore, if the // layer is a scroll info layer, we leave the margins as-is and they will // be interpreted as relative to the content scroll offset. if (nsIFrame* frame = aContent->GetPrimaryFrame()) { frame->SchedulePaint(); } } else { // Correct the display port due to the difference between mScrollOffset and the // actual scroll offset. APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset); } } else { // For whatever reason we couldn't update the scroll offset on the scroll frame, // which means the data APZ used for its displayport calculation is stale. Fall // back to a sane default behaviour. Note that we don't tile-align the recentered // displayport because tile-alignment depends on the scroll position, and the // scroll position here is out of our control. See bug 966507 comment 21 for a // more detailed explanation. RecenterDisplayPort(aMetrics); } aMetrics.SetScrollOffset(actualScrollOffset); // APZ transforms inputs assuming we applied the exact scroll offset it // requested (|apzScrollOffset|). Since we may not have, record the difference // between what APZ asked for and what we actually applied, and apply it to // input events to compensate. if (aContent) { CSSPoint scrollDelta = apzScrollOffset - actualScrollOffset; aContent->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(scrollDelta), nsINode::DeleteProperty<CSSPoint>); } }
void APZCCallbackHelper::UpdateRootFrame(FrameMetrics& aMetrics) { if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) { return; } nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId()); if (!content) { return; } nsCOMPtr<nsIPresShell> shell = GetPresShell(content); if (!shell || aMetrics.GetPresShellId() != shell->GetPresShellId()) { return; } MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins()); if (gfxPrefs::APZAllowZooming()) { // If zooming is disabled then we don't really want to let APZ fiddle // with these things. In theory setting the resolution here should be a // no-op, but setting the SPCSPS is bad because it can cause a stale value // to be returned by window.innerWidth/innerHeight (see bug 1187792). float presShellResolution = nsLayoutUtils::GetResolution(shell); // If the pres shell resolution has changed on the content side side // the time this repaint request was fired, consider this request out of date // and drop it; setting a zoom based on the out-of-date resolution can have // the effect of getting us stuck with the stale resolution. if (presShellResolution != aMetrics.GetPresShellResolution()) { return; } // Set the scroll port size, which determines the scroll range. For example if // a 500-pixel document is shown in a 100-pixel frame, the scroll port length would // be 100, and gecko would limit the maximum scroll offset to 400 (so as to prevent // overscroll). Note that if the content here was zoomed to 2x, the document would // be 1000 pixels long but the frame would still be 100 pixels, and so the maximum // scroll range would be 900. Therefore this calculation depends on the zoom applied // to the content relative to the container. // Note that this needs to happen before scrolling the frame (in UpdateFrameCommon), // otherwise the scroll position may get clamped incorrectly. CSSSize scrollPort = aMetrics.CalculateCompositedSizeInCssPixels(); nsLayoutUtils::SetScrollPositionClampingScrollPortSize(shell, scrollPort); // The pres shell resolution is updated by the the async zoom since the // last paint. presShellResolution = aMetrics.GetPresShellResolution() * aMetrics.GetAsyncZoom().scale; nsLayoutUtils::SetResolutionAndScaleTo(shell, presShellResolution); } // Do this as late as possible since scrolling can flush layout. It also // adjusts the display port margins, so do it before we set those. ScrollFrame(content, aMetrics); SetDisplayPortMargins(shell, content, aMetrics); }
void ChromeProcessController::RequestContentRepaint(const FrameMetrics& aFrameMetrics) { MOZ_ASSERT(NS_IsMainThread()); if (aFrameMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) { return; } nsCOMPtr<nsIContent> targetContent = nsLayoutUtils::FindContentFor(aFrameMetrics.GetScrollId()); if (targetContent) { FrameMetrics metrics = aFrameMetrics; APZCCallbackHelper::UpdateSubFrame(targetContent, metrics); } }
bool GenericScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) { TimeStamp now = mApzc.GetFrameTime(); CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom(); // If the animation is finished, make sure the final position is correct by // using one last displacement. Otherwise, compute the delta via the timing // function as normal. bool finished = mAnimationPhysics->IsFinished(now); nsPoint sampledDest = mAnimationPhysics->PositionAt(now); ParentLayerPoint displacement = (CSSPoint::FromAppUnits(sampledDest) - aFrameMetrics.GetScrollOffset()) * zoom; if (finished) { mApzc.mX.SetVelocity(0); mApzc.mY.SetVelocity(0); } else if (!IsZero(displacement)) { // Convert velocity from AppUnits/Seconds to ParentLayerCoords/Milliseconds nsSize velocity = mAnimationPhysics->VelocityAt(now); ParentLayerPoint velocityPL = CSSPoint::FromAppUnits(nsPoint(velocity.width, velocity.height)) * zoom; mApzc.mX.SetVelocity(velocityPL.x / 1000.0); mApzc.mY.SetVelocity(velocityPL.y / 1000.0); } // Note: we ignore overscroll for generic animations. ParentLayerPoint adjustedOffset, overscroll; mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x, mDirectionForcedToOverscroll == Some(ScrollDirection::eHorizontal)); mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y, mDirectionForcedToOverscroll == Some(ScrollDirection::eVertical)); // If we expected to scroll, but there's no more scroll range on either axis, // then end the animation early. Note that the initial displacement could be 0 // if the compositor ran very quickly (<1ms) after the animation was created. // When that happens we want to make sure the animation continues. if (!IsZero(displacement) && IsZero(adjustedOffset)) { // Nothing more to do - end the animation. return false; } aFrameMetrics.ScrollBy(adjustedOffset / zoom); return !finished; }
TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) { // set up APZ apzc->SetFrameMetrics(GetPinchableFrameMetrics()); MakeApzcUnzoomable(); nsEventStatus statuses[3]; // scalebegin, scale, scaleend PinchWithPinchInput(apzc, 250, 350, 200, 300, 10, &statuses); FrameMetrics fm = apzc->GetFrameMetrics(); // It starts from (300, 300), then moves the focus point from (250, 350) to // (200, 300) pans by (50, 50) screen pixels, but there is a 2x zoom, which // causes the scroll offset to change by half of that (25, 25) pixels. EXPECT_EQ(325, fm.GetScrollOffset().x); EXPECT_EQ(325, fm.GetScrollOffset().y); EXPECT_EQ(2.0, fm.GetZoom().ToScaleFactor().scale); }
static void SetDisplayPortMargins(nsIPresShell* aPresShell, nsIContent* aContent, const FrameMetrics& aMetrics) { if (!aContent) { return; } ScreenMargin margins = aMetrics.GetDisplayPortMargins(); nsLayoutUtils::SetDisplayPortMargins(aContent, aPresShell, margins, 0); CSSRect baseCSS = aMetrics.CalculateCompositedRectInCssPixels(); nsRect base(0, 0, baseCSS.width * nsPresContext::AppUnitsPerCSSPixel(), baseCSS.height * nsPresContext::AppUnitsPerCSSPixel()); nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent, base); }
bool GenericScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) { TimeStamp now = mApzc.GetFrameTime(); CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom(); // If the animation is finished, make sure the final position is correct by // using one last displacement. Otherwise, compute the delta via the timing // function as normal. bool finished = IsFinished(now); nsPoint sampledDest = finished ? mDestination : PositionAt(now); ParentLayerPoint displacement = (CSSPoint::FromAppUnits(sampledDest) - aFrameMetrics.GetScrollOffset()) * zoom; if (finished) { mApzc.mX.SetVelocity(0); mApzc.mY.SetVelocity(0); } else if (!IsZero(displacement)) { // Velocity is measured in ParentLayerCoords / Milliseconds float xVelocity = displacement.x / aDelta.ToMilliseconds(); float yVelocity = displacement.y / aDelta.ToMilliseconds(); mApzc.mX.SetVelocity(xVelocity); mApzc.mY.SetVelocity(yVelocity); } // Note: we ignore overscroll for generic animations. ParentLayerPoint adjustedOffset, overscroll; mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x); mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y, mForceVerticalOverscroll); // If we expected to scroll, but there's no more scroll range on either axis, // then end the animation early. Note that the initial displacement could be 0 // if the compositor ran very quickly (<1ms) after the animation was created. // When that happens we want to make sure the animation continues. if (!IsZero(displacement) && IsZero(adjustedOffset)) { // Nothing more to do - end the animation. return false; } aFrameMetrics.ScrollBy(adjustedOffset / zoom); return !finished; }
bool APZCCallbackHelper::HasValidPresShellId(nsIDOMWindowUtils* aUtils, const FrameMetrics& aMetrics) { MOZ_ASSERT(aUtils); uint32_t presShellId; nsresult rv = aUtils->GetPresShellId(&presShellId); MOZ_ASSERT(NS_SUCCEEDED(rv)); return NS_SUCCEEDED(rv) && aMetrics.GetPresShellId() == presShellId; }
void APZCCallbackHelper::UpdateSubFrame(FrameMetrics& aMetrics) { if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) { return; } nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId()); if (!content) { return; } MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins()); // We don't currently support zooming for subframes, so nothing extra // needs to be done beyond the tasks common to this and UpdateRootFrame. ScrollFrame(content, aMetrics); if (nsCOMPtr<nsIPresShell> shell = GetPresShell(content)) { SetDisplayPortMargins(shell, content, aMetrics); } }
void APZCCallbackHelper::UpdateRootFrame(nsIDOMWindowUtils* aUtils, FrameMetrics& aMetrics) { // Precondition checks MOZ_ASSERT(aUtils); MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins()); if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) { return; } // Set the scroll port size, which determines the scroll range. For example if // a 500-pixel document is shown in a 100-pixel frame, the scroll port length would // be 100, and gecko would limit the maximum scroll offset to 400 (so as to prevent // overscroll). Note that if the content here was zoomed to 2x, the document would // be 1000 pixels long but the frame would still be 100 pixels, and so the maximum // scroll range would be 900. Therefore this calculation depends on the zoom applied // to the content relative to the container. // Note that this needs to happen before scrolling the frame (in UpdateFrameCommon), // otherwise the scroll position may get clamped incorrectly. CSSSize scrollPort = aMetrics.CalculateCompositedSizeInCssPixels(); aUtils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height); nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId()); ScrollFrame(content, aMetrics); // The pres shell resolution is updated by the the async zoom since the // last paint. float presShellResolution = aMetrics.GetPresShellResolution() * aMetrics.GetAsyncZoom().scale; aUtils->SetResolutionAndScaleTo(presShellResolution, presShellResolution); SetDisplayPortMargins(aUtils, content, aMetrics); }
bool ClientTiledPaintedLayer::IsScrollingOnCompositor(const FrameMetrics& aParentMetrics) { CompositorChild* compositor = nullptr; if (Manager() && Manager()->AsClientLayerManager()) { compositor = Manager()->AsClientLayerManager()->GetCompositorChild(); } if (!compositor) { return false; } FrameMetrics compositorMetrics; if (!compositor->LookupCompositorFrameMetrics(aParentMetrics.GetScrollId(), compositorMetrics)) { return false; } // 1 is a tad high for a fuzzy equals epsilon however if our scroll delta // is so small then we have nothing to gain from using paint heuristics. float COORDINATE_EPSILON = 1.f; return !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().x, aParentMetrics.GetScrollOffset().x, COORDINATE_EPSILON) || !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().y, aParentMetrics.GetScrollOffset().y, COORDINATE_EPSILON); }
void APZCCallbackHandler::RequestContentRepaint(const FrameMetrics& aFrameMetrics) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aFrameMetrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID); if (aFrameMetrics.GetIsRoot()) { nsIDOMWindowUtils* utils = GetDOMWindowUtils(); if (utils && APZCCallbackHelper::HasValidPresShellId(utils, aFrameMetrics)) { FrameMetrics metrics = aFrameMetrics; APZCCallbackHelper::UpdateRootFrame(utils, metrics); } } else { // aFrameMetrics.mIsRoot is false, so we are trying to update a subframe. // This requires special handling. nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aFrameMetrics.GetScrollId()); if (content) { FrameMetrics newSubFrameMetrics(aFrameMetrics); APZCCallbackHelper::UpdateSubFrame(content, newSubFrameMetrics); } } }
void APZCCallbackHelper::UpdateRootFrame(FrameMetrics& aMetrics) { if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) { return; } nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId()); if (!content) { return; } nsCOMPtr<nsIPresShell> shell = GetPresShell(content); if (!shell || aMetrics.GetPresShellId() != shell->GetPresShellId()) { return; } MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins()); if (gfxPrefs::APZAllowZooming()) { // If zooming is disabled then we don't really want to let APZ fiddle // with these things. In theory setting the resolution here should be a // no-op, but setting the SPCSPS is bad because it can cause a stale value // to be returned by window.innerWidth/innerHeight (see bug 1187792). float presShellResolution = shell->GetResolution(); // If the pres shell resolution has changed on the content side side // the time this repaint request was fired, consider this request out of date // and drop it; setting a zoom based on the out-of-date resolution can have // the effect of getting us stuck with the stale resolution. if (presShellResolution != aMetrics.GetPresShellResolution()) { return; } // The pres shell resolution is updated by the the async zoom since the // last paint. presShellResolution = aMetrics.GetPresShellResolution() * aMetrics.GetAsyncZoom().scale; shell->SetResolutionAndScaleTo(presShellResolution); } // Do this as late as possible since scrolling can flush layout. It also // adjusts the display port margins, so do it before we set those. ScrollFrame(content, aMetrics); MOZ_ASSERT(nsLayoutUtils::HasDisplayPort(content)); SetDisplayPortMargins(shell, content, aMetrics); SetPaintRequestTime(content, aMetrics.GetPaintRequestTime()); }
bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime, const FrameMetrics& aFrame, const gfx3DMatrix& aCurrentTransform, gfx3DMatrix* aNewTransform) { // The eventual return value of this function. The compositor needs to know // whether or not to advance by a frame as soon as it can. For example, if a // fling is happening, it has to keep compositing so that the animation is // smooth. If an animation frame is requested, it is the compositor's // responsibility to schedule a composite. bool requestAnimationFrame = false; // Scales on the root layer, on what's currently painted. float rootScaleX = aCurrentTransform.GetXScale(), rootScaleY = aCurrentTransform.GetYScale(); nsIntPoint metricsScrollOffset(0, 0); nsIntPoint scrollOffset; float localScaleX, localScaleY; { MonitorAutoLock mon(mMonitor); // If a fling is currently happening, apply it now. We can pull the updated // metrics afterwards. requestAnimationFrame = requestAnimationFrame || DoFling(aSampleTime - mLastSampleTime); // Current local transform; this is not what's painted but rather what PZC has // transformed due to touches like panning or pinching. Eventually, the root // layer transform will become this during runtime, but we must wait for Gecko // to repaint. localScaleX = mFrameMetrics.mResolution.width; localScaleY = mFrameMetrics.mResolution.height; if (aFrame.IsScrollable()) { metricsScrollOffset = aFrame.mViewportScrollOffset; } scrollOffset = mFrameMetrics.mViewportScrollOffset; } nsIntPoint scrollCompensation( (scrollOffset.x / rootScaleX - metricsScrollOffset.x) * localScaleX, (scrollOffset.y / rootScaleY - metricsScrollOffset.y) * localScaleY); ViewTransform treeTransform(-scrollCompensation, localScaleX, localScaleY); *aNewTransform = gfx3DMatrix(treeTransform) * aCurrentTransform; mLastSampleTime = aSampleTime; return requestAnimationFrame; }
// APZC sends us this request when we need to update the display port on // the scrollable frame the apzc is managing. void APZController::RequestContentRepaint(const FrameMetrics& aFrameMetrics) { #ifdef DEBUG_CONTROLLER WinUtils::Log("APZController::RequestContentRepaint scrollid=%I64d", aFrameMetrics.GetScrollId()); #endif // This must be on the gecko thread since we access the dom MOZ_ASSERT(NS_IsMainThread()); #ifdef DEBUG_CONTROLLER WinUtils::Log("APZController: mScrollOffset: %f %f", aFrameMetrics.mScrollOffset.x, aFrameMetrics.mScrollOffset.y); #endif nsCOMPtr<nsIDocument> subDocument; nsCOMPtr<nsIContent> targetContent; if (!GetDOMTargets(aFrameMetrics.GetScrollId(), subDocument, targetContent)) { return; } // If we're dealing with a sub frame or content editable element, // call UpdateSubFrame. if (targetContent) { #ifdef DEBUG_CONTROLLER WinUtils::Log("APZController: detected subframe or content editable"); #endif FrameMetrics metrics = aFrameMetrics; mozilla::layers::APZCCallbackHelper::UpdateSubFrame(targetContent, metrics); return; } #ifdef DEBUG_CONTROLLER WinUtils::Log("APZController: detected tab"); #endif // We're dealing with a tab, call UpdateRootFrame. nsCOMPtr<nsIDOMWindowUtils> utils; nsCOMPtr<nsIDOMWindow> window = subDocument->GetDefaultView(); if (window) { utils = do_GetInterface(window); if (utils) { FrameMetrics metrics = aFrameMetrics; mozilla::layers::APZCCallbackHelper::UpdateRootFrame(utils, metrics); #ifdef DEBUG_CONTROLLER WinUtils::Log("APZController: %I64d mDisplayPortMargins: %0.2f %0.2f %0.2f %0.2f", metrics.GetScrollId(), metrics.GetDisplayPortMargins().left, metrics.GetDisplayPortMargins().top, metrics.GetDisplayPortMargins().right, metrics.GetDisplayPortMargins().bottom); #endif } } }
static void PrintUniformityInfo(Layer* aLayer) { if(Layer::TYPE_CONTAINER != aLayer->GetType()) { return; } // Don't want to print a log for smaller layers if (aLayer->GetEffectiveVisibleRegion().GetBounds().width < 300 || aLayer->GetEffectiveVisibleRegion().GetBounds().height < 300) { return; } FrameMetrics frameMetrics = aLayer->AsContainerLayer()->GetFrameMetrics(); LayerIntPoint scrollOffset = RoundedToInt(frameMetrics.GetScrollOffsetInLayerPixels()); const gfx::Point layerTransform = GetScrollData(aLayer); gfx::Point layerScroll; layerScroll.x = scrollOffset.x - layerTransform.x; layerScroll.y = scrollOffset.y - layerTransform.y; printf_stderr("UniformityInfo Layer_Move %llu %p %f, %f\n", TimeStamp::Now(), aLayer, layerScroll.x, layerScroll.y); }
static void SetDisplayPortMargins(nsIPresShell* aPresShell, nsIContent* aContent, const FrameMetrics& aMetrics) { if (!aContent) { return; } bool hadDisplayPort = nsLayoutUtils::HasDisplayPort(aContent); ScreenMargin margins = aMetrics.GetDisplayPortMargins(); nsLayoutUtils::SetDisplayPortMargins(aContent, aPresShell, margins, 0); if (!hadDisplayPort) { nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors( aContent->GetPrimaryFrame(), nsLayoutUtils::RepaintMode::Repaint); } CSSRect baseCSS = aMetrics.CalculateCompositedRectInCssPixels(); nsRect base(0, 0, baseCSS.width * nsPresContext::AppUnitsPerCSSPixel(), baseCSS.height * nsPresContext::AppUnitsPerCSSPixel()); nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent, base); }
void APZCCallbackHelper::UpdateSubFrame(nsIContent* aContent, FrameMetrics& aMetrics) { // Precondition checks MOZ_ASSERT(aContent); MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins()); if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) { return; } nsCOMPtr<nsIDOMWindowUtils> utils = GetDOMWindowUtils(aContent); if (!utils) { return; } // We currently do not support zooming arbitrary subframes. They can only // be scrolled, so here we only have to set the scroll position and displayport. nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId()); bool scrollUpdated = false; CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.GetScrollOffset(), scrollUpdated); nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent); if (element) { if (scrollUpdated) { AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset); } else { RecenterDisplayPort(aMetrics); } gfx::IntSize alignment = gfxPrefs::LayersTilesEnabled() ? gfx::IntSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()) : gfx::IntSize(0, 0); LayerMargin margins = aMetrics.GetDisplayPortMargins(); utils->SetDisplayPortMarginsForElement(margins.left, margins.top, margins.right, margins.bottom, alignment.width, alignment.height, element, 0); CSSRect baseCSS = aMetrics.CalculateCompositedRectInCssPixels(); nsRect base(baseCSS.x * nsPresContext::AppUnitsPerCSSPixel(), baseCSS.y * nsPresContext::AppUnitsPerCSSPixel(), baseCSS.width * nsPresContext::AppUnitsPerCSSPixel(), baseCSS.height * nsPresContext::AppUnitsPerCSSPixel()); nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent, base); } aMetrics.SetScrollOffset(actualScrollOffset); }
void APZCCallbackHelper::UpdateSubFrame(nsIContent* aContent, FrameMetrics& aMetrics) { // Precondition checks MOZ_ASSERT(aContent); MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins()); // We don't currently support zooming for subframes, so nothing extra // needs to be done beyond the tasks common to this and UpdateRootFrame. ScrollFrame(aContent, aMetrics); if (nsCOMPtr<nsIPresShell> shell = GetPresShell(aContent)) { SetDisplayPortMargins(shell, aContent, aMetrics); } }
/** * Scroll the scroll frame associated with |aContent| to the scroll position * requested in |aMetrics|. * The scroll offset in |aMetrics| is updated to reflect the actual scroll * position. * The displayport stored in |aMetrics| and the callback-transform stored on * the content are updated to reflect any difference between the requested * and actual scroll positions. */ static void ScrollFrame(nsIContent* aContent, FrameMetrics& aMetrics) { // Scroll the window to the desired spot nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId()); bool scrollUpdated = false; CSSPoint apzScrollOffset = aMetrics.GetScrollOffset(); CSSPoint actualScrollOffset = ScrollFrameTo(sf, apzScrollOffset, scrollUpdated); if (scrollUpdated) { // Correct the display port due to the difference between mScrollOffset and the // actual scroll offset. AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset); } else { // For whatever reason we couldn't update the scroll offset on the scroll frame, // which means the data APZ used for its displayport calculation is stale. Fall // back to a sane default behaviour. Note that we don't tile-align the recentered // displayport because tile-alignment depends on the scroll position, and the // scroll position here is out of our control. See bug 966507 comment 21 for a // more detailed explanation. RecenterDisplayPort(aMetrics); } aMetrics.SetScrollOffset(actualScrollOffset); // APZ transforms inputs assuming we applied the exact scroll offset it // requested (|apzScrollOffset|). Since we may not have, record the difference // between what APZ asked for and what we actually applied, and apply it to // input events to compensate. if (aContent) { CSSPoint scrollDelta = apzScrollOffset - actualScrollOffset; aContent->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(scrollDelta), nsINode::DeleteProperty<CSSPoint>); } }
void AsyncPanZoomController::GetContentTransformForFrame(const FrameMetrics& aFrame, const gfx3DMatrix& aRootTransform, const gfxSize& aWidgetSize, gfx3DMatrix* aTreeTransform, gfxPoint* aReverseViewTranslation) { // Scales on the root layer, on what's currently painted. float rootScaleX = aRootTransform.GetXScale(), rootScaleY = aRootTransform.GetYScale(); // Current local transform; this is not what's painted but rather what PZC has // transformed due to touches like panning or pinching. Eventually, the root // layer transform will become this during runtime, but we must wait for Gecko // to repaint. float localScaleX = mFrameMetrics.mResolution.width, localScaleY = mFrameMetrics.mResolution.height; // Handle transformations for asynchronous panning and zooming. We determine the // zoom used by Gecko from the transformation set on the root layer, and we // determine the scroll offset used by Gecko from the frame metrics of the // primary scrollable layer. We compare this to the desired zoom and scroll // offset in the view transform we obtained from Java in order to compute the // transformation we need to apply. float tempScaleDiffX = rootScaleX * localScaleX; float tempScaleDiffY = rootScaleY * localScaleY; nsIntPoint metricsScrollOffset(0, 0); if (aFrame.IsScrollable()) metricsScrollOffset = aFrame.mViewportScrollOffset; nsIntPoint scrollCompensation( mFrameMetrics.mViewportScrollOffset.x / rootScaleX - metricsScrollOffset.x, mFrameMetrics.mViewportScrollOffset.y / rootScaleY - metricsScrollOffset.y); ViewTransform treeTransform(-scrollCompensation, localScaleX, localScaleY); *aTreeTransform = gfx3DMatrix(treeTransform); float offsetX = mFrameMetrics.mViewportScrollOffset.x / tempScaleDiffX, offsetY = mFrameMetrics.mViewportScrollOffset.y / tempScaleDiffY; nsIntRect localContentRect = mFrameMetrics.mContentRect; offsetX = NS_MAX((float)localContentRect.x, NS_MIN(offsetX, (float)(localContentRect.XMost() - aWidgetSize.width))); offsetY = NS_MAX((float)localContentRect.y, NS_MIN(offsetY, (float)(localContentRect.YMost() - aWidgetSize.height))); *aReverseViewTranslation = gfxPoint(offsetX - metricsScrollOffset.x, offsetY - metricsScrollOffset.y); }