summaryrefslogtreecommitdiffstats
path: root/layout/base/DisplayPortUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/base/DisplayPortUtils.cpp')
-rw-r--r--layout/base/DisplayPortUtils.cpp1145
1 files changed, 1145 insertions, 0 deletions
diff --git a/layout/base/DisplayPortUtils.cpp b/layout/base/DisplayPortUtils.cpp
new file mode 100644
index 0000000000..6ea541d065
--- /dev/null
+++ b/layout/base/DisplayPortUtils.cpp
@@ -0,0 +1,1145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DisplayPortUtils.h"
+
+#include "FrameMetrics.h"
+#include "Layers.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/APZPublicUtils.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/PAPZ.h"
+#include "mozilla/layers/RepaintRequest.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "nsDeckFrame.h"
+#include "nsIScrollableFrame.h"
+#include "nsLayoutUtils.h"
+#include "nsPlaceholderFrame.h"
+#include "nsSubDocumentFrame.h"
+#include "RetainedDisplayListBuilder.h"
+
+#include <ostream>
+
+namespace mozilla {
+
+using gfx::gfxVars;
+using gfx::IntSize;
+
+using layers::APZCCallbackHelper;
+using layers::FrameMetrics;
+using layers::LayerManager;
+using layers::RepaintRequest;
+using layers::ScrollableLayerGuid;
+
+typedef ScrollableLayerGuid::ViewID ViewID;
+
+static LazyLogModule sDisplayportLog("apz.displayport");
+
+/* static */
+DisplayPortMargins DisplayPortMargins::FromAPZ(
+ const ScreenMargin& aMargins, const CSSPoint& aVisualOffset,
+ const CSSPoint& aLayoutOffset, const CSSToScreenScale2D& aScale) {
+ return DisplayPortMargins{aMargins, aVisualOffset, aLayoutOffset, aScale};
+}
+
+CSSToScreenScale2D ComputeDisplayportScale(nsIScrollableFrame* aScrollFrame) {
+ // The calculation here is equivalent to
+ // CalculateBasicFrameMetrics(aScrollFrame).DisplayportPixelsPerCSSPixel(),
+ // but we don't calculate the entire frame metrics.
+ if (!aScrollFrame) {
+ return CSSToScreenScale2D(1.0, 1.0);
+ }
+ nsIFrame* frame = do_QueryFrame(aScrollFrame);
+ MOZ_ASSERT(frame);
+ nsPresContext* presContext = frame->PresContext();
+ PresShell* presShell = presContext->PresShell();
+ return presContext->CSSToDevPixelScale() *
+ LayoutDeviceToLayerScale2D(
+ presShell->GetCumulativeResolution() *
+ nsLayoutUtils::GetTransformToAncestorScale(frame)) *
+ LayerToScreenScale2D(1.0, 1.0);
+}
+
+/* static */
+DisplayPortMargins DisplayPortMargins::ForScrollFrame(
+ nsIScrollableFrame* aScrollFrame, const ScreenMargin& aMargins,
+ const Maybe<CSSToScreenScale2D>& aScale) {
+ CSSPoint visualOffset;
+ CSSPoint layoutOffset;
+ if (aScrollFrame) {
+ nsIFrame* scrollFrame = do_QueryFrame(aScrollFrame);
+ PresShell* presShell = scrollFrame->PresShell();
+ layoutOffset = CSSPoint::FromAppUnits(aScrollFrame->GetScrollPosition());
+ if (aScrollFrame->IsRootScrollFrameOfDocument() &&
+ presShell->IsVisualViewportOffsetSet()) {
+ visualOffset =
+ CSSPoint::FromAppUnits(presShell->GetVisualViewportOffset());
+
+ } else {
+ visualOffset = layoutOffset;
+ }
+ }
+ return DisplayPortMargins{aMargins, visualOffset, layoutOffset,
+ aScale.valueOrFrom([&] {
+ return ComputeDisplayportScale(aScrollFrame);
+ })};
+}
+
+/* static */
+DisplayPortMargins DisplayPortMargins::ForContent(
+ nsIContent* aContent, const ScreenMargin& aMargins) {
+ return ForScrollFrame(
+ aContent ? nsLayoutUtils::FindScrollableFrameFor(aContent) : nullptr,
+ aMargins, Nothing());
+}
+
+ScreenMargin DisplayPortMargins::GetRelativeToLayoutViewport(
+ ContentGeometryType aGeometryType,
+ nsIScrollableFrame* aScrollableFrame) const {
+ // APZ wants |mMargins| applied relative to the visual viewport.
+ // The main-thread painting code applies margins relative to
+ // the layout viewport. To get the main thread to paint the
+ // area APZ wants, apply a translation between the two. The
+ // magnitude of the translation depends on whether we are
+ // applying the displayport to scrolled or fixed content.
+ CSSPoint scrollDeltaCss =
+ ComputeAsyncTranslation(aGeometryType, aScrollableFrame);
+ ScreenPoint scrollDelta = scrollDeltaCss * mScale;
+ ScreenMargin margins = mMargins;
+ margins.left -= scrollDelta.x;
+ margins.right += scrollDelta.x;
+ margins.top -= scrollDelta.y;
+ margins.bottom += scrollDelta.y;
+ return margins;
+}
+
+std::ostream& operator<<(std::ostream& aOs,
+ const DisplayPortMargins& aMargins) {
+ if (aMargins.mVisualOffset == CSSPoint() &&
+ aMargins.mLayoutOffset == CSSPoint()) {
+ aOs << aMargins.mMargins;
+ } else {
+ aOs << "{" << aMargins.mMargins << "," << aMargins.mVisualOffset << ","
+ << aMargins.mLayoutOffset << "}";
+ }
+ return aOs;
+}
+
+CSSPoint DisplayPortMargins::ComputeAsyncTranslation(
+ ContentGeometryType aGeometryType,
+ nsIScrollableFrame* aScrollableFrame) const {
+ // If we are applying the displayport to scrolled content, the
+ // translation is the entire difference between the visual and
+ // layout offsets.
+ if (aGeometryType == ContentGeometryType::Scrolled) {
+ return mVisualOffset - mLayoutOffset;
+ }
+
+ // If we are applying the displayport to fixed content, only
+ // part of the difference between the visual and layout offsets
+ // should be applied. This is because fixed content remains fixed
+ // to the layout viewport, and some of the async delta between
+ // the visual and layout offsets can drag the layout viewport
+ // with it. We want only the remaining delta, i.e. the offset of
+ // the visual viewport relative to the (async-scrolled) layout
+ // viewport.
+ if (!aScrollableFrame) {
+ // Displayport on a non-scrolling frame for some reason.
+ // There will be no divergence between the two viewports.
+ return CSSPoint();
+ }
+ // Fixed content is always fixed to an RSF.
+ MOZ_ASSERT(aScrollableFrame->IsRootScrollFrameOfDocument());
+ nsIFrame* scrollFrame = do_QueryFrame(aScrollableFrame);
+ if (!scrollFrame->PresShell()->IsVisualViewportSizeSet()) {
+ // Zooming is disabled, so the layout viewport tracks the
+ // visual viewport completely.
+ return CSSPoint();
+ }
+ // Use KeepLayoutViewportEnclosingViewportVisual() to compute
+ // an async layout viewport the way APZ would.
+ const CSSRect visualViewport{
+ mVisualOffset,
+ // TODO: There are probably some edge cases here around async zooming
+ // that are not currently being handled properly. For proper handling,
+ // we'd likely need to save APZ's async zoom when populating
+ // mVisualOffset, and using it to adjust the visual viewport size here.
+ // Note that any incorrectness caused by this will only occur transiently
+ // during async zooming.
+ CSSSize::FromAppUnits(scrollFrame->PresShell()->GetVisualViewportSize())};
+ const CSSRect scrollableRect = CSSRect::FromAppUnits(
+ nsLayoutUtils::CalculateExpandedScrollableRect(scrollFrame));
+ CSSRect asyncLayoutViewport{
+ mLayoutOffset,
+ CSSSize::FromAppUnits(aScrollableFrame->GetScrollPortRect().Size())};
+ FrameMetrics::KeepLayoutViewportEnclosingVisualViewport(
+ visualViewport, scrollableRect, /* out */ asyncLayoutViewport);
+ return mVisualOffset - asyncLayoutViewport.TopLeft();
+}
+
+// Return the maximum displayport size, based on the LayerManager's maximum
+// supported texture size. The result is in app units.
+static nscoord GetMaxDisplayPortSize(nsIContent* aContent,
+ nsPresContext* aFallbackPrescontext) {
+ MOZ_ASSERT(!StaticPrefs::layers_enable_tiles_AtStartup(),
+ "Do not clamp displayports if tiling is enabled");
+
+ // Pick a safe maximum displayport size for sanity purposes. This is the
+ // lowest maximum texture size on tileless-platforms (Windows, D3D10).
+ // If the gfx.max-texture-size pref is set, further restrict the displayport
+ // size to fit within that, because the compositor won't upload stuff larger
+ // than this size.
+ nscoord safeMaximum = aFallbackPrescontext
+ ? aFallbackPrescontext->DevPixelsToAppUnits(
+ std::min(8192, gfxPlatform::MaxTextureSize()))
+ : nscoord_MAX;
+
+ nsIFrame* frame = aContent->GetPrimaryFrame();
+ if (!frame) {
+ return safeMaximum;
+ }
+ frame = nsLayoutUtils::GetDisplayRootFrame(frame);
+
+ nsIWidget* widget = frame->GetNearestWidget();
+ if (!widget) {
+ return safeMaximum;
+ }
+ LayerManager* lm = widget->GetLayerManager();
+ if (!lm) {
+ return safeMaximum;
+ }
+ nsPresContext* presContext = frame->PresContext();
+
+ int32_t maxSizeInDevPixels = lm->GetMaxTextureSize();
+ if (maxSizeInDevPixels < 0 || maxSizeInDevPixels == INT_MAX) {
+ return safeMaximum;
+ }
+ maxSizeInDevPixels =
+ std::min(maxSizeInDevPixels, gfxPlatform::MaxTextureSize());
+ return presContext->DevPixelsToAppUnits(maxSizeInDevPixels);
+}
+
+static nsRect ApplyRectMultiplier(nsRect aRect, float aMultiplier) {
+ if (aMultiplier == 1.0f) {
+ return aRect;
+ }
+ float newWidth = aRect.width * aMultiplier;
+ float newHeight = aRect.height * aMultiplier;
+ float newX = aRect.x - ((newWidth - aRect.width) / 2.0f);
+ float newY = aRect.y - ((newHeight - aRect.height) / 2.0f);
+ // Rounding doesn't matter too much here, do a round-in
+ return nsRect(ceil(newX), ceil(newY), floor(newWidth), floor(newHeight));
+}
+
+static nsRect GetDisplayPortFromRectData(nsIContent* aContent,
+ DisplayPortPropertyData* aRectData,
+ float aMultiplier) {
+ // In the case where the displayport is set as a rect, we assume it is
+ // already aligned and clamped as necessary. The burden to do that is
+ // on the setter of the displayport. In practice very few places set the
+ // displayport directly as a rect (mostly tests). We still do need to
+ // expand it by the multiplier though.
+ return ApplyRectMultiplier(aRectData->mRect, aMultiplier);
+}
+
+static nsRect GetDisplayPortFromMarginsData(
+ nsIContent* aContent, DisplayPortMarginsPropertyData* aMarginsData,
+ float aMultiplier, const DisplayPortOptions& aOptions) {
+ // In the case where the displayport is set via margins, we apply the margins
+ // to a base rect. Then we align the expanded rect based on the alignment
+ // requested, further expand the rect by the multiplier, and finally, clamp it
+ // to the size of the scrollable rect.
+
+ nsRect base;
+ if (nsRect* baseData = static_cast<nsRect*>(
+ aContent->GetProperty(nsGkAtoms::DisplayPortBase))) {
+ base = *baseData;
+ } else {
+ // In theory we shouldn't get here, but we do sometimes (see bug 1212136).
+ // Fall through for graceful handling.
+ }
+
+ nsIFrame* frame = nsLayoutUtils::GetScrollFrameFromContent(aContent);
+ if (!frame) {
+ // Turns out we can't really compute it. Oops. We still should return
+ // something sane. Note that since we can't clamp the rect without a
+ // frame, we don't apply the multiplier either as it can cause the result
+ // to leak outside the scrollable area.
+ NS_WARNING(
+ "Attempting to get a displayport from a content with no primary "
+ "frame!");
+ return base;
+ }
+
+ bool isRoot = false;
+ if (aContent->OwnerDoc()->GetRootElement() == aContent) {
+ isRoot = true;
+ }
+
+ nsIScrollableFrame* scrollableFrame = frame->GetScrollTargetFrame();
+ nsPoint scrollPos;
+ if (scrollableFrame) {
+ scrollPos = scrollableFrame->GetScrollPosition();
+ }
+
+ nsPresContext* presContext = frame->PresContext();
+ int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
+
+ LayoutDeviceToScreenScale2D res(
+ presContext->PresShell()->GetCumulativeResolution() *
+ nsLayoutUtils::GetTransformToAncestorScale(frame));
+
+ // Calculate the expanded scrollable rect, which we'll be clamping the
+ // displayport to.
+ nsRect expandedScrollableRect =
+ nsLayoutUtils::CalculateExpandedScrollableRect(frame);
+
+ // GetTransformToAncestorScale() can return 0. In this case, just return the
+ // base rect (clamped to the expanded scrollable rect), as other calculations
+ // would run into divisions by zero.
+ if (res == LayoutDeviceToScreenScale2D(0, 0)) {
+ // Make sure the displayport remains within the scrollable rect.
+ return base.MoveInsideAndClamp(expandedScrollableRect - scrollPos);
+ }
+
+ // First convert the base rect to screen pixels
+ LayoutDeviceToScreenScale2D parentRes = res;
+ if (isRoot) {
+ // the base rect for root scroll frames is specified in the parent document
+ // coordinate space, so it doesn't include the local resolution.
+ float localRes = presContext->PresShell()->GetResolution();
+ parentRes.xScale /= localRes;
+ parentRes.yScale /= localRes;
+ }
+ ScreenRect screenRect =
+ LayoutDeviceRect::FromAppUnits(base, auPerDevPixel) * parentRes;
+
+ // Note on the correctness of applying the alignment in Screen space:
+ // The correct space to apply the alignment in would be Layer space, but
+ // we don't necessarily know the scale to convert to Layer space at this
+ // point because Layout may not yet have chosen the resolution at which to
+ // render (it chooses that in FrameLayerBuilder, but this can be called
+ // during display list building). Therefore, we perform the alignment in
+ // Screen space, which basically assumes that Layout chose to render at
+ // screen resolution; since this is what Layout does most of the time,
+ // this is a good approximation. A proper solution would involve moving
+ // the choosing of the resolution to display-list building time.
+ ScreenSize alignment;
+
+ PresShell* presShell = presContext->PresShell();
+ MOZ_ASSERT(presShell);
+
+ bool useWebRender = gfxVars::UseWebRender();
+
+ ScreenMargin margins = aMarginsData->mMargins.GetRelativeToLayoutViewport(
+ aOptions.mGeometryType, scrollableFrame);
+
+ if (presShell->IsDisplayportSuppressed()) {
+ alignment = ScreenSize(1, 1);
+ } else if (useWebRender) {
+ // Moving the displayport is relatively expensive with WR so we use a larger
+ // alignment that causes the displayport to move less frequently. The
+ // alignment scales up with the size of the base rect so larger scrollframes
+ // use a larger alignment, but we clamp the alignment to a power of two
+ // between 128 and 1024 (inclusive).
+ // This naturally also increases the size of the displayport compared to
+ // always using a 128 alignment, so the displayport multipliers are also
+ // correspondingly smaller when WR is enabled to prevent the displayport
+ // from becoming too big.
+ IntSize multiplier =
+ layers::apz::GetDisplayportAlignmentMultiplier(screenRect.Size());
+ alignment = ScreenSize(128 * multiplier.width, 128 * multiplier.height);
+ } else if (StaticPrefs::layers_enable_tiles_AtStartup()) {
+ // Don't align to tiles if they are too large, because we could expand
+ // the displayport by a lot which can take more paint time. It's a tradeoff
+ // though because if we don't align to tiles we have more waste on upload.
+ IntSize tileSize = gfxVars::TileSize();
+ alignment = ScreenSize(std::min(256, tileSize.width),
+ std::min(256, tileSize.height));
+ } else {
+ // If we're not drawing with tiles then we need to be careful about not
+ // hitting the max texture size and we only need 1 draw call per layer
+ // so we can align to a smaller multiple.
+ alignment = ScreenSize(128, 128);
+ }
+
+ // Avoid division by zero.
+ if (alignment.width == 0) {
+ alignment.width = 128;
+ }
+ if (alignment.height == 0) {
+ alignment.height = 128;
+ }
+
+ if (StaticPrefs::layers_enable_tiles_AtStartup() || useWebRender) {
+ // Expand the rect by the margins
+ screenRect.Inflate(margins);
+ } else {
+ // Calculate the displayport to make sure we fit within the max texture size
+ // when not tiling.
+ nscoord maxSizeAppUnits = GetMaxDisplayPortSize(aContent, presContext);
+ MOZ_ASSERT(maxSizeAppUnits < nscoord_MAX);
+
+ // The alignment code can round up to 3 tiles, we want to make sure
+ // that the displayport can grow by up to 3 tiles without going
+ // over the max texture size.
+ const int MAX_ALIGN_ROUNDING = 3;
+
+ // Find the maximum size in screen pixels.
+ int32_t maxSizeDevPx = presContext->AppUnitsToDevPixels(maxSizeAppUnits);
+ int32_t maxWidthScreenPx = floor(double(maxSizeDevPx) * res.xScale) -
+ MAX_ALIGN_ROUNDING * alignment.width;
+ int32_t maxHeightScreenPx = floor(double(maxSizeDevPx) * res.yScale) -
+ MAX_ALIGN_ROUNDING * alignment.height;
+
+ // For each axis, inflate the margins up to the maximum size.
+ if (screenRect.height < maxHeightScreenPx) {
+ int32_t budget = maxHeightScreenPx - screenRect.height;
+ // Scale the margins down to fit into the budget if necessary, maintaining
+ // their relative ratio.
+ float scale = 1.0f;
+ if (float(budget) < margins.TopBottom()) {
+ scale = float(budget) / margins.TopBottom();
+ }
+ float top = margins.top * scale;
+ float bottom = margins.bottom * scale;
+ screenRect.y -= top;
+ screenRect.height += top + bottom;
+ }
+ if (screenRect.width < maxWidthScreenPx) {
+ int32_t budget = maxWidthScreenPx - screenRect.width;
+ float scale = 1.0f;
+ if (float(budget) < margins.LeftRight()) {
+ scale = float(budget) / margins.LeftRight();
+ }
+ float left = margins.left * scale;
+ float right = margins.right * scale;
+ screenRect.x -= left;
+ screenRect.width += left + right;
+ }
+ }
+
+ ScreenPoint scrollPosScreen =
+ LayoutDevicePoint::FromAppUnits(scrollPos, auPerDevPixel) * res;
+
+ // Align the display port.
+ screenRect += scrollPosScreen;
+ float x = alignment.width * floor(screenRect.x / alignment.width);
+ float y = alignment.height * floor(screenRect.y / alignment.height);
+ float w = alignment.width * ceil(screenRect.width / alignment.width + 1);
+ float h = alignment.height * ceil(screenRect.height / alignment.height + 1);
+ screenRect = ScreenRect(x, y, w, h);
+ screenRect -= scrollPosScreen;
+
+ // Convert the aligned rect back into app units.
+ nsRect result = LayoutDeviceRect::ToAppUnits(screenRect / res, auPerDevPixel);
+
+ // If we have non-zero margins, expand the displayport for the low-res buffer
+ // if that's what we're drawing. If we have zero margins, we want the
+ // displayport to reflect the scrollport.
+ if (margins != ScreenMargin()) {
+ result = ApplyRectMultiplier(result, aMultiplier);
+ }
+
+ // Make sure the displayport remains within the scrollable rect.
+ result = result.MoveInsideAndClamp(expandedScrollableRect - scrollPos);
+
+ return result;
+}
+
+static bool GetDisplayPortData(
+ nsIContent* aContent, DisplayPortPropertyData** aOutRectData,
+ DisplayPortMarginsPropertyData** aOutMarginsData) {
+ MOZ_ASSERT(aOutRectData && aOutMarginsData);
+
+ *aOutRectData = static_cast<DisplayPortPropertyData*>(
+ aContent->GetProperty(nsGkAtoms::DisplayPort));
+ *aOutMarginsData = static_cast<DisplayPortMarginsPropertyData*>(
+ aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
+
+ if (!*aOutRectData && !*aOutMarginsData) {
+ // This content element has no displayport data at all
+ return false;
+ }
+
+ if (*aOutRectData && *aOutMarginsData) {
+ // choose margins if equal priority
+ if ((*aOutRectData)->mPriority > (*aOutMarginsData)->mPriority) {
+ *aOutMarginsData = nullptr;
+ } else {
+ *aOutRectData = nullptr;
+ }
+ }
+
+ NS_ASSERTION((*aOutRectData == nullptr) != (*aOutMarginsData == nullptr),
+ "Only one of aOutRectData or aOutMarginsData should be set!");
+
+ return true;
+}
+
+static bool GetWasDisplayPortPainted(nsIContent* aContent) {
+ DisplayPortPropertyData* rectData = nullptr;
+ DisplayPortMarginsPropertyData* marginsData = nullptr;
+
+ if (!GetDisplayPortData(aContent, &rectData, &marginsData)) {
+ return false;
+ }
+
+ return rectData ? rectData->mPainted : marginsData->mPainted;
+}
+
+bool DisplayPortUtils::IsMissingDisplayPortBaseRect(nsIContent* aContent) {
+ DisplayPortPropertyData* rectData = nullptr;
+ DisplayPortMarginsPropertyData* marginsData = nullptr;
+
+ if (GetDisplayPortData(aContent, &rectData, &marginsData) && marginsData) {
+ return !aContent->GetProperty(nsGkAtoms::DisplayPortBase);
+ }
+
+ return false;
+}
+
+static void TranslateFromScrollPortToScrollFrame(nsIContent* aContent,
+ nsRect* aRect) {
+ MOZ_ASSERT(aRect);
+ if (nsIScrollableFrame* scrollableFrame =
+ nsLayoutUtils::FindScrollableFrameFor(aContent)) {
+ *aRect += scrollableFrame->GetScrollPortRect().TopLeft();
+ }
+}
+
+static bool GetDisplayPortImpl(nsIContent* aContent, nsRect* aResult,
+ float aMultiplier,
+ const DisplayPortOptions& aOptions) {
+ DisplayPortPropertyData* rectData = nullptr;
+ DisplayPortMarginsPropertyData* marginsData = nullptr;
+
+ if (!GetDisplayPortData(aContent, &rectData, &marginsData)) {
+ return false;
+ }
+
+ nsIFrame* frame = aContent->GetPrimaryFrame();
+ if (frame && !frame->PresShell()->AsyncPanZoomEnabled()) {
+ return false;
+ }
+
+ if (!aResult) {
+ // We have displayport data, but the caller doesn't want the actual
+ // rect, so we don't need to actually compute it.
+ return true;
+ }
+
+ bool isDisplayportSuppressed = false;
+
+ if (frame) {
+ nsPresContext* presContext = frame->PresContext();
+ MOZ_ASSERT(presContext);
+ PresShell* presShell = presContext->PresShell();
+ MOZ_ASSERT(presShell);
+ isDisplayportSuppressed = presShell->IsDisplayportSuppressed();
+ }
+
+ nsRect result;
+ if (rectData) {
+ result = GetDisplayPortFromRectData(aContent, rectData, aMultiplier);
+ } else if (isDisplayportSuppressed ||
+ nsLayoutUtils::ShouldDisableApzForElement(aContent)) {
+ // Make a copy of the margins data but set the margins to empty.
+ // Do not create a new DisplayPortMargins object with
+ // DisplayPortMargins::Empty(), because that will record the visual
+ // and layout scroll offsets in place right now on the DisplayPortMargins,
+ // and those are only meant to be recorded when the margins are stored.
+ DisplayPortMarginsPropertyData noMargins = *marginsData;
+ noMargins.mMargins.mMargins = ScreenMargin();
+ result = GetDisplayPortFromMarginsData(aContent, &noMargins, aMultiplier,
+ aOptions);
+ } else {
+ result = GetDisplayPortFromMarginsData(aContent, marginsData, aMultiplier,
+ aOptions);
+ }
+
+ if (!StaticPrefs::layers_enable_tiles_AtStartup()) {
+ // Perform the desired error handling if the displayport dimensions
+ // exceeds the maximum allowed size
+ nscoord maxSize = GetMaxDisplayPortSize(aContent, nullptr);
+ if (result.width > maxSize || result.height > maxSize) {
+ switch (aOptions.mMaxSizeExceededBehaviour) {
+ case MaxSizeExceededBehaviour::Assert:
+ NS_ASSERTION(false, "Displayport must be a valid texture size");
+ break;
+ case MaxSizeExceededBehaviour::Drop:
+ return false;
+ }
+ }
+ }
+
+ if (aOptions.mRelativeTo == DisplayportRelativeTo::ScrollFrame) {
+ TranslateFromScrollPortToScrollFrame(aContent, &result);
+ }
+
+ *aResult = result;
+ return true;
+}
+
+bool DisplayPortUtils::GetDisplayPort(nsIContent* aContent, nsRect* aResult,
+ const DisplayPortOptions& aOptions) {
+ float multiplier = StaticPrefs::layers_low_precision_buffer()
+ ? 1.0f / StaticPrefs::layers_low_precision_resolution()
+ : 1.0f;
+ return GetDisplayPortImpl(aContent, aResult, multiplier, aOptions);
+}
+
+bool DisplayPortUtils::HasDisplayPort(nsIContent* aContent) {
+ return GetDisplayPort(aContent, nullptr);
+}
+
+bool DisplayPortUtils::HasPaintedDisplayPort(nsIContent* aContent) {
+ DisplayPortPropertyData* rectData = nullptr;
+ DisplayPortMarginsPropertyData* marginsData = nullptr;
+ GetDisplayPortData(aContent, &rectData, &marginsData);
+ if (rectData) {
+ return rectData->mPainted;
+ }
+ if (marginsData) {
+ return marginsData->mPainted;
+ }
+ return false;
+}
+
+void DisplayPortUtils::MarkDisplayPortAsPainted(nsIContent* aContent) {
+ DisplayPortPropertyData* rectData = nullptr;
+ DisplayPortMarginsPropertyData* marginsData = nullptr;
+ GetDisplayPortData(aContent, &rectData, &marginsData);
+ MOZ_ASSERT(rectData || marginsData,
+ "MarkDisplayPortAsPainted should only be called for an element "
+ "with a displayport");
+ if (rectData) {
+ rectData->mPainted = true;
+ }
+ if (marginsData) {
+ marginsData->mPainted = true;
+ }
+}
+
+/* static */
+bool DisplayPortUtils::GetDisplayPortForVisibilityTesting(nsIContent* aContent,
+ nsRect* aResult) {
+ MOZ_ASSERT(aResult);
+ // Since the base rect might not have been updated very recently, it's
+ // possible to end up with an extra-large displayport at this point, if the
+ // zoom level is changed by a lot. Instead of using the default behaviour of
+ // asserting, we can just ignore the displayport if that happens, as this
+ // call site is best-effort.
+ return GetDisplayPortImpl(aContent, aResult, 1.0f,
+ DisplayPortOptions()
+ .With(MaxSizeExceededBehaviour::Drop)
+ .With(DisplayportRelativeTo::ScrollFrame));
+}
+
+void DisplayPortUtils::InvalidateForDisplayPortChange(
+ nsIContent* aContent, bool aHadDisplayPort, const nsRect& aOldDisplayPort,
+ const nsRect& aNewDisplayPort, RepaintMode aRepaintMode) {
+ if (aRepaintMode != RepaintMode::Repaint) {
+ return;
+ }
+
+ bool changed =
+ !aHadDisplayPort || !aOldDisplayPort.IsEqualEdges(aNewDisplayPort);
+
+ nsIFrame* frame = nsLayoutUtils::GetScrollFrameFromContent(aContent);
+ if (frame) {
+ frame = do_QueryFrame(frame->GetScrollTargetFrame());
+ }
+
+ if (changed && frame) {
+ // It is important to call SchedulePaint on the same frame that we set the
+ // dirty rect properties on so we can find the frame later to remove the
+ // properties.
+ frame->SchedulePaint();
+
+ if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() ||
+ !nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(frame)) {
+ return;
+ }
+
+ bool found;
+ nsRect* rect = frame->GetProperty(
+ nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), &found);
+
+ if (!found) {
+ rect = new nsRect();
+ frame->AddProperty(
+ nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), rect);
+ frame->SetHasOverrideDirtyRegion(true);
+
+ nsIFrame* rootFrame = frame->PresShell()->GetRootFrame();
+ MOZ_ASSERT(rootFrame);
+
+ RetainedDisplayListData* data =
+ GetOrSetRetainedDisplayListData(rootFrame);
+ data->Flags(frame) |= RetainedDisplayListData::FrameFlags::HasProps;
+ } else {
+ MOZ_ASSERT(rect, "this property should only store non-null values");
+ }
+
+ if (aHadDisplayPort) {
+ // We only need to build a display list for any new areas added
+ nsRegion newRegion(aNewDisplayPort);
+ newRegion.SubOut(aOldDisplayPort);
+ rect->UnionRect(*rect, newRegion.GetBounds());
+ } else {
+ rect->UnionRect(*rect, aNewDisplayPort);
+ }
+ }
+}
+
+bool DisplayPortUtils::SetDisplayPortMargins(nsIContent* aContent,
+ PresShell* aPresShell,
+ const DisplayPortMargins& aMargins,
+ uint32_t aPriority,
+ RepaintMode aRepaintMode) {
+ MOZ_ASSERT(aContent);
+ MOZ_ASSERT(aContent->GetComposedDoc() == aPresShell->GetDocument());
+
+ DisplayPortMarginsPropertyData* currentData =
+ static_cast<DisplayPortMarginsPropertyData*>(
+ aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
+ if (currentData && currentData->mPriority > aPriority) {
+ return false;
+ }
+
+ if (currentData && currentData->mMargins.mVisualOffset != CSSPoint() &&
+ aMargins.mVisualOffset == CSSPoint()) {
+ // If we hit this, then it's possible that we're setting a displayport
+ // that is wrong because the old one had a layout/visual adjustment and
+ // the new one does not.
+ MOZ_LOG(sDisplayportLog, LogLevel::Warning,
+ ("Dropping visual offset %s",
+ ToString(currentData->mMargins.mVisualOffset).c_str()));
+ }
+
+ nsIFrame* scrollFrame = nsLayoutUtils::GetScrollFrameFromContent(aContent);
+
+ nsRect oldDisplayPort;
+ bool hadDisplayPort = false;
+ bool wasPainted = GetWasDisplayPortPainted(aContent);
+ if (scrollFrame) {
+ // We only use the two return values from this function to call
+ // InvalidateForDisplayPortChange. InvalidateForDisplayPortChange does
+ // nothing if aContent does not have a frame. So getting the displayport is
+ // useless if the content has no frame, so we avoid calling this to avoid
+ // triggering a warning about not having a frame.
+ hadDisplayPort = GetHighResolutionDisplayPort(aContent, &oldDisplayPort);
+ }
+
+ aContent->SetProperty(
+ nsGkAtoms::DisplayPortMargins,
+ new DisplayPortMarginsPropertyData(aMargins, aPriority, wasPainted),
+ nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
+
+ nsIScrollableFrame* scrollableFrame =
+ scrollFrame ? scrollFrame->GetScrollTargetFrame() : nullptr;
+ if (!scrollableFrame) {
+ return true;
+ }
+
+ nsRect newDisplayPort;
+ DebugOnly<bool> hasDisplayPort =
+ GetHighResolutionDisplayPort(aContent, &newDisplayPort);
+ MOZ_ASSERT(hasDisplayPort);
+
+ if (MOZ_LOG_TEST(sDisplayportLog, LogLevel::Debug)) {
+ mozilla::layers::ScrollableLayerGuid::ViewID viewID =
+ mozilla::layers::ScrollableLayerGuid::NULL_SCROLL_ID;
+ nsLayoutUtils::FindIDFor(aContent, &viewID);
+ if (!hadDisplayPort) {
+ MOZ_LOG(sDisplayportLog, LogLevel::Debug,
+ ("SetDisplayPortMargins %s on scrollId=%" PRIu64 ", newDp=%s\n",
+ ToString(aMargins).c_str(), viewID,
+ ToString(newDisplayPort).c_str()));
+ } else {
+ // Use verbose level logging for when an existing displayport got its
+ // margins updated.
+ MOZ_LOG(sDisplayportLog, LogLevel::Verbose,
+ ("SetDisplayPortMargins %s on scrollId=%" PRIu64 ", newDp=%s\n",
+ ToString(aMargins).c_str(), viewID,
+ ToString(newDisplayPort).c_str()));
+ }
+ }
+
+ InvalidateForDisplayPortChange(aContent, hadDisplayPort, oldDisplayPort,
+ newDisplayPort, aRepaintMode);
+
+ scrollableFrame->TriggerDisplayPortExpiration();
+
+ // Display port margins changing means that the set of visible frames may
+ // have drastically changed. Check if we should schedule an update.
+ hadDisplayPort =
+ scrollableFrame->GetDisplayPortAtLastApproximateFrameVisibilityUpdate(
+ &oldDisplayPort);
+
+ bool needVisibilityUpdate = !hadDisplayPort;
+ // Check if the total size has changed by a large factor.
+ if (!needVisibilityUpdate) {
+ if ((newDisplayPort.width > 2 * oldDisplayPort.width) ||
+ (oldDisplayPort.width > 2 * newDisplayPort.width) ||
+ (newDisplayPort.height > 2 * oldDisplayPort.height) ||
+ (oldDisplayPort.height > 2 * newDisplayPort.height)) {
+ needVisibilityUpdate = true;
+ }
+ }
+ // Check if it's moved by a significant amount.
+ if (!needVisibilityUpdate) {
+ if (nsRect* baseData = static_cast<nsRect*>(
+ aContent->GetProperty(nsGkAtoms::DisplayPortBase))) {
+ nsRect base = *baseData;
+ if ((std::abs(newDisplayPort.X() - oldDisplayPort.X()) > base.width) ||
+ (std::abs(newDisplayPort.XMost() - oldDisplayPort.XMost()) >
+ base.width) ||
+ (std::abs(newDisplayPort.Y() - oldDisplayPort.Y()) > base.height) ||
+ (std::abs(newDisplayPort.YMost() - oldDisplayPort.YMost()) >
+ base.height)) {
+ needVisibilityUpdate = true;
+ }
+ }
+ }
+ if (needVisibilityUpdate) {
+ aPresShell->ScheduleApproximateFrameVisibilityUpdateNow();
+ }
+
+ return true;
+}
+
+void DisplayPortUtils::SetDisplayPortBase(nsIContent* aContent,
+ const nsRect& aBase) {
+ if (MOZ_LOG_TEST(sDisplayportLog, LogLevel::Verbose)) {
+ ViewID viewId = nsLayoutUtils::FindOrCreateIDFor(aContent);
+ MOZ_LOG(sDisplayportLog, LogLevel::Verbose,
+ ("Setting base rect %s for scrollId=%" PRIu64 "\n",
+ ToString(aBase).c_str(), viewId));
+ }
+ aContent->SetProperty(nsGkAtoms::DisplayPortBase, new nsRect(aBase),
+ nsINode::DeleteProperty<nsRect>);
+}
+
+void DisplayPortUtils::SetDisplayPortBaseIfNotSet(nsIContent* aContent,
+ const nsRect& aBase) {
+ if (!aContent->GetProperty(nsGkAtoms::DisplayPortBase)) {
+ SetDisplayPortBase(aContent, aBase);
+ }
+}
+
+bool DisplayPortUtils::GetCriticalDisplayPort(
+ nsIContent* aContent, nsRect* aResult, const DisplayPortOptions& aOptions) {
+ if (StaticPrefs::layers_low_precision_buffer()) {
+ return GetDisplayPortImpl(aContent, aResult, 1.0f, aOptions);
+ }
+ return false;
+}
+
+bool DisplayPortUtils::HasCriticalDisplayPort(nsIContent* aContent) {
+ return GetCriticalDisplayPort(aContent, nullptr);
+}
+
+bool DisplayPortUtils::GetHighResolutionDisplayPort(
+ nsIContent* aContent, nsRect* aResult, const DisplayPortOptions& aOptions) {
+ if (StaticPrefs::layers_low_precision_buffer()) {
+ return GetCriticalDisplayPort(aContent, aResult, aOptions);
+ }
+ return GetDisplayPort(aContent, aResult, aOptions);
+}
+
+void DisplayPortUtils::RemoveDisplayPort(nsIContent* aContent) {
+ aContent->RemoveProperty(nsGkAtoms::DisplayPort);
+ aContent->RemoveProperty(nsGkAtoms::DisplayPortMargins);
+}
+
+bool DisplayPortUtils::ViewportHasDisplayPort(nsPresContext* aPresContext) {
+ nsIFrame* rootScrollFrame = aPresContext->PresShell()->GetRootScrollFrame();
+ return rootScrollFrame && HasDisplayPort(rootScrollFrame->GetContent());
+}
+
+bool DisplayPortUtils::IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame) {
+ // Fixed-pos frames are parented by the viewport frame or the page content
+ // frame. We'll assume that printing/print preview don't have displayports for
+ // their pages!
+ nsIFrame* parent = aFrame->GetParent();
+ if (!parent || parent->GetParent() ||
+ aFrame->StyleDisplay()->mPosition != StylePositionProperty::Fixed) {
+ return false;
+ }
+ return ViewportHasDisplayPort(aFrame->PresContext());
+}
+
+// We want to this return true for the scroll frame, but not the
+// scrolled frame (which has the same content).
+bool DisplayPortUtils::FrameHasDisplayPort(nsIFrame* aFrame,
+ const nsIFrame* aScrolledFrame) {
+ if (!aFrame->GetContent() || !HasDisplayPort(aFrame->GetContent())) {
+ return false;
+ }
+ nsIScrollableFrame* sf = do_QueryFrame(aFrame);
+ if (sf) {
+ if (aScrolledFrame && aScrolledFrame != sf->GetScrolledFrame()) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool DisplayPortUtils::CalculateAndSetDisplayPortMargins(
+ nsIScrollableFrame* aScrollFrame, RepaintMode aRepaintMode) {
+ nsIFrame* frame = do_QueryFrame(aScrollFrame);
+ MOZ_ASSERT(frame);
+ nsIContent* content = frame->GetContent();
+ MOZ_ASSERT(content);
+
+ FrameMetrics metrics =
+ nsLayoutUtils::CalculateBasicFrameMetrics(aScrollFrame);
+ ScreenMargin displayportMargins = layers::apz::CalculatePendingDisplayPort(
+ metrics, ParentLayerPoint(0.0f, 0.0f));
+ PresShell* presShell = frame->PresContext()->GetPresShell();
+
+ DisplayPortMargins margins = DisplayPortMargins::ForScrollFrame(
+ aScrollFrame, displayportMargins,
+ Some(metrics.DisplayportPixelsPerCSSPixel()));
+
+ return SetDisplayPortMargins(content, presShell, margins, 0, aRepaintMode);
+}
+
+bool DisplayPortUtils::MaybeCreateDisplayPort(nsDisplayListBuilder* aBuilder,
+ nsIFrame* aScrollFrame,
+ RepaintMode aRepaintMode) {
+ nsIContent* content = aScrollFrame->GetContent();
+ nsIScrollableFrame* scrollableFrame = do_QueryFrame(aScrollFrame);
+ if (!content || !scrollableFrame) {
+ return false;
+ }
+
+ bool haveDisplayPort = HasDisplayPort(content);
+
+ // We perform an optimization where we ensure that at least one
+ // async-scrollable frame (i.e. one that WantsAsyncScroll()) has a
+ // displayport. If that's not the case yet, and we are async-scrollable, we
+ // will get a displayport.
+ if (aBuilder->IsPaintingToWindow() &&
+ nsLayoutUtils::AsyncPanZoomEnabled(aScrollFrame) &&
+ !aBuilder->HaveScrollableDisplayPort() &&
+ scrollableFrame->WantAsyncScroll()) {
+ // If we don't already have a displayport, calculate and set one.
+ if (!haveDisplayPort) {
+ // We only use the viewId for logging purposes, but create it
+ // unconditionally to minimize impact of enabling logging. If we don't
+ // assign a viewId here it will get assigned later anyway so functionally
+ // there should be no difference.
+ ViewID viewId = nsLayoutUtils::FindOrCreateIDFor(content);
+ MOZ_LOG(
+ sDisplayportLog, LogLevel::Debug,
+ ("Setting DP on first-encountered scrollId=%" PRIu64 "\n", viewId));
+
+ CalculateAndSetDisplayPortMargins(scrollableFrame, aRepaintMode);
+#ifdef DEBUG
+ haveDisplayPort = HasDisplayPort(content);
+ MOZ_ASSERT(haveDisplayPort,
+ "should have a displayport after having just set it");
+#endif
+ }
+
+ // Record that the we now have a scrollable display port.
+ aBuilder->SetHaveScrollableDisplayPort();
+ return true;
+ }
+ return false;
+}
+void DisplayPortUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
+ nsIFrame* aFrame) {
+ nsIFrame* frame = aFrame;
+ while (frame) {
+ frame = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(frame);
+ if (!frame) {
+ break;
+ }
+ nsIScrollableFrame* scrollAncestor =
+ nsLayoutUtils::GetAsyncScrollableAncestorFrame(frame);
+ if (!scrollAncestor) {
+ break;
+ }
+ frame = do_QueryFrame(scrollAncestor);
+ MOZ_ASSERT(frame);
+ MOZ_ASSERT(scrollAncestor->WantAsyncScroll() ||
+ frame->PresShell()->GetRootScrollFrame() == frame);
+ if (nsLayoutUtils::AsyncPanZoomEnabled(frame) &&
+ !HasDisplayPort(frame->GetContent())) {
+ SetDisplayPortMargins(frame->GetContent(), frame->PresShell(),
+ DisplayPortMargins::Empty(frame->GetContent()), 0,
+ RepaintMode::Repaint);
+ }
+ }
+}
+
+bool DisplayPortUtils::MaybeCreateDisplayPortInFirstScrollFrameEncountered(
+ nsIFrame* aFrame, nsDisplayListBuilder* aBuilder) {
+ nsIScrollableFrame* sf = do_QueryFrame(aFrame);
+ if (sf) {
+ if (MaybeCreateDisplayPort(aBuilder, aFrame, RepaintMode::Repaint)) {
+ return true;
+ }
+ }
+ if (aFrame->IsPlaceholderFrame()) {
+ nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(aFrame);
+ if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(
+ placeholder->GetOutOfFlowFrame(), aBuilder)) {
+ return true;
+ }
+ }
+ if (aFrame->IsSubDocumentFrame()) {
+ PresShell* presShell = static_cast<nsSubDocumentFrame*>(aFrame)
+ ->GetSubdocumentPresShellForPainting(0);
+ nsIFrame* root = presShell ? presShell->GetRootFrame() : nullptr;
+ if (root) {
+ if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(root, aBuilder)) {
+ return true;
+ }
+ }
+ }
+ if (aFrame->IsDeckFrame()) {
+ // only descend the visible card of a decks
+ nsIFrame* child = static_cast<nsDeckFrame*>(aFrame)->GetSelectedBox();
+ if (child) {
+ return MaybeCreateDisplayPortInFirstScrollFrameEncountered(child,
+ aBuilder);
+ }
+ }
+
+ for (nsIFrame* child : aFrame->PrincipalChildList()) {
+ if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(child, aBuilder)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void DisplayPortUtils::ExpireDisplayPortOnAsyncScrollableAncestor(
+ nsIFrame* aFrame) {
+ nsIFrame* frame = aFrame;
+ while (frame) {
+ frame = nsLayoutUtils::GetCrossDocParentFrame(frame);
+ if (!frame) {
+ break;
+ }
+ nsIScrollableFrame* scrollAncestor =
+ nsLayoutUtils::GetAsyncScrollableAncestorFrame(frame);
+ if (!scrollAncestor) {
+ break;
+ }
+ frame = do_QueryFrame(scrollAncestor);
+ MOZ_ASSERT(frame);
+ if (!frame) {
+ break;
+ }
+ MOZ_ASSERT(scrollAncestor->WantAsyncScroll() ||
+ frame->PresShell()->GetRootScrollFrame() == frame);
+ if (HasDisplayPort(frame->GetContent())) {
+ scrollAncestor->TriggerDisplayPortExpiration();
+ // Stop after the first trigger. If it failed, there's no point in
+ // continuing because all the rest of the frames we encounter are going
+ // to be ancestors of |scrollAncestor| which will keep its displayport.
+ // If the trigger succeeded, we stop because when the trigger executes
+ // it will call this function again to trigger the next ancestor up the
+ // chain.
+ break;
+ }
+ }
+}
+
+static PresShell* GetPresShell(const nsIContent* aContent) {
+ if (dom::Document* doc = aContent->GetComposedDoc()) {
+ return doc->GetPresShell();
+ }
+ return nullptr;
+}
+
+static void UpdateDisplayPortMarginsForPendingMetrics(
+ const RepaintRequest& aMetrics) {
+ nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());
+ if (!content) {
+ return;
+ }
+
+ RefPtr<PresShell> presShell = GetPresShell(content);
+ if (!presShell) {
+ return;
+ }
+
+ if (nsLayoutUtils::AllowZoomingForDocument(presShell->GetDocument()) &&
+ aMetrics.IsRootContent()) {
+ // See APZCCallbackHelper::UpdateRootFrame for details.
+ float presShellResolution = presShell->GetResolution();
+ if (presShellResolution != aMetrics.GetPresShellResolution()) {
+ return;
+ }
+ }
+
+ nsIScrollableFrame* frame =
+ nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());
+
+ if (!frame) {
+ return;
+ }
+
+ if (APZCCallbackHelper::IsScrollInProgress(frame)) {
+ // If these conditions are true, then the UpdateFrame
+ // message may be ignored by the main-thread, so we
+ // shouldn't update the displayport based on it.
+ return;
+ }
+
+ DisplayPortMarginsPropertyData* currentData =
+ static_cast<DisplayPortMarginsPropertyData*>(
+ content->GetProperty(nsGkAtoms::DisplayPortMargins));
+ if (!currentData) {
+ return;
+ }
+
+ CSSPoint frameScrollOffset =
+ CSSPoint::FromAppUnits(frame->GetScrollPosition());
+
+ DisplayPortUtils::SetDisplayPortMargins(
+ content, presShell,
+ DisplayPortMargins::FromAPZ(
+ aMetrics.GetDisplayPortMargins(), aMetrics.GetVisualScrollOffset(),
+ frameScrollOffset, aMetrics.DisplayportPixelsPerCSSPixel()),
+ 0);
+}
+
+/* static */
+void DisplayPortUtils::UpdateDisplayPortMarginsFromPendingMessages() {
+ if (XRE_IsContentProcess() && layers::CompositorBridgeChild::Get() &&
+ layers::CompositorBridgeChild::Get()->GetIPCChannel()) {
+ layers::CompositorBridgeChild::Get()->GetIPCChannel()->PeekMessages(
+ [](const IPC::Message& aMsg) -> bool {
+ if (aMsg.type() == layers::PAPZ::Msg_RequestContentRepaint__ID) {
+ PickleIterator iter(aMsg);
+ RepaintRequest request;
+ if (!IPC::ReadParam(&aMsg, &iter, &request)) {
+ MOZ_ASSERT(false);
+ return true;
+ }
+
+ UpdateDisplayPortMarginsForPendingMetrics(request);
+ }
+ return true;
+ });
+ }
+}
+
+} // namespace mozilla