Bug 1726704 [wpt PR 30103] - Add more extensive tests for .canShare(), a=testonly
[gecko.git] / layout / base / DisplayPortUtils.cpp
blob6903f5b1cc8619e15f5bd62e85ba76fa94a093cf
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DisplayPortUtils.h"
9 #include "FrameMetrics.h"
10 #include "Layers.h"
11 #include "mozilla/dom/BrowserChild.h"
12 #include "mozilla/dom/Document.h"
13 #include "mozilla/gfx/gfxVars.h"
14 #include "mozilla/gfx/Point.h"
15 #include "mozilla/layers/APZCCallbackHelper.h"
16 #include "mozilla/layers/APZPublicUtils.h"
17 #include "mozilla/layers/CompositorBridgeChild.h"
18 #include "mozilla/layers/PAPZ.h"
19 #include "mozilla/layers/RepaintRequest.h"
20 #include "mozilla/PresShell.h"
21 #include "mozilla/StaticPrefs_layers.h"
22 #include "nsDeckFrame.h"
23 #include "nsIScrollableFrame.h"
24 #include "nsLayoutUtils.h"
25 #include "nsPlaceholderFrame.h"
26 #include "nsSubDocumentFrame.h"
27 #include "RetainedDisplayListBuilder.h"
29 #include <ostream>
31 namespace mozilla {
33 using gfx::gfxVars;
34 using gfx::IntSize;
36 using layers::APZCCallbackHelper;
37 using layers::FrameMetrics;
38 using layers::LayerManager;
39 using layers::RepaintRequest;
40 using layers::ScrollableLayerGuid;
42 typedef ScrollableLayerGuid::ViewID ViewID;
44 static LazyLogModule sDisplayportLog("apz.displayport");
46 /* static */
47 DisplayPortMargins DisplayPortMargins::FromAPZ(
48 const ScreenMargin& aMargins, const CSSPoint& aVisualOffset,
49 const CSSPoint& aLayoutOffset, const CSSToScreenScale2D& aScale) {
50 return DisplayPortMargins{aMargins, aVisualOffset, aLayoutOffset, aScale};
53 CSSToScreenScale2D ComputeDisplayportScale(nsIScrollableFrame* aScrollFrame) {
54 // The calculation here is equivalent to
55 // CalculateBasicFrameMetrics(aScrollFrame).DisplayportPixelsPerCSSPixel(),
56 // but we don't calculate the entire frame metrics.
57 if (!aScrollFrame) {
58 return CSSToScreenScale2D(1.0, 1.0);
60 nsIFrame* frame = do_QueryFrame(aScrollFrame);
61 MOZ_ASSERT(frame);
62 nsPresContext* presContext = frame->PresContext();
63 PresShell* presShell = presContext->PresShell();
64 return presContext->CSSToDevPixelScale() *
65 LayoutDeviceToLayerScale2D(
66 presShell->GetCumulativeResolution() *
67 nsLayoutUtils::GetTransformToAncestorScale(frame)) *
68 LayerToScreenScale2D(1.0, 1.0);
71 /* static */
72 DisplayPortMargins DisplayPortMargins::ForScrollFrame(
73 nsIScrollableFrame* aScrollFrame, const ScreenMargin& aMargins,
74 const Maybe<CSSToScreenScale2D>& aScale) {
75 CSSPoint visualOffset;
76 CSSPoint layoutOffset;
77 if (aScrollFrame) {
78 nsIFrame* scrollFrame = do_QueryFrame(aScrollFrame);
79 PresShell* presShell = scrollFrame->PresShell();
80 layoutOffset = CSSPoint::FromAppUnits(aScrollFrame->GetScrollPosition());
81 if (aScrollFrame->IsRootScrollFrameOfDocument() &&
82 presShell->IsVisualViewportOffsetSet()) {
83 visualOffset =
84 CSSPoint::FromAppUnits(presShell->GetVisualViewportOffset());
86 } else {
87 visualOffset = layoutOffset;
90 return DisplayPortMargins{aMargins, visualOffset, layoutOffset,
91 aScale.valueOrFrom([&] {
92 return ComputeDisplayportScale(aScrollFrame);
93 })};
96 /* static */
97 DisplayPortMargins DisplayPortMargins::ForContent(
98 nsIContent* aContent, const ScreenMargin& aMargins) {
99 return ForScrollFrame(
100 aContent ? nsLayoutUtils::FindScrollableFrameFor(aContent) : nullptr,
101 aMargins, Nothing());
104 ScreenMargin DisplayPortMargins::GetRelativeToLayoutViewport(
105 ContentGeometryType aGeometryType,
106 nsIScrollableFrame* aScrollableFrame) const {
107 // APZ wants |mMargins| applied relative to the visual viewport.
108 // The main-thread painting code applies margins relative to
109 // the layout viewport. To get the main thread to paint the
110 // area APZ wants, apply a translation between the two. The
111 // magnitude of the translation depends on whether we are
112 // applying the displayport to scrolled or fixed content.
113 CSSPoint scrollDeltaCss =
114 ComputeAsyncTranslation(aGeometryType, aScrollableFrame);
115 ScreenPoint scrollDelta = scrollDeltaCss * mScale;
116 ScreenMargin margins = mMargins;
117 margins.left -= scrollDelta.x;
118 margins.right += scrollDelta.x;
119 margins.top -= scrollDelta.y;
120 margins.bottom += scrollDelta.y;
121 return margins;
124 std::ostream& operator<<(std::ostream& aOs,
125 const DisplayPortMargins& aMargins) {
126 if (aMargins.mVisualOffset == CSSPoint() &&
127 aMargins.mLayoutOffset == CSSPoint()) {
128 aOs << aMargins.mMargins;
129 } else {
130 aOs << "{" << aMargins.mMargins << "," << aMargins.mVisualOffset << ","
131 << aMargins.mLayoutOffset << "}";
133 return aOs;
136 CSSPoint DisplayPortMargins::ComputeAsyncTranslation(
137 ContentGeometryType aGeometryType,
138 nsIScrollableFrame* aScrollableFrame) const {
139 // If we are applying the displayport to scrolled content, the
140 // translation is the entire difference between the visual and
141 // layout offsets.
142 if (aGeometryType == ContentGeometryType::Scrolled) {
143 return mVisualOffset - mLayoutOffset;
146 // If we are applying the displayport to fixed content, only
147 // part of the difference between the visual and layout offsets
148 // should be applied. This is because fixed content remains fixed
149 // to the layout viewport, and some of the async delta between
150 // the visual and layout offsets can drag the layout viewport
151 // with it. We want only the remaining delta, i.e. the offset of
152 // the visual viewport relative to the (async-scrolled) layout
153 // viewport.
154 if (!aScrollableFrame) {
155 // Displayport on a non-scrolling frame for some reason.
156 // There will be no divergence between the two viewports.
157 return CSSPoint();
159 // Fixed content is always fixed to an RSF.
160 MOZ_ASSERT(aScrollableFrame->IsRootScrollFrameOfDocument());
161 nsIFrame* scrollFrame = do_QueryFrame(aScrollableFrame);
162 if (!scrollFrame->PresShell()->IsVisualViewportSizeSet()) {
163 // Zooming is disabled, so the layout viewport tracks the
164 // visual viewport completely.
165 return CSSPoint();
167 // Use KeepLayoutViewportEnclosingViewportVisual() to compute
168 // an async layout viewport the way APZ would.
169 const CSSRect visualViewport{
170 mVisualOffset,
171 // TODO: There are probably some edge cases here around async zooming
172 // that are not currently being handled properly. For proper handling,
173 // we'd likely need to save APZ's async zoom when populating
174 // mVisualOffset, and using it to adjust the visual viewport size here.
175 // Note that any incorrectness caused by this will only occur transiently
176 // during async zooming.
177 CSSSize::FromAppUnits(scrollFrame->PresShell()->GetVisualViewportSize())};
178 const CSSRect scrollableRect = CSSRect::FromAppUnits(
179 nsLayoutUtils::CalculateExpandedScrollableRect(scrollFrame));
180 CSSRect asyncLayoutViewport{
181 mLayoutOffset,
182 CSSSize::FromAppUnits(aScrollableFrame->GetScrollPortRect().Size())};
183 FrameMetrics::KeepLayoutViewportEnclosingVisualViewport(
184 visualViewport, scrollableRect, /* out */ asyncLayoutViewport);
185 return mVisualOffset - asyncLayoutViewport.TopLeft();
188 // Return the maximum displayport size, based on the LayerManager's maximum
189 // supported texture size. The result is in app units.
190 static nscoord GetMaxDisplayPortSize(nsIContent* aContent,
191 nsPresContext* aFallbackPrescontext) {
192 MOZ_ASSERT(!StaticPrefs::layers_enable_tiles_AtStartup(),
193 "Do not clamp displayports if tiling is enabled");
195 // Pick a safe maximum displayport size for sanity purposes. This is the
196 // lowest maximum texture size on tileless-platforms (Windows, D3D10).
197 // If the gfx.max-texture-size pref is set, further restrict the displayport
198 // size to fit within that, because the compositor won't upload stuff larger
199 // than this size.
200 nscoord safeMaximum = aFallbackPrescontext
201 ? aFallbackPrescontext->DevPixelsToAppUnits(
202 std::min(8192, gfxPlatform::MaxTextureSize()))
203 : nscoord_MAX;
205 nsIFrame* frame = aContent->GetPrimaryFrame();
206 if (!frame) {
207 return safeMaximum;
209 frame = nsLayoutUtils::GetDisplayRootFrame(frame);
211 nsIWidget* widget = frame->GetNearestWidget();
212 if (!widget) {
213 return safeMaximum;
215 WindowRenderer* renderer = widget->GetWindowRenderer();
216 if (!renderer || !renderer->AsLayerManager()) {
217 return safeMaximum;
219 nsPresContext* presContext = frame->PresContext();
221 int32_t maxSizeInDevPixels = renderer->AsLayerManager()->GetMaxTextureSize();
222 if (maxSizeInDevPixels < 0 || maxSizeInDevPixels == INT_MAX) {
223 return safeMaximum;
225 maxSizeInDevPixels =
226 std::min(maxSizeInDevPixels, gfxPlatform::MaxTextureSize());
227 return presContext->DevPixelsToAppUnits(maxSizeInDevPixels);
230 static nsRect ApplyRectMultiplier(nsRect aRect, float aMultiplier) {
231 if (aMultiplier == 1.0f) {
232 return aRect;
234 float newWidth = aRect.width * aMultiplier;
235 float newHeight = aRect.height * aMultiplier;
236 float newX = aRect.x - ((newWidth - aRect.width) / 2.0f);
237 float newY = aRect.y - ((newHeight - aRect.height) / 2.0f);
238 // Rounding doesn't matter too much here, do a round-in
239 return nsRect(ceil(newX), ceil(newY), floor(newWidth), floor(newHeight));
242 static nsRect GetDisplayPortFromRectData(nsIContent* aContent,
243 DisplayPortPropertyData* aRectData,
244 float aMultiplier) {
245 // In the case where the displayport is set as a rect, we assume it is
246 // already aligned and clamped as necessary. The burden to do that is
247 // on the setter of the displayport. In practice very few places set the
248 // displayport directly as a rect (mostly tests). We still do need to
249 // expand it by the multiplier though.
250 return ApplyRectMultiplier(aRectData->mRect, aMultiplier);
253 static nsRect GetDisplayPortFromMarginsData(
254 nsIContent* aContent, DisplayPortMarginsPropertyData* aMarginsData,
255 float aMultiplier, const DisplayPortOptions& aOptions) {
256 // In the case where the displayport is set via margins, we apply the margins
257 // to a base rect. Then we align the expanded rect based on the alignment
258 // requested, further expand the rect by the multiplier, and finally, clamp it
259 // to the size of the scrollable rect.
261 nsRect base;
262 if (nsRect* baseData = static_cast<nsRect*>(
263 aContent->GetProperty(nsGkAtoms::DisplayPortBase))) {
264 base = *baseData;
265 } else {
266 // In theory we shouldn't get here, but we do sometimes (see bug 1212136).
267 // Fall through for graceful handling.
270 nsIFrame* frame = nsLayoutUtils::GetScrollFrameFromContent(aContent);
271 if (!frame) {
272 // Turns out we can't really compute it. Oops. We still should return
273 // something sane. Note that since we can't clamp the rect without a
274 // frame, we don't apply the multiplier either as it can cause the result
275 // to leak outside the scrollable area.
276 NS_WARNING(
277 "Attempting to get a displayport from a content with no primary "
278 "frame!");
279 return base;
282 bool isRoot = false;
283 if (aContent->OwnerDoc()->GetRootElement() == aContent) {
284 isRoot = true;
287 nsIScrollableFrame* scrollableFrame = frame->GetScrollTargetFrame();
288 nsPoint scrollPos;
289 if (scrollableFrame) {
290 scrollPos = scrollableFrame->GetScrollPosition();
293 nsPresContext* presContext = frame->PresContext();
294 int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
296 LayoutDeviceToScreenScale2D res(
297 presContext->PresShell()->GetCumulativeResolution() *
298 nsLayoutUtils::GetTransformToAncestorScale(frame));
300 // Calculate the expanded scrollable rect, which we'll be clamping the
301 // displayport to.
302 nsRect expandedScrollableRect =
303 nsLayoutUtils::CalculateExpandedScrollableRect(frame);
305 // GetTransformToAncestorScale() can return 0. In this case, just return the
306 // base rect (clamped to the expanded scrollable rect), as other calculations
307 // would run into divisions by zero.
308 if (res == LayoutDeviceToScreenScale2D(0, 0)) {
309 // Make sure the displayport remains within the scrollable rect.
310 return base.MoveInsideAndClamp(expandedScrollableRect - scrollPos);
313 // First convert the base rect to screen pixels
314 LayoutDeviceToScreenScale2D parentRes = res;
315 if (isRoot) {
316 // the base rect for root scroll frames is specified in the parent document
317 // coordinate space, so it doesn't include the local resolution.
318 float localRes = presContext->PresShell()->GetResolution();
319 parentRes.xScale /= localRes;
320 parentRes.yScale /= localRes;
322 ScreenRect screenRect =
323 LayoutDeviceRect::FromAppUnits(base, auPerDevPixel) * parentRes;
325 // Note on the correctness of applying the alignment in Screen space:
326 // The correct space to apply the alignment in would be Layer space, but
327 // we don't necessarily know the scale to convert to Layer space at this
328 // point because Layout may not yet have chosen the resolution at which to
329 // render (it chooses that in FrameLayerBuilder, but this can be called
330 // during display list building). Therefore, we perform the alignment in
331 // Screen space, which basically assumes that Layout chose to render at
332 // screen resolution; since this is what Layout does most of the time,
333 // this is a good approximation. A proper solution would involve moving
334 // the choosing of the resolution to display-list building time.
335 ScreenSize alignment;
337 PresShell* presShell = presContext->PresShell();
338 MOZ_ASSERT(presShell);
340 bool useWebRender = gfxVars::UseWebRender();
342 ScreenMargin margins = aMarginsData->mMargins.GetRelativeToLayoutViewport(
343 aOptions.mGeometryType, scrollableFrame);
345 if (presShell->IsDisplayportSuppressed() ||
346 aContent->GetProperty(nsGkAtoms::MinimalDisplayPort)) {
347 alignment = ScreenSize(1, 1);
348 } else if (useWebRender) {
349 // Moving the displayport is relatively expensive with WR so we use a larger
350 // alignment that causes the displayport to move less frequently. The
351 // alignment scales up with the size of the base rect so larger scrollframes
352 // use a larger alignment, but we clamp the alignment to a power of two
353 // between 128 and 1024 (inclusive).
354 // This naturally also increases the size of the displayport compared to
355 // always using a 128 alignment, so the displayport multipliers are also
356 // correspondingly smaller when WR is enabled to prevent the displayport
357 // from becoming too big.
358 IntSize multiplier =
359 layers::apz::GetDisplayportAlignmentMultiplier(screenRect.Size());
360 alignment = ScreenSize(128 * multiplier.width, 128 * multiplier.height);
361 } else if (StaticPrefs::layers_enable_tiles_AtStartup()) {
362 // Don't align to tiles if they are too large, because we could expand
363 // the displayport by a lot which can take more paint time. It's a tradeoff
364 // though because if we don't align to tiles we have more waste on upload.
365 IntSize tileSize = gfxVars::TileSize();
366 alignment = ScreenSize(std::min(256, tileSize.width),
367 std::min(256, tileSize.height));
368 } else {
369 // If we're not drawing with tiles then we need to be careful about not
370 // hitting the max texture size and we only need 1 draw call per layer
371 // so we can align to a smaller multiple.
372 alignment = ScreenSize(128, 128);
375 // Avoid division by zero.
376 if (alignment.width == 0) {
377 alignment.width = 128;
379 if (alignment.height == 0) {
380 alignment.height = 128;
383 if (StaticPrefs::layers_enable_tiles_AtStartup() || useWebRender) {
384 // Expand the rect by the margins
385 screenRect.Inflate(margins);
386 } else {
387 // Calculate the displayport to make sure we fit within the max texture size
388 // when not tiling.
389 nscoord maxSizeAppUnits = GetMaxDisplayPortSize(aContent, presContext);
390 MOZ_ASSERT(maxSizeAppUnits < nscoord_MAX);
392 // The alignment code can round up to 3 tiles, we want to make sure
393 // that the displayport can grow by up to 3 tiles without going
394 // over the max texture size.
395 const int MAX_ALIGN_ROUNDING = 3;
397 // Find the maximum size in screen pixels.
398 int32_t maxSizeDevPx = presContext->AppUnitsToDevPixels(maxSizeAppUnits);
399 int32_t maxWidthScreenPx = floor(double(maxSizeDevPx) * res.xScale) -
400 MAX_ALIGN_ROUNDING * alignment.width;
401 int32_t maxHeightScreenPx = floor(double(maxSizeDevPx) * res.yScale) -
402 MAX_ALIGN_ROUNDING * alignment.height;
404 // For each axis, inflate the margins up to the maximum size.
405 if (screenRect.height < maxHeightScreenPx) {
406 int32_t budget = maxHeightScreenPx - screenRect.height;
407 // Scale the margins down to fit into the budget if necessary, maintaining
408 // their relative ratio.
409 float scale = 1.0f;
410 if (float(budget) < margins.TopBottom()) {
411 scale = float(budget) / margins.TopBottom();
413 float top = margins.top * scale;
414 float bottom = margins.bottom * scale;
415 screenRect.y -= top;
416 screenRect.height += top + bottom;
418 if (screenRect.width < maxWidthScreenPx) {
419 int32_t budget = maxWidthScreenPx - screenRect.width;
420 float scale = 1.0f;
421 if (float(budget) < margins.LeftRight()) {
422 scale = float(budget) / margins.LeftRight();
424 float left = margins.left * scale;
425 float right = margins.right * scale;
426 screenRect.x -= left;
427 screenRect.width += left + right;
431 ScreenPoint scrollPosScreen =
432 LayoutDevicePoint::FromAppUnits(scrollPos, auPerDevPixel) * res;
434 // Align the display port.
435 screenRect += scrollPosScreen;
436 float x = alignment.width * floor(screenRect.x / alignment.width);
437 float y = alignment.height * floor(screenRect.y / alignment.height);
438 float w = alignment.width * ceil(screenRect.width / alignment.width + 1);
439 float h = alignment.height * ceil(screenRect.height / alignment.height + 1);
440 screenRect = ScreenRect(x, y, w, h);
441 screenRect -= scrollPosScreen;
443 // Convert the aligned rect back into app units.
444 nsRect result = LayoutDeviceRect::ToAppUnits(screenRect / res, auPerDevPixel);
446 // If we have non-zero margins, expand the displayport for the low-res buffer
447 // if that's what we're drawing. If we have zero margins, we want the
448 // displayport to reflect the scrollport.
449 if (margins != ScreenMargin()) {
450 result = ApplyRectMultiplier(result, aMultiplier);
453 // Make sure the displayport remains within the scrollable rect.
454 result = result.MoveInsideAndClamp(expandedScrollableRect - scrollPos);
456 return result;
459 static bool GetDisplayPortData(
460 nsIContent* aContent, DisplayPortPropertyData** aOutRectData,
461 DisplayPortMarginsPropertyData** aOutMarginsData) {
462 MOZ_ASSERT(aOutRectData && aOutMarginsData);
464 *aOutRectData = static_cast<DisplayPortPropertyData*>(
465 aContent->GetProperty(nsGkAtoms::DisplayPort));
466 *aOutMarginsData = static_cast<DisplayPortMarginsPropertyData*>(
467 aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
469 if (!*aOutRectData && !*aOutMarginsData) {
470 // This content element has no displayport data at all
471 return false;
474 if (*aOutRectData && *aOutMarginsData) {
475 // choose margins if equal priority
476 if ((*aOutRectData)->mPriority > (*aOutMarginsData)->mPriority) {
477 *aOutMarginsData = nullptr;
478 } else {
479 *aOutRectData = nullptr;
483 NS_ASSERTION((*aOutRectData == nullptr) != (*aOutMarginsData == nullptr),
484 "Only one of aOutRectData or aOutMarginsData should be set!");
486 return true;
489 static bool GetWasDisplayPortPainted(nsIContent* aContent) {
490 DisplayPortPropertyData* rectData = nullptr;
491 DisplayPortMarginsPropertyData* marginsData = nullptr;
493 if (!GetDisplayPortData(aContent, &rectData, &marginsData)) {
494 return false;
497 return rectData ? rectData->mPainted : marginsData->mPainted;
500 bool DisplayPortUtils::IsMissingDisplayPortBaseRect(nsIContent* aContent) {
501 DisplayPortPropertyData* rectData = nullptr;
502 DisplayPortMarginsPropertyData* marginsData = nullptr;
504 if (GetDisplayPortData(aContent, &rectData, &marginsData) && marginsData) {
505 return !aContent->GetProperty(nsGkAtoms::DisplayPortBase);
508 return false;
511 static void TranslateFromScrollPortToScrollFrame(nsIContent* aContent,
512 nsRect* aRect) {
513 MOZ_ASSERT(aRect);
514 if (nsIScrollableFrame* scrollableFrame =
515 nsLayoutUtils::FindScrollableFrameFor(aContent)) {
516 *aRect += scrollableFrame->GetScrollPortRect().TopLeft();
520 static bool GetDisplayPortImpl(nsIContent* aContent, nsRect* aResult,
521 float aMultiplier,
522 const DisplayPortOptions& aOptions) {
523 DisplayPortPropertyData* rectData = nullptr;
524 DisplayPortMarginsPropertyData* marginsData = nullptr;
526 if (!GetDisplayPortData(aContent, &rectData, &marginsData)) {
527 return false;
530 nsIFrame* frame = aContent->GetPrimaryFrame();
531 if (frame && !frame->PresShell()->AsyncPanZoomEnabled()) {
532 return false;
535 if (!aResult) {
536 // We have displayport data, but the caller doesn't want the actual
537 // rect, so we don't need to actually compute it.
538 return true;
541 bool isDisplayportSuppressed = false;
543 if (frame) {
544 nsPresContext* presContext = frame->PresContext();
545 MOZ_ASSERT(presContext);
546 PresShell* presShell = presContext->PresShell();
547 MOZ_ASSERT(presShell);
548 isDisplayportSuppressed = presShell->IsDisplayportSuppressed();
551 nsRect result;
552 if (rectData) {
553 result = GetDisplayPortFromRectData(aContent, rectData, aMultiplier);
554 } else if (isDisplayportSuppressed ||
555 nsLayoutUtils::ShouldDisableApzForElement(aContent) ||
556 aContent->GetProperty(nsGkAtoms::MinimalDisplayPort)) {
557 // Make a copy of the margins data but set the margins to empty.
558 // Do not create a new DisplayPortMargins object with
559 // DisplayPortMargins::Empty(), because that will record the visual
560 // and layout scroll offsets in place right now on the DisplayPortMargins,
561 // and those are only meant to be recorded when the margins are stored.
562 DisplayPortMarginsPropertyData noMargins = *marginsData;
563 noMargins.mMargins.mMargins = ScreenMargin();
564 result = GetDisplayPortFromMarginsData(aContent, &noMargins, aMultiplier,
565 aOptions);
566 } else {
567 result = GetDisplayPortFromMarginsData(aContent, marginsData, aMultiplier,
568 aOptions);
571 if (!StaticPrefs::layers_enable_tiles_AtStartup()) {
572 // Perform the desired error handling if the displayport dimensions
573 // exceeds the maximum allowed size
574 nscoord maxSize = GetMaxDisplayPortSize(aContent, nullptr);
575 if (result.width > maxSize || result.height > maxSize) {
576 switch (aOptions.mMaxSizeExceededBehaviour) {
577 case MaxSizeExceededBehaviour::Assert:
578 NS_ASSERTION(false, "Displayport must be a valid texture size");
579 break;
580 case MaxSizeExceededBehaviour::Drop:
581 return false;
586 if (aOptions.mRelativeTo == DisplayportRelativeTo::ScrollFrame) {
587 TranslateFromScrollPortToScrollFrame(aContent, &result);
590 *aResult = result;
591 return true;
594 bool DisplayPortUtils::GetDisplayPort(nsIContent* aContent, nsRect* aResult,
595 const DisplayPortOptions& aOptions) {
596 float multiplier = StaticPrefs::layers_low_precision_buffer()
597 ? 1.0f / StaticPrefs::layers_low_precision_resolution()
598 : 1.0f;
599 return GetDisplayPortImpl(aContent, aResult, multiplier, aOptions);
602 bool DisplayPortUtils::HasDisplayPort(nsIContent* aContent) {
603 return GetDisplayPort(aContent, nullptr);
606 bool DisplayPortUtils::HasPaintedDisplayPort(nsIContent* aContent) {
607 DisplayPortPropertyData* rectData = nullptr;
608 DisplayPortMarginsPropertyData* marginsData = nullptr;
609 GetDisplayPortData(aContent, &rectData, &marginsData);
610 if (rectData) {
611 return rectData->mPainted;
613 if (marginsData) {
614 return marginsData->mPainted;
616 return false;
619 void DisplayPortUtils::MarkDisplayPortAsPainted(nsIContent* aContent) {
620 DisplayPortPropertyData* rectData = nullptr;
621 DisplayPortMarginsPropertyData* marginsData = nullptr;
622 GetDisplayPortData(aContent, &rectData, &marginsData);
623 MOZ_ASSERT(rectData || marginsData,
624 "MarkDisplayPortAsPainted should only be called for an element "
625 "with a displayport");
626 if (rectData) {
627 rectData->mPainted = true;
629 if (marginsData) {
630 marginsData->mPainted = true;
634 bool DisplayPortUtils::HasNonMinimalDisplayPort(nsIContent* aContent) {
635 return HasDisplayPort(aContent) &&
636 !aContent->GetProperty(nsGkAtoms::MinimalDisplayPort);
639 bool DisplayPortUtils::HasNonMinimalNonZeroDisplayPort(nsIContent* aContent) {
640 if (!HasDisplayPort(aContent)) {
641 return false;
643 if (aContent->GetProperty(nsGkAtoms::MinimalDisplayPort)) {
644 return false;
647 DisplayPortMarginsPropertyData* currentData =
648 static_cast<DisplayPortMarginsPropertyData*>(
649 aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
651 if (!currentData) {
652 // We have a display port, so if we don't have margin data we must have rect
653 // data. We consider such as non zero and non minimal, it's probably not too
654 // important as display port rects are only used in tests.
655 return true;
658 if (currentData->mMargins.mMargins != ScreenMargin()) {
659 return true;
662 return false;
665 /* static */
666 bool DisplayPortUtils::GetDisplayPortForVisibilityTesting(nsIContent* aContent,
667 nsRect* aResult) {
668 MOZ_ASSERT(aResult);
669 // Since the base rect might not have been updated very recently, it's
670 // possible to end up with an extra-large displayport at this point, if the
671 // zoom level is changed by a lot. Instead of using the default behaviour of
672 // asserting, we can just ignore the displayport if that happens, as this
673 // call site is best-effort.
674 return GetDisplayPortImpl(aContent, aResult, 1.0f,
675 DisplayPortOptions()
676 .With(MaxSizeExceededBehaviour::Drop)
677 .With(DisplayportRelativeTo::ScrollFrame));
680 void DisplayPortUtils::InvalidateForDisplayPortChange(
681 nsIContent* aContent, bool aHadDisplayPort, const nsRect& aOldDisplayPort,
682 const nsRect& aNewDisplayPort, RepaintMode aRepaintMode) {
683 if (aRepaintMode != RepaintMode::Repaint) {
684 return;
687 bool changed =
688 !aHadDisplayPort || !aOldDisplayPort.IsEqualEdges(aNewDisplayPort);
690 nsIFrame* frame = nsLayoutUtils::GetScrollFrameFromContent(aContent);
691 if (frame) {
692 frame = do_QueryFrame(frame->GetScrollTargetFrame());
695 if (changed && frame) {
696 // It is important to call SchedulePaint on the same frame that we set the
697 // dirty rect properties on so we can find the frame later to remove the
698 // properties.
699 frame->SchedulePaint();
701 if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() ||
702 !nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(frame)) {
703 return;
706 bool found;
707 nsRect* rect = frame->GetProperty(
708 nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), &found);
710 if (!found) {
711 rect = new nsRect();
712 frame->AddProperty(
713 nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), rect);
714 frame->SetHasOverrideDirtyRegion(true);
716 nsIFrame* rootFrame = frame->PresShell()->GetRootFrame();
717 MOZ_ASSERT(rootFrame);
719 RetainedDisplayListData* data =
720 GetOrSetRetainedDisplayListData(rootFrame);
721 data->Flags(frame) |= RetainedDisplayListData::FrameFlags::HasProps;
722 } else {
723 MOZ_ASSERT(rect, "this property should only store non-null values");
726 if (aHadDisplayPort) {
727 // We only need to build a display list for any new areas added
728 nsRegion newRegion(aNewDisplayPort);
729 newRegion.SubOut(aOldDisplayPort);
730 rect->UnionRect(*rect, newRegion.GetBounds());
731 } else {
732 rect->UnionRect(*rect, aNewDisplayPort);
737 bool DisplayPortUtils::SetDisplayPortMargins(
738 nsIContent* aContent, PresShell* aPresShell,
739 const DisplayPortMargins& aMargins,
740 ClearMinimalDisplayPortProperty aClearMinimalDisplayPortProperty,
741 uint32_t aPriority, RepaintMode aRepaintMode) {
742 MOZ_ASSERT(aContent);
743 MOZ_ASSERT(aContent->GetComposedDoc() == aPresShell->GetDocument());
745 DisplayPortMarginsPropertyData* currentData =
746 static_cast<DisplayPortMarginsPropertyData*>(
747 aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
748 if (currentData && currentData->mPriority > aPriority) {
749 return false;
752 if (currentData && currentData->mMargins.mVisualOffset != CSSPoint() &&
753 aMargins.mVisualOffset == CSSPoint()) {
754 // If we hit this, then it's possible that we're setting a displayport
755 // that is wrong because the old one had a layout/visual adjustment and
756 // the new one does not.
757 MOZ_LOG(sDisplayportLog, LogLevel::Warning,
758 ("Dropping visual offset %s",
759 ToString(currentData->mMargins.mVisualOffset).c_str()));
762 nsIFrame* scrollFrame = nsLayoutUtils::GetScrollFrameFromContent(aContent);
764 nsRect oldDisplayPort;
765 bool hadDisplayPort = false;
766 bool wasPainted = GetWasDisplayPortPainted(aContent);
767 if (scrollFrame) {
768 // We only use the two return values from this function to call
769 // InvalidateForDisplayPortChange. InvalidateForDisplayPortChange does
770 // nothing if aContent does not have a frame. So getting the displayport is
771 // useless if the content has no frame, so we avoid calling this to avoid
772 // triggering a warning about not having a frame.
773 hadDisplayPort = GetHighResolutionDisplayPort(aContent, &oldDisplayPort);
776 aContent->SetProperty(
777 nsGkAtoms::DisplayPortMargins,
778 new DisplayPortMarginsPropertyData(aMargins, aPriority, wasPainted),
779 nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
781 if (aClearMinimalDisplayPortProperty ==
782 ClearMinimalDisplayPortProperty::Yes) {
783 if (MOZ_LOG_TEST(sDisplayportLog, LogLevel::Debug) &&
784 aContent->GetProperty(nsGkAtoms::MinimalDisplayPort)) {
785 mozilla::layers::ScrollableLayerGuid::ViewID viewID =
786 mozilla::layers::ScrollableLayerGuid::NULL_SCROLL_ID;
787 nsLayoutUtils::FindIDFor(aContent, &viewID);
788 MOZ_LOG(sDisplayportLog, LogLevel::Debug,
789 ("SetDisplayPortMargins removing MinimalDisplayPort prop on "
790 "scrollId=%" PRIu64 "\n",
791 viewID));
793 aContent->RemoveProperty(nsGkAtoms::MinimalDisplayPort);
796 nsIScrollableFrame* scrollableFrame =
797 scrollFrame ? scrollFrame->GetScrollTargetFrame() : nullptr;
798 if (!scrollableFrame) {
799 return true;
802 nsRect newDisplayPort;
803 DebugOnly<bool> hasDisplayPort =
804 GetHighResolutionDisplayPort(aContent, &newDisplayPort);
805 MOZ_ASSERT(hasDisplayPort);
807 if (MOZ_LOG_TEST(sDisplayportLog, LogLevel::Debug)) {
808 mozilla::layers::ScrollableLayerGuid::ViewID viewID =
809 mozilla::layers::ScrollableLayerGuid::NULL_SCROLL_ID;
810 nsLayoutUtils::FindIDFor(aContent, &viewID);
811 if (!hadDisplayPort) {
812 MOZ_LOG(sDisplayportLog, LogLevel::Debug,
813 ("SetDisplayPortMargins %s on scrollId=%" PRIu64 ", newDp=%s\n",
814 ToString(aMargins).c_str(), viewID,
815 ToString(newDisplayPort).c_str()));
816 } else {
817 // Use verbose level logging for when an existing displayport got its
818 // margins updated.
819 MOZ_LOG(sDisplayportLog, LogLevel::Verbose,
820 ("SetDisplayPortMargins %s on scrollId=%" PRIu64 ", newDp=%s\n",
821 ToString(aMargins).c_str(), viewID,
822 ToString(newDisplayPort).c_str()));
826 InvalidateForDisplayPortChange(aContent, hadDisplayPort, oldDisplayPort,
827 newDisplayPort, aRepaintMode);
829 scrollableFrame->TriggerDisplayPortExpiration();
831 // Display port margins changing means that the set of visible frames may
832 // have drastically changed. Check if we should schedule an update.
833 hadDisplayPort =
834 scrollableFrame->GetDisplayPortAtLastApproximateFrameVisibilityUpdate(
835 &oldDisplayPort);
837 bool needVisibilityUpdate = !hadDisplayPort;
838 // Check if the total size has changed by a large factor.
839 if (!needVisibilityUpdate) {
840 if ((newDisplayPort.width > 2 * oldDisplayPort.width) ||
841 (oldDisplayPort.width > 2 * newDisplayPort.width) ||
842 (newDisplayPort.height > 2 * oldDisplayPort.height) ||
843 (oldDisplayPort.height > 2 * newDisplayPort.height)) {
844 needVisibilityUpdate = true;
847 // Check if it's moved by a significant amount.
848 if (!needVisibilityUpdate) {
849 if (nsRect* baseData = static_cast<nsRect*>(
850 aContent->GetProperty(nsGkAtoms::DisplayPortBase))) {
851 nsRect base = *baseData;
852 if ((std::abs(newDisplayPort.X() - oldDisplayPort.X()) > base.width) ||
853 (std::abs(newDisplayPort.XMost() - oldDisplayPort.XMost()) >
854 base.width) ||
855 (std::abs(newDisplayPort.Y() - oldDisplayPort.Y()) > base.height) ||
856 (std::abs(newDisplayPort.YMost() - oldDisplayPort.YMost()) >
857 base.height)) {
858 needVisibilityUpdate = true;
862 if (needVisibilityUpdate) {
863 aPresShell->ScheduleApproximateFrameVisibilityUpdateNow();
866 return true;
869 void DisplayPortUtils::SetDisplayPortBase(nsIContent* aContent,
870 const nsRect& aBase) {
871 if (MOZ_LOG_TEST(sDisplayportLog, LogLevel::Verbose)) {
872 ViewID viewId = nsLayoutUtils::FindOrCreateIDFor(aContent);
873 MOZ_LOG(sDisplayportLog, LogLevel::Verbose,
874 ("Setting base rect %s for scrollId=%" PRIu64 "\n",
875 ToString(aBase).c_str(), viewId));
877 aContent->SetProperty(nsGkAtoms::DisplayPortBase, new nsRect(aBase),
878 nsINode::DeleteProperty<nsRect>);
881 void DisplayPortUtils::SetDisplayPortBaseIfNotSet(nsIContent* aContent,
882 const nsRect& aBase) {
883 if (!aContent->GetProperty(nsGkAtoms::DisplayPortBase)) {
884 SetDisplayPortBase(aContent, aBase);
888 bool DisplayPortUtils::GetCriticalDisplayPort(
889 nsIContent* aContent, nsRect* aResult, const DisplayPortOptions& aOptions) {
890 if (StaticPrefs::layers_low_precision_buffer()) {
891 return GetDisplayPortImpl(aContent, aResult, 1.0f, aOptions);
893 return false;
896 bool DisplayPortUtils::HasCriticalDisplayPort(nsIContent* aContent) {
897 return GetCriticalDisplayPort(aContent, nullptr);
900 bool DisplayPortUtils::GetHighResolutionDisplayPort(
901 nsIContent* aContent, nsRect* aResult, const DisplayPortOptions& aOptions) {
902 if (StaticPrefs::layers_low_precision_buffer()) {
903 return GetCriticalDisplayPort(aContent, aResult, aOptions);
905 return GetDisplayPort(aContent, aResult, aOptions);
908 void DisplayPortUtils::RemoveDisplayPort(nsIContent* aContent) {
909 aContent->RemoveProperty(nsGkAtoms::DisplayPort);
910 aContent->RemoveProperty(nsGkAtoms::DisplayPortMargins);
913 bool DisplayPortUtils::ViewportHasDisplayPort(nsPresContext* aPresContext) {
914 nsIFrame* rootScrollFrame = aPresContext->PresShell()->GetRootScrollFrame();
915 return rootScrollFrame && HasDisplayPort(rootScrollFrame->GetContent());
918 bool DisplayPortUtils::IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame) {
919 // Fixed-pos frames are parented by the viewport frame or the page content
920 // frame. We'll assume that printing/print preview don't have displayports for
921 // their pages!
922 nsIFrame* parent = aFrame->GetParent();
923 if (!parent || parent->GetParent() ||
924 aFrame->StyleDisplay()->mPosition != StylePositionProperty::Fixed) {
925 return false;
927 return ViewportHasDisplayPort(aFrame->PresContext());
930 // We want to this return true for the scroll frame, but not the
931 // scrolled frame (which has the same content).
932 bool DisplayPortUtils::FrameHasDisplayPort(nsIFrame* aFrame,
933 const nsIFrame* aScrolledFrame) {
934 if (!aFrame->GetContent() || !HasDisplayPort(aFrame->GetContent())) {
935 return false;
937 nsIScrollableFrame* sf = do_QueryFrame(aFrame);
938 if (sf) {
939 if (aScrolledFrame && aScrolledFrame != sf->GetScrolledFrame()) {
940 return false;
942 return true;
944 return false;
947 bool DisplayPortUtils::CalculateAndSetDisplayPortMargins(
948 nsIScrollableFrame* aScrollFrame, RepaintMode aRepaintMode) {
949 nsIFrame* frame = do_QueryFrame(aScrollFrame);
950 MOZ_ASSERT(frame);
951 nsIContent* content = frame->GetContent();
952 MOZ_ASSERT(content);
954 FrameMetrics metrics =
955 nsLayoutUtils::CalculateBasicFrameMetrics(aScrollFrame);
956 ScreenMargin displayportMargins = layers::apz::CalculatePendingDisplayPort(
957 metrics, ParentLayerPoint(0.0f, 0.0f));
958 PresShell* presShell = frame->PresContext()->GetPresShell();
960 DisplayPortMargins margins = DisplayPortMargins::ForScrollFrame(
961 aScrollFrame, displayportMargins,
962 Some(metrics.DisplayportPixelsPerCSSPixel()));
964 return SetDisplayPortMargins(content, presShell, margins,
965 ClearMinimalDisplayPortProperty::Yes, 0,
966 aRepaintMode);
969 bool DisplayPortUtils::MaybeCreateDisplayPort(nsDisplayListBuilder* aBuilder,
970 nsIFrame* aScrollFrame,
971 RepaintMode aRepaintMode) {
972 nsIContent* content = aScrollFrame->GetContent();
973 nsIScrollableFrame* scrollableFrame = do_QueryFrame(aScrollFrame);
974 if (!content || !scrollableFrame) {
975 return false;
978 bool haveDisplayPort = HasNonMinimalNonZeroDisplayPort(content);
980 // We perform an optimization where we ensure that at least one
981 // async-scrollable frame (i.e. one that WantsAsyncScroll()) has a
982 // displayport. If that's not the case yet, and we are async-scrollable, we
983 // will get a displayport.
984 if (aBuilder->IsPaintingToWindow() &&
985 nsLayoutUtils::AsyncPanZoomEnabled(aScrollFrame) &&
986 !aBuilder->HaveScrollableDisplayPort() &&
987 scrollableFrame->WantAsyncScroll()) {
988 // If we don't already have a displayport, calculate and set one.
989 if (!haveDisplayPort) {
990 // We only use the viewId for logging purposes, but create it
991 // unconditionally to minimize impact of enabling logging. If we don't
992 // assign a viewId here it will get assigned later anyway so functionally
993 // there should be no difference.
994 ViewID viewId = nsLayoutUtils::FindOrCreateIDFor(content);
995 MOZ_LOG(
996 sDisplayportLog, LogLevel::Debug,
997 ("Setting DP on first-encountered scrollId=%" PRIu64 "\n", viewId));
999 CalculateAndSetDisplayPortMargins(scrollableFrame, aRepaintMode);
1000 #ifdef DEBUG
1001 haveDisplayPort = HasNonMinimalDisplayPort(content);
1002 MOZ_ASSERT(haveDisplayPort,
1003 "should have a displayport after having just set it");
1004 #endif
1007 // Record that the we now have a scrollable display port.
1008 aBuilder->SetHaveScrollableDisplayPort();
1009 return true;
1011 return false;
1013 void DisplayPortUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
1014 nsIFrame* aFrame) {
1015 nsIFrame* frame = aFrame;
1016 while (frame) {
1017 frame = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(frame);
1018 if (!frame) {
1019 break;
1021 nsIScrollableFrame* scrollAncestor =
1022 nsLayoutUtils::GetAsyncScrollableAncestorFrame(frame);
1023 if (!scrollAncestor) {
1024 break;
1026 frame = do_QueryFrame(scrollAncestor);
1027 MOZ_ASSERT(frame);
1028 MOZ_ASSERT(scrollAncestor->WantAsyncScroll() ||
1029 frame->PresShell()->GetRootScrollFrame() == frame);
1030 if (nsLayoutUtils::AsyncPanZoomEnabled(frame) &&
1031 !HasDisplayPort(frame->GetContent())) {
1032 SetDisplayPortMargins(frame->GetContent(), frame->PresShell(),
1033 DisplayPortMargins::Empty(frame->GetContent()),
1034 ClearMinimalDisplayPortProperty::No, 0,
1035 RepaintMode::Repaint);
1040 bool DisplayPortUtils::MaybeCreateDisplayPortInFirstScrollFrameEncountered(
1041 nsIFrame* aFrame, nsDisplayListBuilder* aBuilder) {
1042 nsIScrollableFrame* sf = do_QueryFrame(aFrame);
1043 if (sf) {
1044 if (MaybeCreateDisplayPort(aBuilder, aFrame, RepaintMode::Repaint)) {
1045 return true;
1048 if (aFrame->IsPlaceholderFrame()) {
1049 nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(aFrame);
1050 if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(
1051 placeholder->GetOutOfFlowFrame(), aBuilder)) {
1052 return true;
1055 if (aFrame->IsSubDocumentFrame()) {
1056 PresShell* presShell = static_cast<nsSubDocumentFrame*>(aFrame)
1057 ->GetSubdocumentPresShellForPainting(0);
1058 nsIFrame* root = presShell ? presShell->GetRootFrame() : nullptr;
1059 if (root) {
1060 if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(root, aBuilder)) {
1061 return true;
1065 if (aFrame->IsDeckFrame()) {
1066 // only descend the visible card of a decks
1067 nsIFrame* child = static_cast<nsDeckFrame*>(aFrame)->GetSelectedBox();
1068 if (child) {
1069 return MaybeCreateDisplayPortInFirstScrollFrameEncountered(child,
1070 aBuilder);
1074 for (nsIFrame* child : aFrame->PrincipalChildList()) {
1075 if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(child, aBuilder)) {
1076 return true;
1080 return false;
1083 void DisplayPortUtils::ExpireDisplayPortOnAsyncScrollableAncestor(
1084 nsIFrame* aFrame) {
1085 nsIFrame* frame = aFrame;
1086 while (frame) {
1087 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
1088 if (!frame) {
1089 break;
1091 nsIScrollableFrame* scrollAncestor =
1092 nsLayoutUtils::GetAsyncScrollableAncestorFrame(frame);
1093 if (!scrollAncestor) {
1094 break;
1096 frame = do_QueryFrame(scrollAncestor);
1097 MOZ_ASSERT(frame);
1098 if (!frame) {
1099 break;
1101 MOZ_ASSERT(scrollAncestor->WantAsyncScroll() ||
1102 frame->PresShell()->GetRootScrollFrame() == frame);
1103 if (HasDisplayPort(frame->GetContent())) {
1104 scrollAncestor->TriggerDisplayPortExpiration();
1105 // Stop after the first trigger. If it failed, there's no point in
1106 // continuing because all the rest of the frames we encounter are going
1107 // to be ancestors of |scrollAncestor| which will keep its displayport.
1108 // If the trigger succeeded, we stop because when the trigger executes
1109 // it will call this function again to trigger the next ancestor up the
1110 // chain.
1111 break;
1116 static PresShell* GetPresShell(const nsIContent* aContent) {
1117 if (dom::Document* doc = aContent->GetComposedDoc()) {
1118 return doc->GetPresShell();
1120 return nullptr;
1123 static void UpdateDisplayPortMarginsForPendingMetrics(
1124 const RepaintRequest& aMetrics) {
1125 nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());
1126 if (!content) {
1127 return;
1130 RefPtr<PresShell> presShell = GetPresShell(content);
1131 if (!presShell) {
1132 return;
1135 if (nsLayoutUtils::AllowZoomingForDocument(presShell->GetDocument()) &&
1136 aMetrics.IsRootContent()) {
1137 // See APZCCallbackHelper::UpdateRootFrame for details.
1138 float presShellResolution = presShell->GetResolution();
1139 if (presShellResolution != aMetrics.GetPresShellResolution()) {
1140 return;
1144 nsIScrollableFrame* frame =
1145 nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());
1147 if (!frame) {
1148 return;
1151 if (APZCCallbackHelper::IsScrollInProgress(frame)) {
1152 // If these conditions are true, then the UpdateFrame
1153 // message may be ignored by the main-thread, so we
1154 // shouldn't update the displayport based on it.
1155 return;
1158 DisplayPortMarginsPropertyData* currentData =
1159 static_cast<DisplayPortMarginsPropertyData*>(
1160 content->GetProperty(nsGkAtoms::DisplayPortMargins));
1161 if (!currentData) {
1162 return;
1165 CSSPoint frameScrollOffset =
1166 CSSPoint::FromAppUnits(frame->GetScrollPosition());
1168 DisplayPortUtils::SetDisplayPortMargins(
1169 content, presShell,
1170 DisplayPortMargins::FromAPZ(
1171 aMetrics.GetDisplayPortMargins(), aMetrics.GetVisualScrollOffset(),
1172 frameScrollOffset, aMetrics.DisplayportPixelsPerCSSPixel()),
1173 DisplayPortUtils::ClearMinimalDisplayPortProperty::No, 0);
1176 /* static */
1177 void DisplayPortUtils::UpdateDisplayPortMarginsFromPendingMessages() {
1178 if (XRE_IsContentProcess() && layers::CompositorBridgeChild::Get() &&
1179 layers::CompositorBridgeChild::Get()->GetIPCChannel()) {
1180 layers::CompositorBridgeChild::Get()->GetIPCChannel()->PeekMessages(
1181 [](const IPC::Message& aMsg) -> bool {
1182 if (aMsg.type() == layers::PAPZ::Msg_RequestContentRepaint__ID) {
1183 PickleIterator iter(aMsg);
1184 RepaintRequest request;
1185 if (!IPC::ReadParam(&aMsg, &iter, &request)) {
1186 MOZ_ASSERT(false);
1187 return true;
1190 UpdateDisplayPortMarginsForPendingMetrics(request);
1192 return true;
1197 Maybe<nsRect> DisplayPortUtils::GetRootDisplayportBase(PresShell* aPresShell) {
1198 DebugOnly<nsPresContext*> pc = aPresShell->GetPresContext();
1199 MOZ_ASSERT(pc, "this function should be called after PresShell::Init");
1200 MOZ_ASSERT(pc->IsRootContentDocumentCrossProcess() ||
1201 !pc->GetParentPresContext());
1203 dom::BrowserChild* browserChild = dom::BrowserChild::GetFrom(aPresShell);
1204 if (browserChild && !browserChild->IsTopLevel()) {
1205 // If this is an in-process root in on OOP iframe, use the visible rect if
1206 // it's been set.
1207 return browserChild->GetVisibleRect();
1210 nsIFrame* frame = aPresShell->GetRootScrollFrame();
1211 if (!frame) {
1212 frame = aPresShell->GetRootFrame();
1215 nsRect baseRect;
1216 if (frame) {
1217 baseRect = nsRect(nsPoint(0, 0),
1218 nsLayoutUtils::CalculateCompositionSizeForFrame(frame));
1219 } else {
1220 baseRect = nsRect(nsPoint(0, 0),
1221 aPresShell->GetPresContext()->GetVisibleArea().Size());
1224 return Some(baseRect);
1227 } // namespace mozilla