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); }
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 APZCCallbackHelper::UpdateRootFrame(nsIPresShell* aPresShell, FrameMetrics& aMetrics) { // Precondition checks MOZ_ASSERT(aPresShell); MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins()); if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) { return; } float presShellResolution = nsLayoutUtils::GetResolution(aPresShell); // 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(aPresShell, scrollPort); // The pres shell resolution is updated by the the async zoom since the // last paint. presShellResolution = aMetrics.GetPresShellResolution() * aMetrics.GetAsyncZoom().scale; nsLayoutUtils::SetResolutionAndScaleTo(aPresShell, 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. nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId()); ScrollFrame(content, aMetrics); SetDisplayPortMargins(aPresShell, content, aMetrics); }
CSSRect CalculateRectToZoomTo(const nsCOMPtr<nsIDocument>& aRootContentDocument, const CSSPoint& aPoint) { // Ensure the layout information we get is up-to-date. aRootContentDocument->FlushPendingNotifications(FlushType::Layout); // An empty rect as return value is interpreted as "zoom out". const CSSRect zoomOut; nsCOMPtr<nsIPresShell> shell = aRootContentDocument->GetShell(); if (!shell) { return zoomOut; } nsIScrollableFrame* rootScrollFrame = shell->GetRootScrollFrameAsScrollable(); if (!rootScrollFrame) { return zoomOut; } nsCOMPtr<dom::Element> element = ElementFromPoint(shell, aPoint); if (!element) { return zoomOut; } while (element && !ShouldZoomToElement(element)) { element = element->GetParentElement(); } if (!element) { return zoomOut; } FrameMetrics metrics = nsLayoutUtils::CalculateBasicFrameMetrics(rootScrollFrame); CSSRect compositedArea(metrics.GetScrollOffset(), metrics.CalculateCompositedSizeInCssPixels()); const CSSCoord margin = 15; CSSRect rect = nsLayoutUtils::GetBoundingContentRect(element, rootScrollFrame); // If the element is taller than the visible area of the page scale // the height of the |rect| so that it has the same aspect ratio as // the root frame. The clipped |rect| is centered on the y value of // the touch point. This allows tall narrow elements to be zoomed. if (!rect.IsEmpty() && compositedArea.width > 0.0f) { const float widthRatio = rect.width / compositedArea.width; float targetHeight = compositedArea.height * widthRatio; if (widthRatio < 0.9 && targetHeight < rect.height) { const CSSPoint scrollPoint = CSSPoint::FromAppUnits(rootScrollFrame->GetScrollPosition()); float newY = aPoint.y + scrollPoint.y - (targetHeight * 0.5f); if ((newY + targetHeight) > (rect.y + rect.height)) { rect.y += rect.height - targetHeight; } else if (newY > rect.y) { rect.y = newY; } rect.height = targetHeight; } } rect = CSSRect(std::max(metrics.GetScrollableRect().x, rect.x - margin), rect.y, rect.width + 2 * margin, rect.height); // Constrict the rect to the screen's right edge rect.width = std::min(rect.width, metrics.GetScrollableRect().XMost() - rect.x); // If the rect is already taking up most of the visible area and is // stretching the width of the page, then we want to zoom out instead. if (IsRectZoomedIn(rect, compositedArea)) { return zoomOut; } CSSRect rounded(rect); rounded.Round(); // If the block we're zooming to is really tall, and the user double-tapped // more than a screenful of height from the top of it, then adjust the // y-coordinate so that we center the actual point the user double-tapped // upon. This prevents flying to the top of the page when double-tapping // to zoom in (bug 761721). The 1.2 multiplier is just a little fuzz to // compensate for 'rect' including horizontal margins but not vertical ones. CSSCoord cssTapY = metrics.GetScrollOffset().y + aPoint.y; if ((rect.height > rounded.height) && (cssTapY > rounded.y + (rounded.height * 1.2))) { rounded.y = cssTapY - (rounded.height / 2); } return rounded; }
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. CSSSize scrollPort = aMetrics.CalculateCompositedSizeInCssPixels(); aUtils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height); // Scroll the window to the desired spot nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId()); bool scrollUpdated = false; CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.GetScrollOffset(), 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); // 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. ParentLayerToLayerScale presShellResolution = aMetrics.GetZoom() / aMetrics.mDevPixelsPerCSSPixel / aMetrics.GetParentResolution() * ScreenToLayerScale(1.0f); aUtils->SetResolution(presShellResolution.scale, presShellResolution.scale); // Finally, we set the displayport. nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId()); if (!content) { return; } nsCOMPtr<nsIDOMElement> element = do_QueryInterface(content); if (!element) { return; } gfx::IntSize alignment = gfxPrefs::LayersTilesEnabled() ? gfx::IntSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()) : gfx::IntSize(0, 0); LayerMargin margins = aMetrics.GetDisplayPortMargins(); aUtils->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(content, base); }
CSSRect CalculateRectToZoomTo(const nsCOMPtr<nsIDocument>& aRootContentDocument, const CSSPoint& aPoint) { // Ensure the layout information we get is up-to-date. aRootContentDocument->FlushPendingNotifications(Flush_Layout); // An empty rect as return value is interpreted as "zoom out". const CSSRect zoomOut; nsCOMPtr<nsIPresShell> shell = aRootContentDocument->GetShell(); if (!shell) { return zoomOut; } nsIScrollableFrame* rootScrollFrame = shell->GetRootScrollFrameAsScrollable(); if (!rootScrollFrame) { return zoomOut; } nsCOMPtr<dom::Element> element = ElementFromPoint(shell, aPoint); if (!element) { return zoomOut; } while (element && !ShouldZoomToElement(element)) { element = element->GetParentElement(); } if (!element) { return zoomOut; } FrameMetrics metrics = nsLayoutUtils::CalculateBasicFrameMetrics(rootScrollFrame); CSSRect compositedArea(metrics.GetScrollOffset(), metrics.CalculateCompositedSizeInCssPixels()); const CSSCoord margin = 15; CSSRect rect = GetBoundingContentRect(shell, element, rootScrollFrame); rect = CSSRect(std::max(metrics.GetScrollableRect().x, rect.x - margin), rect.y, rect.width + 2 * margin, rect.height); // Constrict the rect to the screen's right edge rect.width = std::min(rect.width, metrics.GetScrollableRect().XMost() - rect.x); // If the rect is already taking up most of the visible area and is // stretching the width of the page, then we want to zoom out instead. if (IsRectZoomedIn(rect, compositedArea)) { return zoomOut; } CSSRect rounded(rect); rounded.Round(); // If the block we're zooming to is really tall, and the user double-tapped // more than a screenful of height from the top of it, then adjust the // y-coordinate so that we center the actual point the user double-tapped // upon. This prevents flying to the top of the page when double-tapping // to zoom in (bug 761721). The 1.2 multiplier is just a little fuzz to // compensate for 'rect' including horizontal margins but not vertical ones. CSSCoord cssTapY = metrics.GetScrollOffset().y + aPoint.y; if ((rect.height > rounded.height) && (cssTapY > rounded.y + (rounded.height * 1.2))) { rounded.y = cssTapY - (rounded.height / 2); } return rounded; }
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. CSSSize scrollPort = aMetrics.CalculateCompositedSizeInCssPixels(); aUtils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height); // Scroll the window to the desired spot nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId()); bool scrollUpdated = false; CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.GetScrollOffset(), 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); // 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); // Finally, we set the displayport. nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId()); if (!content) { return; } nsCOMPtr<nsIDOMElement> element = do_QueryInterface(content); 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(content, base); }