nsDisplayList.cpp (335287B)
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/. 6 */ 7 8 /* 9 * structures that represent things to be painted (ordered in z-order), 10 * used during painting and hit testing 11 */ 12 13 #include "nsDisplayList.h" 14 15 #include <stdint.h> 16 17 #include <algorithm> 18 #include <limits> 19 20 #include "ActiveLayerTracker.h" 21 #include "BorderConsts.h" 22 #include "ImageContainer.h" 23 #include "LayerAnimationInfo.h" 24 #include "StickyScrollContainer.h" 25 #include "TextDrawTarget.h" 26 #include "UnitTransforms.h" 27 #include "gfxContext.h" 28 #include "gfxMatrix.h" 29 #include "gfxUtils.h" 30 #include "imgIContainer.h" 31 #include "mozilla/AnimationPerformanceWarning.h" 32 #include "mozilla/AnimationUtils.h" 33 #include "mozilla/AutoRestore.h" 34 #include "mozilla/DisplayPortUtils.h" 35 #include "mozilla/EffectCompositor.h" 36 #include "mozilla/EffectSet.h" 37 #include "mozilla/EventStateManager.h" 38 #include "mozilla/Likely.h" 39 #include "mozilla/LookAndFeel.h" 40 #include "mozilla/OperatorNewExtensions.h" 41 #include "mozilla/Preferences.h" 42 #include "mozilla/PresShell.h" 43 #include "mozilla/ProfilerLabels.h" 44 #include "mozilla/ProfilerMarkers.h" 45 #include "mozilla/SVGClipPathFrame.h" 46 #include "mozilla/SVGIntegrationUtils.h" 47 #include "mozilla/SVGMaskFrame.h" 48 #include "mozilla/SVGObserverUtils.h" 49 #include "mozilla/SVGUtils.h" 50 #include "mozilla/ScrollContainerFrame.h" 51 #include "mozilla/ServoBindings.h" 52 #include "mozilla/ShapeUtils.h" 53 #include "mozilla/StaticPrefs_apz.h" 54 #include "mozilla/StaticPrefs_gfx.h" 55 #include "mozilla/StaticPrefs_layers.h" 56 #include "mozilla/StaticPrefs_layout.h" 57 #include "mozilla/StaticPrefs_print.h" 58 #include "mozilla/StyleAnimationValue.h" 59 #include "mozilla/UniquePtr.h" 60 #include "mozilla/ViewportFrame.h" 61 #include "mozilla/ViewportUtils.h" 62 #include "mozilla/dom/BrowserChild.h" 63 #include "mozilla/dom/HTMLCanvasElement.h" 64 #include "mozilla/dom/PerformanceMainThread.h" 65 #include "mozilla/dom/RemoteBrowser.h" 66 #include "mozilla/dom/SVGElement.h" 67 #include "mozilla/dom/Selection.h" 68 #include "mozilla/dom/ServiceWorkerRegistrar.h" 69 #include "mozilla/dom/ServiceWorkerRegistration.h" 70 #include "mozilla/dom/TouchEvent.h" 71 #include "mozilla/dom/ViewTransition.h" 72 #include "mozilla/gfx/2D.h" 73 #include "mozilla/gfx/gfxVars.h" 74 #include "mozilla/glean/GfxMetrics.h" 75 #include "mozilla/layers/AnimationHelper.h" 76 #include "mozilla/layers/CompositorThread.h" 77 #include "mozilla/layers/InputAPZContext.h" 78 #include "mozilla/layers/RenderRootStateManager.h" 79 #include "mozilla/layers/StackingContextHelper.h" 80 #include "mozilla/layers/TreeTraversal.h" 81 #include "mozilla/layers/WebRenderBridgeChild.h" 82 #include "mozilla/layers/WebRenderLayerManager.h" 83 #include "mozilla/layers/WebRenderMessages.h" 84 #include "mozilla/layers/WebRenderScrollData.h" 85 #include "nsCSSProps.h" 86 #include "nsCSSRendering.h" 87 #include "nsCSSRenderingGradients.h" 88 #include "nsCanvasFrame.h" 89 #include "nsCaret.h" 90 #include "nsCaseTreatment.h" 91 #include "nsDOMTokenList.h" 92 #include "nsEscape.h" 93 #include "nsFocusManager.h" 94 #include "nsIFrameInlines.h" 95 #include "nsImageFrame.h" 96 #include "nsLayoutUtils.h" 97 #include "nsPresContextInlines.h" 98 #include "nsPrintfCString.h" 99 #include "nsRefreshDriver.h" 100 #include "nsRegion.h" 101 #include "nsSliderFrame.h" 102 #include "nsStyleConsts.h" 103 #include "nsStyleStructInlines.h" 104 #include "nsStyleTransformMatrix.h" 105 #include "nsSubDocumentFrame.h" 106 #include "nsTableCellFrame.h" 107 #include "nsTableColFrame.h" 108 #include "nsTextFrame.h" 109 #include "nsTextPaintStyle.h" 110 #include "nsTransitionManager.h" 111 112 namespace mozilla { 113 114 using namespace dom; 115 using namespace gfx; 116 using namespace layout; 117 using namespace layers; 118 using namespace image; 119 120 LazyLogModule sContentDisplayListLog("dl.content"); 121 LazyLogModule sParentDisplayListLog("dl.parent"); 122 123 LazyLogModule& GetLoggerByProcess() { 124 return XRE_IsContentProcess() ? sContentDisplayListLog 125 : sParentDisplayListLog; 126 } 127 128 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 129 void AssertUniqueItem(nsDisplayItem* aItem) { 130 for (nsDisplayItem* i : aItem->Frame()->DisplayItems()) { 131 if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() && 132 i->GetPerFrameKey() == aItem->GetPerFrameKey()) { 133 if (i->IsPreProcessedItem() || i->IsPreProcessed()) { 134 continue; 135 } 136 MOZ_DIAGNOSTIC_CRASH("Duplicate display item!"); 137 } 138 } 139 } 140 #endif 141 142 bool ShouldBuildItemForEvents(const DisplayItemType aType) { 143 return aType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO || 144 aType == DisplayItemType::TYPE_REMOTE || 145 (GetDisplayItemFlagsForType(aType) & TYPE_IS_CONTAINER); 146 } 147 148 static bool ItemTypeSupportsHitTesting(const DisplayItemType aType) { 149 switch (aType) { 150 case DisplayItemType::TYPE_BACKGROUND: 151 case DisplayItemType::TYPE_BACKGROUND_COLOR: 152 case DisplayItemType::TYPE_THEMED_BACKGROUND: 153 return true; 154 default: 155 return false; 156 } 157 } 158 159 void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder, 160 nsPaintedDisplayItem* aItem, 161 const DisplayItemType aType) { 162 if (ItemTypeSupportsHitTesting(aType)) { 163 aItem->InitializeHitTestInfo(aBuilder); 164 } 165 } 166 167 /* static */ 168 already_AddRefed<ActiveScrolledRoot> ActiveScrolledRoot::GetOrCreateASRForFrame( 169 const ActiveScrolledRoot* aParent, 170 ScrollContainerFrame* aScrollContainerFrame, 171 nsTArray<RefPtr<ActiveScrolledRoot>>& aActiveScrolledRoots) { 172 RefPtr<ActiveScrolledRoot> asr = 173 aScrollContainerFrame->GetProperty(ActiveScrolledRootCache()); 174 175 #ifdef DEBUG 176 if (asr && aActiveScrolledRoots.Contains(asr)) { 177 // If this is the second time we are called for this frame in this same 178 // paint, assert that we aren't changing any of the values. (The values can 179 // change *between* paints, but not during one paint.) 180 MOZ_ASSERT(asr->mParent == aParent); 181 MOZ_ASSERT(asr->mFrame == aScrollContainerFrame); 182 MOZ_ASSERT(asr->mKind == ASRKind::Scroll); 183 MOZ_ASSERT(asr->mDepth == (aParent ? aParent->mDepth + 1 : 1)); 184 } 185 #endif 186 187 if (!asr) { 188 asr = new ActiveScrolledRoot(); 189 190 RefPtr<ActiveScrolledRoot> ref = asr; 191 aScrollContainerFrame->SetProperty(ActiveScrolledRootCache(), 192 ref.forget().take()); 193 aActiveScrolledRoots.AppendElement(asr); 194 } 195 asr->mParent = aParent; 196 asr->mFrame = aScrollContainerFrame; 197 asr->mKind = ASRKind::Scroll; 198 asr->mDepth = aParent ? aParent->mDepth + 1 : 1; 199 200 return asr.forget(); 201 } 202 203 /* static */ 204 already_AddRefed<ActiveScrolledRoot> 205 ActiveScrolledRoot::GetOrCreateASRForStickyFrame( 206 const ActiveScrolledRoot* aParent, nsIFrame* aStickyFrame, 207 nsTArray<RefPtr<ActiveScrolledRoot>>& aActiveScrolledRoots) { 208 aStickyFrame = aStickyFrame->FirstContinuation(); 209 210 RefPtr<ActiveScrolledRoot> asr = 211 aStickyFrame->GetProperty(StickyActiveScrolledRootCache()); 212 213 #ifdef DEBUG 214 if (asr && aActiveScrolledRoots.Contains(asr)) { 215 // If this is the second time we are called for this frame in this same 216 // paint, assert that we aren't changing any of the values. (The values can 217 // change *between* paints, but not during one paint.) 218 MOZ_ASSERT(asr->mParent == aParent); 219 MOZ_ASSERT(asr->mFrame == aStickyFrame); 220 MOZ_ASSERT(asr->mKind == ASRKind::Sticky); 221 MOZ_ASSERT(asr->mDepth == (aParent ? aParent->mDepth + 1 : 1)); 222 } 223 #endif 224 225 if (!asr) { 226 asr = new ActiveScrolledRoot(); 227 228 RefPtr<ActiveScrolledRoot> ref = asr; 229 aStickyFrame->SetProperty(StickyActiveScrolledRootCache(), 230 ref.forget().take()); 231 aActiveScrolledRoots.AppendElement(asr); 232 } 233 234 asr->mParent = aParent; 235 asr->mFrame = aStickyFrame; 236 asr->mKind = ASRKind::Sticky; 237 asr->mDepth = aParent ? aParent->mDepth + 1 : 1; 238 239 return asr.forget(); 240 } 241 242 /* static */ 243 bool ActiveScrolledRoot::IsAncestor(const ActiveScrolledRoot* aAncestor, 244 const ActiveScrolledRoot* aDescendant) { 245 if (!aAncestor) { 246 // nullptr is the root 247 return true; 248 } 249 if (Depth(aAncestor) > Depth(aDescendant)) { 250 return false; 251 } 252 const ActiveScrolledRoot* asr = aDescendant; 253 while (asr) { 254 if (asr == aAncestor) { 255 return true; 256 } 257 asr = asr->mParent; 258 } 259 return false; 260 } 261 262 /* static */ 263 bool ActiveScrolledRoot::IsProperAncestor( 264 const ActiveScrolledRoot* aAncestor, 265 const ActiveScrolledRoot* aDescendant) { 266 return aAncestor != aDescendant && IsAncestor(aAncestor, aDescendant); 267 } 268 269 ScrollContainerFrame* ActiveScrolledRoot::ScrollFrameOrNull() const { 270 if (mKind == ASRKind::Scroll) { 271 ScrollContainerFrame* scrollFrame = 272 static_cast<ScrollContainerFrame*>(mFrame); 273 MOZ_ASSERT(scrollFrame); 274 return scrollFrame; 275 } 276 return nullptr; 277 } 278 279 const ActiveScrolledRoot* ActiveScrolledRoot::GetNearestScrollASR() const { 280 const ActiveScrolledRoot* ret = this; 281 282 while (ret && ret->mKind != ASRKind::Scroll) { 283 ret = ret->mParent; 284 } 285 286 if (!ret || ret->mKind != ASRKind::Scroll) { 287 return nullptr; 288 } 289 290 return ret; 291 } 292 293 layers::ScrollableLayerGuid::ViewID 294 ActiveScrolledRoot::GetNearestScrollASRViewId() const { 295 const ActiveScrolledRoot* scrollASR = GetNearestScrollASR(); 296 if (scrollASR) { 297 return scrollASR->GetViewId(); 298 } 299 return ScrollableLayerGuid::NULL_SCROLL_ID; 300 } 301 302 /* static */ 303 const ActiveScrolledRoot* ActiveScrolledRoot::GetStickyASRFromFrame( 304 nsIFrame* aStickyFrame) { 305 return aStickyFrame->FirstContinuation()->GetProperty( 306 StickyActiveScrolledRootCache()); 307 } 308 309 /* static */ 310 nsCString ActiveScrolledRoot::ToString( 311 const ActiveScrolledRoot* aActiveScrolledRoot) { 312 nsAutoCString str; 313 if (!aActiveScrolledRoot) { 314 str.AppendPrintf("null"); 315 return str; 316 } 317 if (aActiveScrolledRoot->mKind == ASRKind::Sticky) { 318 str.AppendPrintf("sticky "); 319 } 320 for (const auto* asr = aActiveScrolledRoot; asr; asr = asr->mParent) { 321 str.AppendPrintf("<0x%p>", asr->mFrame); 322 if (asr->mParent) { 323 str.AppendLiteral(", "); 324 } 325 } 326 return std::move(str); 327 } 328 329 ScrollableLayerGuid::ViewID ActiveScrolledRoot::ComputeViewId() const { 330 const ActiveScrolledRoot* scrollASR = GetNearestScrollASR(); 331 MOZ_ASSERT(scrollASR, 332 "ComputeViewId() called on ASR with no enclosing scroll frame"); 333 nsIContent* content = scrollASR->ScrollFrame()->GetContent(); 334 return nsLayoutUtils::FindOrCreateIDFor(content); 335 } 336 337 ActiveScrolledRoot::~ActiveScrolledRoot() { 338 if (mFrame) { 339 mFrame->RemoveProperty(mKind == ASRKind::Sticky 340 ? StickyActiveScrolledRootCache() 341 : ActiveScrolledRootCache()); 342 } 343 } 344 345 static uint64_t AddAnimationsForWebRender( 346 nsDisplayItem* aItem, RenderRootStateManager* aManager, 347 nsDisplayListBuilder* aDisplayListBuilder, 348 const Maybe<LayoutDevicePoint>& aPosition = Nothing()) { 349 auto* effects = EffectSet::GetForFrame(aItem->Frame(), aItem->GetType()); 350 if (!effects || effects->IsEmpty()) { 351 // If there is no animation on the nsIFrame, that means 352 // 1) we've never created any animations on this frame or 353 // 2) the frame was reconstruced or 354 // 3) all animations on the frame have finished 355 // in such cases we don't need do anything here. 356 // 357 // Even if there is a WebRenderAnimationData for the display item type on 358 // this frame, it's going to be discarded since it's not marked as being 359 // used. 360 return 0; 361 } 362 363 RefPtr<WebRenderAnimationData> animationData = 364 aManager->CommandBuilder() 365 .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(aItem); 366 AnimationInfo& animationInfo = animationData->GetAnimationInfo(); 367 nsIFrame* frame = aItem->Frame(); 368 animationInfo.AddAnimationsForDisplayItem( 369 frame, aDisplayListBuilder, aItem, aItem->GetType(), 370 aManager->LayerManager(), aPosition); 371 372 // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there 373 // are no active animations. 374 uint64_t animationsId = animationInfo.GetCompositorAnimationsId(); 375 if (!animationInfo.GetAnimations().IsEmpty()) { 376 OpAddCompositorAnimations anim( 377 CompositorAnimations(animationInfo.GetAnimations(), animationsId)); 378 aManager->WrBridge()->AddWebRenderParentCommand(anim); 379 aManager->AddActiveCompositorAnimationId(animationsId); 380 } else if (animationsId) { 381 aManager->AddCompositorAnimationsIdForDiscard(animationsId); 382 animationsId = 0; 383 } 384 385 return animationsId; 386 } 387 388 static bool GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext, 389 const nsRect& aFillRect, 390 nsDisplayListBuilder* aBuilder) { 391 if (aBuilder->IsForGenerateGlyphMask()) { 392 return false; 393 } 394 395 SVGObserverUtils::GetAndObserveBackgroundClip(aFrame); 396 397 // The main function of enabling background-clip:text property value. 398 // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call 399 // this function to 400 // 1. Generate a mask by all descendant text frames 401 // 2. Push the generated mask into aContext. 402 403 gfxContext* sourceCtx = aContext; 404 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits( 405 aFillRect, aFrame->PresContext()->AppUnitsPerDevPixel()); 406 407 // Create a mask surface. 408 RefPtr<DrawTarget> sourceTarget = sourceCtx->GetDrawTarget(); 409 RefPtr<DrawTarget> maskDT = sourceTarget->CreateClippedDrawTarget( 410 bounds.ToUnknownRect(), SurfaceFormat::A8); 411 if (!maskDT || !maskDT->IsValid()) { 412 return false; 413 } 414 gfxContext maskCtx(maskDT, /* aPreserveTransform */ true); 415 maskCtx.Multiply(Matrix::Translation(bounds.TopLeft().ToUnknownPoint())); 416 417 // Shade text shape into mask A8 surface. 418 nsLayoutUtils::PaintFrame( 419 &maskCtx, aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()), 420 NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GenerateGlyph); 421 422 // Push the generated mask into aContext, so that the caller can pop and 423 // blend with it. 424 425 Matrix currentMatrix = sourceCtx->CurrentMatrix(); 426 Matrix invCurrentMatrix = currentMatrix; 427 invCurrentMatrix.Invert(); 428 429 RefPtr<SourceSurface> maskSurface = maskDT->Snapshot(); 430 sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0, 431 maskSurface, invCurrentMatrix); 432 433 return true; 434 } 435 436 nsDisplayWrapper* nsDisplayWrapList::CreateShallowCopy( 437 nsDisplayListBuilder* aBuilder) { 438 const nsDisplayWrapList* wrappedItem = AsDisplayWrapList(); 439 MOZ_ASSERT(wrappedItem); 440 441 // Create a new nsDisplayWrapList using a copy-constructor. This is done 442 // to preserve the information about bounds. 443 nsDisplayWrapper* wrapper = 444 new (aBuilder) nsDisplayWrapper(aBuilder, *wrappedItem); 445 wrapper->SetType(nsDisplayWrapper::ItemType()); 446 MOZ_ASSERT(wrapper); 447 448 // Set the display list pointer of the new wrapper item to the display list 449 // of the wrapped item. 450 wrapper->mListPtr = wrappedItem->mListPtr; 451 return wrapper; 452 } 453 454 nsDisplayWrapList* nsDisplayListBuilder::MergeItems( 455 nsTArray<nsDisplayItem*>& aItems) { 456 // For merging, we create a temporary item by cloning the last item of the 457 // mergeable items list. This ensures that the temporary item will have the 458 // correct frame and bounds. 459 nsDisplayWrapList* last = aItems.PopLastElement()->AsDisplayWrapList(); 460 MOZ_ASSERT(last); 461 nsDisplayWrapList* merged = last->Clone(this); 462 MOZ_ASSERT(merged); 463 AddTemporaryItem(merged); 464 465 // Create nsDisplayWrappers that point to the internal display lists of the 466 // items we are merging. These nsDisplayWrappers are added to the display list 467 // of the temporary item. 468 for (nsDisplayItem* item : aItems) { 469 MOZ_ASSERT(item); 470 MOZ_ASSERT(merged->CanMerge(item)); 471 merged->Merge(item); 472 MOZ_ASSERT(item->AsDisplayWrapList()); 473 merged->GetChildren()->AppendToTop( 474 static_cast<nsDisplayWrapList*>(item)->CreateShallowCopy(this)); 475 } 476 477 merged->GetChildren()->AppendToTop(last->CreateShallowCopy(this)); 478 479 return merged; 480 } 481 482 // FIXME(emilio): This whole business should ideally not be needed at all, but 483 // there are a variety of hard-to-deal-with caret invalidation issues, like 484 // bug 1888583, and caret changes are relatively uncommon, enough that it 485 // probably isn't worth chasing all them down. 486 void nsDisplayListBuilder::InvalidateCaretFramesIfNeeded() { 487 if (mPaintedCarets.IsEmpty()) { 488 return; 489 } 490 size_t i = mPaintedCarets.Length(); 491 while (i--) { 492 nsCaret* caret = mPaintedCarets[i]; 493 nsIFrame* oldCaret = caret->GetLastPaintedFrame(); 494 nsIFrame* currentCaret = caret->GetPaintGeometry(); 495 if (oldCaret == currentCaret) { 496 // Keep tracking this caret, it hasn't changed. 497 continue; 498 } 499 if (oldCaret) { 500 oldCaret->MarkNeedsDisplayItemRebuild(); 501 } 502 if (currentCaret) { 503 currentCaret->MarkNeedsDisplayItemRebuild(); 504 } 505 // If / when we paint this caret, we'll track it again. 506 caret->SetLastPaintedFrame(nullptr); 507 mPaintedCarets.RemoveElementAt(i); 508 } 509 } 510 511 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter:: 512 SetCurrentActiveScrolledRoot( 513 const ActiveScrolledRoot* aActiveScrolledRoot) { 514 MOZ_ASSERT(!mUsed); 515 516 // Set the builder's mCurrentActiveScrolledRoot. 517 mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot; 518 519 // We also need to adjust the builder's mCurrentContainerASR. 520 // mCurrentContainerASR needs to be an ASR that all the container's 521 // contents have finite bounds with respect to. If aActiveScrolledRoot 522 // is an ancestor ASR of mCurrentContainerASR, that means we need to 523 // set mCurrentContainerASR to aActiveScrolledRoot, because otherwise 524 // the items that will be created with aActiveScrolledRoot wouldn't 525 // have finite bounds with respect to mCurrentContainerASR. There's one 526 // exception, in the case where there's a content clip on the builder 527 // that is scrolled by a descendant ASR of aActiveScrolledRoot. This 528 // content clip will clip all items that are created while this 529 // AutoCurrentActiveScrolledRootSetter exists. This means that the items 530 // created during our lifetime will have finite bounds with respect to 531 // the content clip's ASR, even if the items' actual ASR is an ancestor 532 // of that. And it also means that mCurrentContainerASR only needs to be 533 // set to the content clip's ASR and not all the way to aActiveScrolledRoot. 534 // This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html 535 // and fixed-pos-scrolled-clip-opacity-inside-layerize.html. 536 537 // finiteBoundsASR is the leafmost ASR that all items created during 538 // object's lifetime have finite bounds with respect to. 539 // TODO(bug 2001862): Explanation may need revising. 540 const ActiveScrolledRoot* finiteBoundsASR = aActiveScrolledRoot; 541 if (!mBuilder->IsInViewTransitionCapture()) { 542 finiteBoundsASR = 543 ActiveScrolledRoot::IsAncestor(aActiveScrolledRoot, mContentClipASR) 544 ? mContentClipASR 545 : aActiveScrolledRoot; 546 } 547 548 // mCurrentContainerASR is adjusted so that it's still an ancestor of 549 // finiteBoundsASR. 550 mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor( 551 mBuilder->mCurrentContainerASR, finiteBoundsASR); 552 553 // If we are entering out-of-flow content inside a CSS filter, mark 554 // scroll frames wrt. which the content is fixed as containing such content. 555 if (mBuilder->mFilterASR && ActiveScrolledRoot::IsAncestor( 556 aActiveScrolledRoot, mBuilder->mFilterASR)) { 557 for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR; 558 asr && asr != aActiveScrolledRoot; asr = asr->mParent) { 559 if (ScrollContainerFrame* scrollFrame = asr->ScrollFrameOrNull()) { 560 scrollFrame->SetHasOutOfFlowContentInsideFilter(); 561 } 562 } 563 } 564 565 mUsed = true; 566 } 567 568 void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter:: 569 InsertScrollFrame(ScrollContainerFrame* aScrollContainerFrame) { 570 MOZ_ASSERT(!mUsed); 571 size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length(); 572 const ActiveScrolledRoot* parentASR = mBuilder->mCurrentActiveScrolledRoot; 573 const ActiveScrolledRoot* asr = 574 mBuilder->GetOrCreateActiveScrolledRoot(parentASR, aScrollContainerFrame); 575 mBuilder->mCurrentActiveScrolledRoot = asr; 576 577 // All child ASRs of parentASR that were created while this 578 // AutoCurrentActiveScrolledRootSetter object was on the stack belong to us 579 // now. Reparent them to asr. 580 for (size_t i = mDescendantsStartIndex; i < descendantsEndIndex; i++) { 581 ActiveScrolledRoot* descendantASR = mBuilder->mActiveScrolledRoots[i]; 582 if (ActiveScrolledRoot::IsAncestor(parentASR, descendantASR)) { 583 descendantASR->IncrementDepth(); 584 if (descendantASR->mParent == parentASR) { 585 descendantASR->mParent = asr; 586 } 587 } 588 } 589 590 mUsed = true; 591 } 592 593 nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker( 594 nsDisplayListBuilder* aBuilder) 595 : mBuilder(aBuilder), mSavedContainerASR(aBuilder->mCurrentContainerASR) { 596 mBuilder->mCurrentContainerASR = mBuilder->mCurrentActiveScrolledRoot; 597 } 598 599 nsPresContext* nsDisplayListBuilder::CurrentPresContext() { 600 return CurrentPresShellState()->mPresShell->GetPresContext(); 601 } 602 603 /* static */ 604 nsRect nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame( 605 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, 606 const nsRect& aVisibleRect, const nsRect& aDirtyRect, 607 nsRect* aOutDirtyRect) { 608 nsRect visible = aVisibleRect; 609 nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect; 610 611 bool inPartialUpdate = 612 aBuilder->IsRetainingDisplayList() && aBuilder->IsPartialUpdate(); 613 if (MOZ_LIKELY(StaticPrefs::apz_allow_zooming()) && 614 aBuilder->IsPaintingToWindow() && !inPartialUpdate && 615 DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame)) { 616 dirtyRectRelativeToDirtyFrame = 617 nsRect(nsPoint(0, 0), aFrame->GetParent()->GetSize()); 618 619 // If there's a visual viewport size set, restrict the amount of the 620 // fixed-position element we paint to the visual viewport. (In general 621 // the fixed-position element can be as large as the layout viewport, 622 // which at a high zoom level can cause us to paint too large of an 623 // area.) 624 PresShell* presShell = aFrame->PresShell(); 625 if (presShell->IsVisualViewportSizeSet()) { 626 dirtyRectRelativeToDirtyFrame = 627 nsRect(presShell->GetVisualViewportOffsetRelativeToLayoutViewport(), 628 presShell->GetVisualViewportSize()); 629 // But if we have a displayport, expand it to the displayport, so 630 // that async-scrolling the visual viewport within the layout viewport 631 // will not checkerboard. 632 if (nsIFrame* rootScrollContainerFrame = 633 presShell->GetRootScrollContainerFrame()) { 634 nsRect displayport; 635 // Note that the displayport here is already in the right coordinate 636 // space: it's relative to the scroll port (= layout viewport), but 637 // covers the visual viewport with some margins around it, which is 638 // exactly what we want. 639 if (DisplayPortUtils::GetDisplayPort( 640 rootScrollContainerFrame->GetContent(), &displayport, 641 DisplayPortOptions().With(ContentGeometryType::Fixed))) { 642 dirtyRectRelativeToDirtyFrame = displayport; 643 } 644 } 645 } 646 visible = dirtyRectRelativeToDirtyFrame; 647 if (StaticPrefs::apz_test_logging_enabled() && 648 presShell->GetDocument()->IsContentDocument()) { 649 nsLayoutUtils::LogAdditionalTestData( 650 aBuilder, "fixedPosDisplayport", 651 ToString(CSSSize::FromAppUnits(visible))); 652 } 653 } 654 655 *aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition(); 656 visible -= aFrame->GetPosition(); 657 658 nsRect overflowRect = aFrame->InkOverflowRect(); 659 660 if (aFrame->IsTransformed() && EffectCompositor::HasAnimationsForCompositor( 661 aFrame, DisplayItemType::TYPE_TRANSFORM)) { 662 /** 663 * Add a fuzz factor to the overflow rectangle so that elements only 664 * just out of view are pulled into the display list, so they can be 665 * prerendered if necessary. 666 */ 667 overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32)); 668 } 669 670 visible.IntersectRect(visible, overflowRect); 671 aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect); 672 673 return visible; 674 } 675 676 nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder* aBuilder, 677 nsIFrame* aFrame, 678 nsDisplayList* aList) 679 : mList(aList) { 680 // Find the element that we need to check for link-ness, bailing out if 681 // we can't find one. 682 Element* elem = Element::FromNodeOrNull(aFrame->GetContent()); 683 if (!elem) { 684 return; 685 } 686 687 // If the element has an id and/or name attribute, generate a destination 688 // for possible internal linking. 689 auto maybeGenerateDest = [&](const nsAtom* aAttr) { 690 nsAutoString attrValue; 691 elem->GetAttr(aAttr, attrValue); 692 if (!attrValue.IsEmpty()) { 693 NS_ConvertUTF16toUTF8 dest(attrValue); 694 // Ensure that we only emit a given destination once, although there may 695 // be multiple frames associated with a given element; we'll simply use 696 // the first of them as the target of any links to it. 697 // XXX(jfkthame) This prevents emitting duplicate destinations *on the 698 // same page*, but does not prevent duplicates on subsequent pages, as 699 // each new page is handled by a new temporary DisplayListBuilder. This 700 // seems to be harmless in practice, though a bit wasteful of space. To 701 // fix, we need to maintain the set of already-seen destinations globally 702 // for the print job, rather than attached to the (per-page) builder. 703 if (aBuilder->mDestinations.EnsureInserted(dest)) { 704 auto* destination = MakeDisplayItem<nsDisplayDestination>( 705 aBuilder, aFrame, dest.get(), aFrame->GetRect().TopLeft()); 706 mList->AppendToTop(destination); 707 } 708 } 709 }; 710 711 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) { 712 if (elem->HasID()) { 713 maybeGenerateDest(nsGkAtoms::id); 714 } 715 if (elem->HasName()) { 716 maybeGenerateDest(nsGkAtoms::name); 717 } 718 } 719 720 // Links don't nest, so if the builder already has a destination, no need to 721 // check for a link element here. 722 if (!aBuilder->mLinkURI.IsEmpty() || !aBuilder->mLinkDest.IsEmpty()) { 723 return; 724 } 725 726 // Check if we have actually found a link. 727 if (!elem->IsLink()) { 728 return; 729 } 730 731 nsCOMPtr<nsIURI> uri = elem->GetHrefURI(); 732 if (!uri) { 733 return; 734 } 735 736 // Is it potentially a local (in-document) destination? 737 bool hasRef, eqExRef; 738 nsIURI* docURI; 739 if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() && 740 NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef && 741 (docURI = aFrame->PresContext()->Document()->GetDocumentURI()) && 742 NS_SUCCEEDED(uri->EqualsExceptRef(docURI, &eqExRef)) && eqExRef) { 743 // Try to get a local destination name. If this fails, we'll leave the 744 // mLinkDest string empty, but still try to set mLinkURI below. 745 if (NS_FAILED(uri->GetRef(aBuilder->mLinkDest))) { 746 aBuilder->mLinkDest.Truncate(); 747 } 748 // The destination name is simply a string; we don't want URL-escaping 749 // applied to it. 750 if (!aBuilder->mLinkDest.IsEmpty()) { 751 NS_UnescapeURL(aBuilder->mLinkDest); 752 } 753 } 754 755 if (NS_FAILED(uri->GetSpec(aBuilder->mLinkURI))) { 756 aBuilder->mLinkURI.Truncate(); 757 } 758 759 // If we didn't get either kind of destination, we won't try to linkify at 760 // this level. 761 if (aBuilder->mLinkDest.IsEmpty() && aBuilder->mLinkURI.IsEmpty()) { 762 return; 763 } 764 765 // Record that we need to reset the builder's state on destruction. 766 mBuilderToReset = aBuilder; 767 } 768 769 void nsDisplayListBuilder::Linkifier::MaybeAppendLink( 770 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) { 771 // Note that we may generate a link here even if the constructor bailed out 772 // without updating aBuilder->mLinkURI/Dest, because it may have been set by 773 // an ancestor that was associated with a link element. 774 if (!aBuilder->mLinkURI.IsEmpty() || !aBuilder->mLinkDest.IsEmpty()) { 775 auto* link = MakeDisplayItem<nsDisplayLink>( 776 aBuilder, aFrame, aBuilder->mLinkDest.get(), aBuilder->mLinkURI.get(), 777 aFrame->GetRect()); 778 mList->AppendToTop(link); 779 } 780 } 781 782 uint32_t nsDisplayListBuilder::sPaintSequenceNumber(1); 783 784 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, 785 nsDisplayListBuilderMode aMode, 786 bool aBuildCaret, 787 bool aRetainingDisplayList) 788 : mReferenceFrame(aReferenceFrame), 789 mIgnoreScrollFrame(nullptr), 790 mCurrentActiveScrolledRoot(nullptr), 791 mCurrentContainerASR(nullptr), 792 mCurrentFrame(aReferenceFrame), 793 mCurrentReferenceFrame(aReferenceFrame), 794 mScrollInfoItemsForHoisting(nullptr), 795 mFirstClipChainToDestroy(nullptr), 796 mTableBackgroundSet(nullptr), 797 mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID), 798 mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID), 799 mFilterASR(nullptr), 800 mDirtyRect(-1, -1, -1, -1), 801 mMode(aMode), 802 mIsBuildingScrollbar(false), 803 mCurrentScrollbarWillHaveLayer(false), 804 mBuildCaret(aBuildCaret), 805 mRetainingDisplayList(aRetainingDisplayList), 806 mPartialUpdate(false), 807 mIgnoreSuppression(false), 808 mIncludeAllOutOfFlows(false), 809 mDescendIntoSubdocuments(true), 810 mSelectedFramesOnly(false), 811 mAllowMergingAndFlattening(true), 812 mInTransform(false), 813 mInEventsOnly(false), 814 mInFilter(false), 815 mInViewTransitionCapture(false), 816 mIsInChromePresContext(false), 817 mSyncDecodeImages(false), 818 mIsPaintingToWindow(false), 819 mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)), 820 mUseHighQualityScaling(false), 821 mIsPaintingForWebRender(false), 822 mAncestorHasApzAwareEventHandler(false), 823 mHaveScrollableDisplayPort(false), 824 mWindowDraggingAllowed(false), 825 mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)), 826 mForceLayerForScrollParent(false), 827 mContainsNonMinimalDisplayPort(false), 828 mBuildingInvisibleItems(false), 829 mIsBuilding(false), 830 mInInvalidSubtree(false), 831 mDisablePartialUpdates(false), 832 mPartialBuildFailed(false), 833 mIsInActiveDocShell(false), 834 mBuildAsyncZoomContainer(false), 835 mIsRelativeToLayoutViewport(false), 836 mUseOverlayScrollbars(false), 837 mAlwaysLayerizeScrollbars(false), 838 mIsDestroying(false) { 839 MOZ_COUNT_CTOR(nsDisplayListBuilder); 840 841 ShouldRebuildDisplayListDueToPrefChange(); 842 843 mUseOverlayScrollbars = 844 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars); 845 846 mAlwaysLayerizeScrollbars = 847 StaticPrefs::layout_scrollbars_always_layerize_track(); 848 849 static_assert( 850 static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS), 851 "Check TYPE_MAX should not overflow"); 852 853 mIsReusingStackingContextItems = 854 mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc(); 855 } 856 857 void nsDisplayListBuilder::BeginFrame() { 858 nsCSSRendering::BeginFrameTreesLocked(); 859 860 mIsPaintingToWindow = false; 861 mUseHighQualityScaling = false; 862 mIgnoreSuppression = false; 863 mInTransform = false; 864 mInFilter = false; 865 mSyncDecodeImages = false; 866 } 867 868 void nsDisplayListBuilder::EndFrame() { 869 NS_ASSERTION(!mInInvalidSubtree, 870 "Someone forgot to cleanup mInInvalidSubtree!"); 871 mCurrentContainerASR = nullptr; 872 mActiveScrolledRoots.Clear(); 873 FreeClipChains(); 874 FreeTemporaryItems(); 875 mAsyncScrollsWithAnchor.Clear(); 876 nsCSSRendering::EndFrameTreesLocked(); 877 } 878 879 void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame, 880 const nsIFrame* aStopAtFrame) { 881 mFramesMarkedForDisplay.AppendElement(aFrame); 882 for (nsIFrame* f = aFrame; f; 883 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) { 884 if (f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { 885 return; 886 } 887 f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO); 888 if (f == aStopAtFrame) { 889 // we've reached a frame that we know will be painted, so we can stop. 890 break; 891 } 892 } 893 } 894 895 void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame) { 896 mFramesMarkedForDisplayIfVisible.AppendElement(aFrame); 897 } 898 899 static void MarkFrameForDisplayIfVisibleInternal(nsIFrame* aFrame, 900 const nsIFrame* aStopAtFrame) { 901 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) { 902 if (f->ForceDescendIntoIfVisible()) { 903 return; 904 } 905 f->SetForceDescendIntoIfVisible(true); 906 907 // This condition must match the condition in 908 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by 909 // nsLayoutUtils::GetDisplayListParent 910 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) { 911 nsIFrame* parent = f->GetParent(); 912 if (parent && !parent->ForceDescendIntoIfVisible()) { 913 // If the GetDisplayListParent call is going to walk to a placeholder, 914 // in rare cases the placeholder might be contained in a different 915 // continuation from the oof. So we have to make sure to mark the oofs 916 // parent. In the common case this doesn't make us do any extra work, 917 // just changes the order in which we visit the frames since walking 918 // through placeholders will walk through the parent, and we stop when 919 // we find a ForceDescendIntoIfVisible bit set. 920 MarkFrameForDisplayIfVisibleInternal(parent, aStopAtFrame); 921 } 922 } 923 924 if (f == aStopAtFrame) { 925 // we've reached a frame that we know will be painted, so we can stop. 926 break; 927 } 928 } 929 } 930 931 void nsDisplayListBuilder::MarkFrameForDisplayIfVisible( 932 nsIFrame* aFrame, const nsIFrame* aStopAtFrame) { 933 AddFrameMarkedForDisplayIfVisible(aFrame); 934 935 MarkFrameForDisplayIfVisibleInternal(aFrame, aStopAtFrame); 936 } 937 938 void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() { 939 mIsRelativeToLayoutViewport = true; 940 UpdateShouldBuildAsyncZoomContainer(); 941 } 942 943 void nsDisplayListBuilder::ForceLayerForScrollParent() { 944 mForceLayerForScrollParent = true; 945 mNumActiveScrollframesEncountered++; 946 } 947 948 void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() { 949 const Document* document = mReferenceFrame->PresContext()->Document(); 950 mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport && 951 !document->Fullscreen() && 952 nsLayoutUtils::AllowZoomingForDocument(document); 953 954 // If mIsRelativeToLayoutViewport == false, hit-testing on this 955 // display list will take into account the pres shell resolution. 956 // If we're not building an async zoom container (meaning, the 957 // resolution will not take effect visually), the resolution better 958 // be 1.0, otherwise rendering and hit-testing are out of sync. 959 #ifdef DEBUG 960 if (!mIsRelativeToLayoutViewport && !mBuildAsyncZoomContainer) { 961 MOZ_ASSERT(document->GetPresShell()->GetResolution() == 1.0f); 962 } 963 #endif 964 } 965 966 // Certain prefs may cause display list items to be added or removed when they 967 // are toggled. In those cases, we need to fully rebuild the display list. 968 bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() { 969 // If we transition between wrapping the RCD-RSF contents into an async 970 // zoom container vs. not, we need to rebuild the display list. This only 971 // happens when the zooming or container scrolling prefs are toggled 972 // (manually by the user, or during test setup). 973 bool didBuildAsyncZoomContainer = mBuildAsyncZoomContainer; 974 UpdateShouldBuildAsyncZoomContainer(); 975 976 bool hadOverlayScrollbarsLastTime = mUseOverlayScrollbars; 977 mUseOverlayScrollbars = 978 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars); 979 980 bool alwaysLayerizedScrollbarsLastTime = mAlwaysLayerizeScrollbars; 981 mAlwaysLayerizeScrollbars = 982 StaticPrefs::layout_scrollbars_always_layerize_track(); 983 984 bool oldShouldActivateAllScrollFrames = mShouldActivateAllScrollFrames; 985 mShouldActivateAllScrollFrames = 986 ScrollContainerFrame::ShouldActivateAllScrollFrames(nullptr, 987 mReferenceFrame); 988 989 if (didBuildAsyncZoomContainer != mBuildAsyncZoomContainer) { 990 return true; 991 } 992 993 if (hadOverlayScrollbarsLastTime != mUseOverlayScrollbars) { 994 return true; 995 } 996 997 if (alwaysLayerizedScrollbarsLastTime != mAlwaysLayerizeScrollbars) { 998 return true; 999 } 1000 1001 if (oldShouldActivateAllScrollFrames != mShouldActivateAllScrollFrames) { 1002 return true; 1003 } 1004 1005 return false; 1006 } 1007 1008 void nsDisplayListBuilder::AddScrollContainerFrameToNotify( 1009 ScrollContainerFrame* aScrollContainerFrame) { 1010 mScrollContainerFramesToNotify.insert(aScrollContainerFrame); 1011 } 1012 1013 void nsDisplayListBuilder::NotifyAndClearScrollContainerFrames() { 1014 for (const auto& it : mScrollContainerFramesToNotify) { 1015 it->NotifyApzTransaction(); 1016 } 1017 mScrollContainerFramesToNotify.clear(); 1018 } 1019 1020 bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay( 1021 nsIFrame* aDirtyFrame, nsIFrame* aFrame, const nsRect& aVisibleRect, 1022 const nsRect& aDirtyRect) { 1023 MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame); 1024 nsRect dirty; 1025 nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame( 1026 this, aFrame, aVisibleRect, aDirtyRect, &dirty); 1027 if (!aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) && 1028 visible.IsEmpty()) { 1029 return false; 1030 } 1031 1032 // Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow 1033 // frame, then it will also mark any outer frames to ensure that building 1034 // reaches the dirty feame. 1035 if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) { 1036 MarkFrameForDisplay(aFrame, aDirtyFrame); 1037 } 1038 1039 return true; 1040 } 1041 1042 static void UnmarkFrameForDisplay(nsIFrame* aFrame, 1043 const nsIFrame* aStopAtFrame) { 1044 for (nsIFrame* f = aFrame; f; 1045 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) { 1046 if (!f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { 1047 return; 1048 } 1049 f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO); 1050 if (f == aStopAtFrame) { 1051 // we've reached a frame that we know will be painted, so we can stop. 1052 break; 1053 } 1054 } 1055 } 1056 1057 static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) { 1058 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) { 1059 if (!f->ForceDescendIntoIfVisible()) { 1060 return; 1061 } 1062 f->SetForceDescendIntoIfVisible(false); 1063 1064 // This condition must match the condition in 1065 // nsLayoutUtils::GetParentOrPlaceholderFor which is used by 1066 // nsLayoutUtils::GetDisplayListParent 1067 if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) { 1068 nsIFrame* parent = f->GetParent(); 1069 if (parent && parent->ForceDescendIntoIfVisible()) { 1070 // If the GetDisplayListParent call is going to walk to a placeholder, 1071 // in rare cases the placeholder might be contained in a different 1072 // continuation from the oof. So we have to make sure to mark the oofs 1073 // parent. In the common case this doesn't make us do any extra work, 1074 // just changes the order in which we visit the frames since walking 1075 // through placeholders will walk through the parent, and we stop when 1076 // we find a ForceDescendIntoIfVisible bit set. 1077 UnmarkFrameForDisplayIfVisible(f); 1078 } 1079 } 1080 } 1081 } 1082 1083 nsDisplayListBuilder::~nsDisplayListBuilder() { 1084 NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0, 1085 "All frames should have been unmarked"); 1086 NS_ASSERTION(mFramesWithOOFData.Length() == 0, 1087 "All OOF data should have been removed"); 1088 NS_ASSERTION(mPresShellStates.Length() == 0, 1089 "All presshells should have been exited"); 1090 1091 DisplayItemClipChain* c = mFirstClipChainToDestroy; 1092 while (c) { 1093 DisplayItemClipChain* next = c->mNextClipChainToDestroy; 1094 c->DisplayItemClipChain::~DisplayItemClipChain(); 1095 c = next; 1096 } 1097 1098 MOZ_COUNT_DTOR(nsDisplayListBuilder); 1099 } 1100 1101 uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() { 1102 uint32_t flags = 0; 1103 if (mSyncDecodeImages) { 1104 flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES; 1105 } 1106 if (mIsPaintingToWindow) { 1107 flags |= nsCSSRendering::PAINTBG_TO_WINDOW; 1108 } 1109 if (mUseHighQualityScaling) { 1110 flags |= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING; 1111 } 1112 return flags; 1113 } 1114 1115 // TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags. 1116 uint32_t nsDisplayListBuilder::GetImageRendererFlags() const { 1117 uint32_t flags = 0; 1118 if (mSyncDecodeImages) { 1119 flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES; 1120 } 1121 if (mIsPaintingToWindow) { 1122 flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW; 1123 } 1124 if (mUseHighQualityScaling) { 1125 flags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING; 1126 } 1127 return flags; 1128 } 1129 1130 uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const { 1131 uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY; 1132 if (mSyncDecodeImages) { 1133 flags |= imgIContainer::FLAG_SYNC_DECODE; 1134 } else { 1135 flags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST; 1136 } 1137 if (mIsPaintingToWindow || mUseHighQualityScaling) { 1138 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING; 1139 } 1140 return flags; 1141 } 1142 1143 nsCaret* nsDisplayListBuilder::GetCaret() { 1144 RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret(); 1145 return caret; 1146 } 1147 1148 void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell* aPresShell) { 1149 if (mIsPaintingToWindow) { 1150 aPresShell->IncrementPaintCount(); 1151 } 1152 } 1153 1154 void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame, 1155 bool aPointerEventsNoneDoc) { 1156 PresShellState* state = mPresShellStates.AppendElement(); 1157 state->mPresShell = aReferenceFrame->PresShell(); 1158 state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length(); 1159 state->mFirstFrameWithOOFData = mFramesWithOOFData.Length(); 1160 1161 ScrollContainerFrame* sf = state->mPresShell->GetRootScrollContainerFrame(); 1162 if (sf && IsInSubdocument()) { 1163 // We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure 1164 // that the canvas background color will be set correctly, and that only one 1165 // unscrollable item will be created. 1166 // This is done to avoid, for example, a case where only scrollbar frames 1167 // are invalidated - we would skip creating nsDisplayCanvasBackgroundColor 1168 // and possibly end up with an extra nsDisplaySolidColor item. 1169 // We skip this for the root document, since we don't want to use 1170 // MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll 1171 // do it manually there. 1172 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame()); 1173 if (canvasFrame) { 1174 MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame); 1175 } 1176 } 1177 1178 #ifdef DEBUG 1179 state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(), 1180 nsLayoutPhase::DisplayListBuilding); 1181 #endif 1182 1183 if (!IsForEventDelivery()) { 1184 state->mPresShell->UpdateCanvasBackground(); 1185 } 1186 1187 bool buildCaret = mBuildCaret; 1188 if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) { 1189 state->mIsBackgroundOnly = false; 1190 } else { 1191 state->mIsBackgroundOnly = true; 1192 buildCaret = false; 1193 } 1194 1195 bool pointerEventsNone = aPointerEventsNoneDoc; 1196 if (IsInSubdocument()) { 1197 pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2] 1198 .mInsidePointerEventsNoneDoc; 1199 } 1200 state->mInsidePointerEventsNoneDoc = pointerEventsNone; 1201 1202 state->mPresShellIgnoreScrollFrame = 1203 state->mPresShell->IgnoringViewportScrolling() 1204 ? state->mPresShell->GetRootScrollContainerFrame() 1205 : nullptr; 1206 1207 nsPresContext* pc = aReferenceFrame->PresContext(); 1208 mIsInChromePresContext = pc->IsChrome(); 1209 nsIDocShell* docShell = pc->GetDocShell(); 1210 1211 if (docShell) { 1212 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed); 1213 } 1214 1215 state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell); 1216 1217 if (auto* vt = pc->Document()->GetActiveViewTransition()) { 1218 // We must ensure captured view transition elements, including offscreen 1219 // elements, are reached for display list building. 1220 AutoTArray<nsIFrame*, 32> capturedFrames; 1221 vt->GetCapturedFrames(capturedFrames); 1222 for (const auto& frame : capturedFrames) { 1223 MarkFrameForDisplay(frame, aReferenceFrame); 1224 } 1225 } 1226 1227 if (!buildCaret) { 1228 return; 1229 } 1230 1231 state->mCaretFrame = [&]() -> nsIFrame* { 1232 RefPtr<nsCaret> caret = state->mPresShell->GetCaret(); 1233 nsIFrame* currentCaret = caret->GetPaintGeometry(&mCaretRect); 1234 if (!currentCaret) { 1235 return nullptr; 1236 } 1237 1238 // Check if the display root for the caret matches the display root that 1239 // we're painting, and only use it if it matches. Likely we only need this 1240 // for carets inside popups. 1241 if (nsLayoutUtils::GetDisplayRootFrame(currentCaret) != 1242 nsLayoutUtils::GetDisplayRootFrame(aReferenceFrame)) { 1243 return nullptr; 1244 } 1245 1246 // Caret frames add visual area to their frame, but we don't update the 1247 // overflow area. Use flags to make sure we build display items for that 1248 // frame instead. 1249 MOZ_ASSERT(currentCaret->PresShell() == state->mPresShell); 1250 MarkFrameForDisplay(currentCaret, aReferenceFrame); 1251 caret->SetLastPaintedFrame(currentCaret); 1252 if (!mPaintedCarets.Contains(caret)) { 1253 mPaintedCarets.AppendElement(std::move(caret)); 1254 } 1255 return currentCaret; 1256 }(); 1257 } 1258 1259 // A non-blank paint is a paint that does not just contain the canvas 1260 // background. 1261 static bool DisplayListIsNonBlank(nsDisplayList* aList) { 1262 for (nsDisplayItem* i : *aList) { 1263 switch (i->GetType()) { 1264 case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO: 1265 case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE: 1266 continue; 1267 case DisplayItemType::TYPE_SOLID_COLOR: 1268 case DisplayItemType::TYPE_BACKGROUND: 1269 case DisplayItemType::TYPE_BACKGROUND_COLOR: 1270 if (i->Frame()->IsCanvasFrame()) { 1271 continue; 1272 } 1273 return true; 1274 default: 1275 return true; 1276 } 1277 } 1278 return false; 1279 } 1280 1281 // A contentful paint is a paint that does contains DOM content (text, 1282 // images, non-blank canvases, SVG): "First Contentful Paint entry 1283 // contains a DOMHighResTimeStamp reporting the time when the browser 1284 // first rendered any text, image (including background images), 1285 // non-white canvas or SVG. This excludes any content of iframes, but 1286 // includes text with pending webfonts. This is the first time users 1287 // could start consuming page content." 1288 static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder, 1289 nsDisplayList* aList) { 1290 for (nsDisplayItem* i : *aList) { 1291 DisplayItemType type = i->GetType(); 1292 nsDisplayList* children = i->GetChildren(); 1293 1294 switch (type) { 1295 case DisplayItemType::TYPE_SUBDOCUMENT: // iframes are ignored 1296 break; 1297 // CANVASes check if they may have been modified (as a stand-in 1298 // actually tracking all modifications) 1299 default: 1300 if (i->IsContentful()) { 1301 bool dummy; 1302 nsRect bound = i->GetBounds(aBuilder, &dummy); 1303 if (!bound.IsEmpty()) { 1304 return true; 1305 } 1306 } 1307 if (children) { 1308 if (DisplayListIsContentful(aBuilder, children)) { 1309 return true; 1310 } 1311 } 1312 break; 1313 } 1314 } 1315 return false; 1316 } 1317 1318 void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame, 1319 nsDisplayList* aPaintedContents) { 1320 NS_ASSERTION( 1321 CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(), 1322 "Presshell mismatch"); 1323 1324 if (mIsPaintingToWindow && aPaintedContents) { 1325 nsPresContext* pc = aReferenceFrame->PresContext(); 1326 if (!pc->HadNonBlankPaint()) { 1327 if (!CurrentPresShellState()->mIsBackgroundOnly && 1328 DisplayListIsNonBlank(aPaintedContents)) { 1329 pc->NotifyNonBlankPaint(); 1330 } 1331 } 1332 nsRootPresContext* rootPresContext = pc->GetRootPresContext(); 1333 if (!pc->HasStoppedGeneratingLCP() && rootPresContext) { 1334 if (!CurrentPresShellState()->mIsBackgroundOnly) { 1335 if (pc->HasEverBuiltInvisibleText() || 1336 DisplayListIsContentful(this, aPaintedContents)) { 1337 pc->NotifyContentfulPaint(); 1338 } 1339 } 1340 } 1341 } 1342 1343 ResetMarkedFramesForDisplayList(aReferenceFrame); 1344 mPresShellStates.RemoveLastElement(); 1345 1346 if (!mPresShellStates.IsEmpty()) { 1347 nsPresContext* pc = CurrentPresContext(); 1348 nsIDocShell* docShell = pc->GetDocShell(); 1349 if (docShell) { 1350 docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed); 1351 } 1352 mIsInChromePresContext = pc->IsChrome(); 1353 } else { 1354 for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) { 1355 UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]); 1356 } 1357 mFramesMarkedForDisplayIfVisible.SetLength(0); 1358 } 1359 } 1360 1361 void nsDisplayListBuilder::FreeClipChains() { 1362 // Iterate the clip chains from newest to oldest (forward 1363 // iteration), so that we destroy descendants first which 1364 // will drop the ref count on their ancestors. 1365 DisplayItemClipChain** indirect = &mFirstClipChainToDestroy; 1366 1367 while (*indirect) { 1368 if (!(*indirect)->mRefCount) { 1369 DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy; 1370 1371 mClipDeduplicator.erase(*indirect); 1372 (*indirect)->DisplayItemClipChain::~DisplayItemClipChain(); 1373 Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect); 1374 1375 *indirect = next; 1376 } else { 1377 indirect = &(*indirect)->mNextClipChainToDestroy; 1378 } 1379 } 1380 } 1381 1382 void nsDisplayListBuilder::FreeTemporaryItems() { 1383 for (nsDisplayItem* i : mTemporaryItems) { 1384 // Temporary display items are not added to the frames. 1385 MOZ_ASSERT(i->Frame()); 1386 i->RemoveFrame(i->Frame()); 1387 i->Destroy(this); 1388 } 1389 1390 mTemporaryItems.Clear(); 1391 } 1392 1393 void nsDisplayListBuilder::ResetMarkedFramesForDisplayList( 1394 const nsIFrame* aReferenceFrame) { 1395 // Unmark and pop off the frames marked for display in this pres shell. 1396 uint32_t firstFrameForShell = 1397 CurrentPresShellState()->mFirstFrameMarkedForDisplay; 1398 for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length(); 1399 ++i) { 1400 UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame); 1401 } 1402 mFramesMarkedForDisplay.SetLength(firstFrameForShell); 1403 1404 firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData; 1405 for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) { 1406 mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty()); 1407 } 1408 mFramesWithOOFData.SetLength(firstFrameForShell); 1409 } 1410 1411 void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() { 1412 CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing(); 1413 } 1414 1415 void nsDisplayListBuilder::MarkFramesForDisplayList( 1416 nsIFrame* aDirtyFrame, const nsFrameList& aFrames) { 1417 nsRect visibleRect = GetVisibleRect(); 1418 nsRect dirtyRect = GetDirtyRect(); 1419 1420 // If we are entering content that is fixed to the RCD-RSF, we are 1421 // crossing the async zoom container boundary, and need to convert from 1422 // visual to layout coordinates. 1423 if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) { 1424 if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() && 1425 viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) { 1426 if (viewportFrame->PresShell()->GetRootScrollContainerFrame()) { 1427 #ifdef DEBUG 1428 for (nsIFrame* f : aFrames) { 1429 MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f)); 1430 } 1431 #endif 1432 visibleRect = ViewportUtils::VisualToLayout(visibleRect, 1433 viewportFrame->PresShell()); 1434 dirtyRect = ViewportUtils::VisualToLayout(dirtyRect, 1435 viewportFrame->PresShell()); 1436 } 1437 #ifdef DEBUG 1438 else { 1439 // This is an edge case that should only happen if we are in a 1440 // document with a XUL root element so that it does not have a root 1441 // scroll frame but it has fixed pos content and all of the frames in 1442 // aFrames are that fixed pos content. 1443 for (nsIFrame* f : aFrames) { 1444 MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) && 1445 f->GetParent() == aDirtyFrame && 1446 f->StyleDisplay()->mPosition == 1447 StylePositionProperty::Fixed); 1448 } 1449 // There's no root scroll frame so there can't be any zooming or async 1450 // panning so we don't need to adjust the visible and dirty rects. 1451 } 1452 #endif 1453 } 1454 } 1455 1456 bool markedFrames = false; 1457 for (nsIFrame* e : aFrames) { 1458 // Skip the AccessibleCaret frame when building no caret. 1459 if (!IsBuildingCaret()) { 1460 nsIContent* content = e->GetContent(); 1461 if (content && content->IsInNativeAnonymousSubtree() && 1462 content->IsElement()) { 1463 const nsAttrValue* classes = content->AsElement()->GetClasses(); 1464 if (classes && 1465 classes->Contains(nsGkAtoms::mozAccessiblecaret, eCaseMatters)) { 1466 continue; 1467 } 1468 } 1469 } 1470 if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) { 1471 markedFrames = true; 1472 } 1473 } 1474 1475 if (markedFrames) { 1476 // mClipState.GetClipChainForContainingBlockDescendants can return pointers 1477 // to objects on the stack, so we need to clone the chain. 1478 const DisplayItemClipChain* clipChain = 1479 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants()); 1480 const DisplayItemClipChain* combinedClipChain = 1481 mClipState.GetCurrentCombinedClipChain(this); 1482 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot; 1483 1484 OutOfFlowDisplayData* data = new OutOfFlowDisplayData( 1485 clipChain, combinedClipChain, asr, mCurrentScrollParentId, visibleRect, 1486 dirtyRect, mInViewTransitionCapture); 1487 aDirtyFrame->SetProperty( 1488 nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data); 1489 mFramesWithOOFData.AppendElement(aDirtyFrame); 1490 } 1491 1492 if (!aDirtyFrame->GetParent()) { 1493 // This is the viewport frame of aDirtyFrame's presshell. 1494 // Store the current display data so that it can be used for fixed 1495 // background images. 1496 NS_ASSERTION( 1497 CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(), 1498 "Presshell mismatch"); 1499 MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData, 1500 "already traversed this presshell's root frame?"); 1501 1502 const DisplayItemClipChain* clipChain = 1503 CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants()); 1504 const DisplayItemClipChain* combinedClipChain = 1505 mClipState.GetCurrentCombinedClipChain(this); 1506 const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot; 1507 CurrentPresShellState()->mFixedBackgroundDisplayData.emplace( 1508 clipChain, combinedClipChain, asr, mCurrentScrollParentId, 1509 GetVisibleRect(), GetDirtyRect(), mInViewTransitionCapture); 1510 } 1511 } 1512 1513 /** 1514 * Mark all preserve-3d children with 1515 * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure 1516 * nsIFrame::BuildDisplayListForChild() would visit them. Also compute 1517 * dirty rect for preserve-3d children. 1518 * 1519 * @param aDirtyFrame is the frame to mark children extending context. 1520 */ 1521 void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList( 1522 nsIFrame* aDirtyFrame) { 1523 for (const auto& childList : aDirtyFrame->ChildLists()) { 1524 for (nsIFrame* child : childList.mList) { 1525 if (child->Combines3DTransformWithAncestors()) { 1526 MarkFrameForDisplay(child, aDirtyFrame); 1527 } 1528 1529 if (child->IsBlockWrapper()) { 1530 // Mark preserve-3d frames inside the block wrapper. 1531 MarkPreserve3DFramesForDisplayList(child); 1532 } 1533 } 1534 } 1535 } 1536 1537 ActiveScrolledRoot* nsDisplayListBuilder::GetOrCreateActiveScrolledRoot( 1538 const ActiveScrolledRoot* aParent, 1539 ScrollContainerFrame* aScrollContainerFrame) { 1540 RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::GetOrCreateASRForFrame( 1541 aParent, aScrollContainerFrame, mActiveScrolledRoots); 1542 return asr; 1543 } 1544 1545 ActiveScrolledRoot* 1546 nsDisplayListBuilder::GetOrCreateActiveScrolledRootForSticky( 1547 const ActiveScrolledRoot* aParent, nsIFrame* aStickyFrame) { 1548 RefPtr<ActiveScrolledRoot> asr = 1549 ActiveScrolledRoot::GetOrCreateASRForStickyFrame(aParent, aStickyFrame, 1550 mActiveScrolledRoots); 1551 return asr; 1552 } 1553 1554 const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain( 1555 const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR, 1556 const DisplayItemClipChain* aParent) { 1557 MOZ_DIAGNOSTIC_ASSERT(!(aParent && aParent->mOnStack)); 1558 void* p = Allocate(sizeof(DisplayItemClipChain), 1559 DisplayListArenaObjectId::CLIPCHAIN); 1560 DisplayItemClipChain* c = new (KnownNotNull, p) 1561 DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy); 1562 #if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) 1563 c->mOnStack = false; 1564 #endif 1565 auto result = mClipDeduplicator.insert(c); 1566 if (!result.second) { 1567 // An equivalent clip chain item was already created, so let's return that 1568 // instead. Destroy the one we just created. 1569 // Note that this can cause clip chains from different coordinate systems to 1570 // collapse into the same clip chain object, because clip chains do not keep 1571 // track of the reference frame that they were created in. 1572 c->DisplayItemClipChain::~DisplayItemClipChain(); 1573 Destroy(DisplayListArenaObjectId::CLIPCHAIN, c); 1574 return *(result.first); 1575 } 1576 mFirstClipChainToDestroy = c; 1577 return c; 1578 } 1579 1580 struct ClipChainItem { 1581 DisplayItemClip clip; 1582 const ActiveScrolledRoot* asr; 1583 }; 1584 1585 static const DisplayItemClipChain* FindCommonAncestorClipForIntersection( 1586 const DisplayItemClipChain* aOne, const DisplayItemClipChain* aTwo) { 1587 for (const ActiveScrolledRoot* asr = 1588 ActiveScrolledRoot::PickDescendant(aOne->mASR, aTwo->mASR); 1589 asr; asr = asr->mParent) { 1590 if (aOne == aTwo) { 1591 return aOne; 1592 } 1593 if (aOne->mASR == asr) { 1594 aOne = aOne->mParent; 1595 } 1596 if (aTwo->mASR == asr) { 1597 aTwo = aTwo->mParent; 1598 } 1599 if (!aOne) { 1600 return aTwo; 1601 } 1602 if (!aTwo) { 1603 return aOne; 1604 } 1605 } 1606 return nullptr; 1607 } 1608 1609 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection( 1610 const DisplayItemClipChain* aAncestor, 1611 const DisplayItemClipChain* aLeafClip1, 1612 const DisplayItemClipChain* aLeafClip2) { 1613 AutoTArray<ClipChainItem, 8> intersectedClips; 1614 1615 const DisplayItemClipChain* clip1 = aLeafClip1; 1616 const DisplayItemClipChain* clip2 = aLeafClip2; 1617 1618 const ActiveScrolledRoot* asr = ActiveScrolledRoot::PickDescendant( 1619 clip1 ? clip1->mASR : nullptr, clip2 ? clip2->mASR : nullptr); 1620 1621 // Build up the intersection from the leaf to the root and put it into 1622 // intersectedClips. The loop below will convert intersectedClips into an 1623 // actual DisplayItemClipChain. 1624 // (We need to do this in two passes because we need the parent clip in order 1625 // to create the DisplayItemClipChain object, but the parent clip has not 1626 // been created at that point.) 1627 while (!aAncestor || asr != aAncestor->mASR) { 1628 if (clip1 && clip1->mASR == asr) { 1629 if (clip2 && clip2->mASR == asr) { 1630 DisplayItemClip intersection = clip1->mClip; 1631 intersection.IntersectWith(clip2->mClip); 1632 intersectedClips.AppendElement(ClipChainItem{intersection, asr}); 1633 clip2 = clip2->mParent; 1634 } else { 1635 intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr}); 1636 } 1637 clip1 = clip1->mParent; 1638 } else if (clip2 && clip2->mASR == asr) { 1639 intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr}); 1640 clip2 = clip2->mParent; 1641 } 1642 if (!asr) { 1643 MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier"); 1644 break; 1645 } 1646 asr = asr->mParent; 1647 } 1648 1649 // Convert intersectedClips into a DisplayItemClipChain. 1650 const DisplayItemClipChain* parentSC = aAncestor; 1651 for (auto& sc : Reversed(intersectedClips)) { 1652 parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC); 1653 } 1654 return parentSC; 1655 } 1656 1657 const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection( 1658 const DisplayItemClipChain* aLeafClip1, 1659 const DisplayItemClipChain* aLeafClip2) { 1660 // aLeafClip2 might be a reference to a clip on the stack. We need to make 1661 // sure that CreateClipChainIntersection will allocate the actual intersected 1662 // clip in the builder's arena, so for the aLeafClip1 == nullptr case, 1663 // we supply nullptr as the common ancestor so that 1664 // CreateClipChainIntersection clones the whole chain. 1665 const DisplayItemClipChain* ancestorClip = 1666 aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2) 1667 : nullptr; 1668 1669 return CreateClipChainIntersection(ancestorClip, aLeafClip1, aLeafClip2); 1670 } 1671 1672 const DisplayItemClipChain* nsDisplayListBuilder::CopyWholeChain( 1673 const DisplayItemClipChain* aClipChain) { 1674 return CreateClipChainIntersection(nullptr, aClipChain, nullptr); 1675 } 1676 1677 const nsIFrame* nsDisplayListBuilder::FindReferenceFrameFor( 1678 const nsIFrame* aFrame, nsPoint* aOffset) const { 1679 auto MaybeApplyAdditionalOffset = [&]() { 1680 if (auto offset = AdditionalOffset()) { 1681 *aOffset += *offset; 1682 } 1683 }; 1684 1685 if (aFrame == mCurrentFrame) { 1686 if (aOffset) { 1687 *aOffset = mCurrentOffsetToReferenceFrame; 1688 } 1689 return mCurrentReferenceFrame; 1690 } 1691 1692 for (const nsIFrame* f = aFrame; f; 1693 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) { 1694 if (f == mReferenceFrame || f->IsTransformed()) { 1695 if (aOffset) { 1696 *aOffset = aFrame->GetOffsetToCrossDoc(f); 1697 MaybeApplyAdditionalOffset(); 1698 } 1699 return f; 1700 } 1701 } 1702 1703 if (aOffset) { 1704 *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame); 1705 MaybeApplyAdditionalOffset(); 1706 } 1707 1708 return mReferenceFrame; 1709 } 1710 1711 // Sticky frames are active if their nearest scrollable frame is also active. 1712 static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder, 1713 nsIFrame* aFrame) { 1714 MOZ_ASSERT(aFrame->StyleDisplay()->mPosition == 1715 StylePositionProperty::Sticky); 1716 1717 auto* ssc = StickyScrollContainer::GetOrCreateForFrame(aFrame); 1718 return ssc && ssc->ScrollContainer()->IsMaybeAsynchronouslyScrolled(); 1719 } 1720 1721 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame, 1722 nsIFrame** aParent) { 1723 if (aFrame == mReferenceFrame) { 1724 return true; 1725 } 1726 1727 if (!IsPaintingToWindow()) { 1728 if (aParent) { 1729 *aParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame); 1730 } 1731 return false; 1732 } 1733 1734 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame); 1735 if (!parent) { 1736 return true; 1737 } 1738 *aParent = parent; 1739 1740 if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Sticky && 1741 IsStickyFrameActive(this, aFrame)) { 1742 return true; 1743 } 1744 1745 if (aFrame->IsTransformed()) { 1746 if (EffectCompositor::HasAnimationsForCompositor( 1747 aFrame, DisplayItemType::TYPE_TRANSFORM)) { 1748 return true; 1749 } 1750 } 1751 1752 if (parent->IsScrollContainerOrSubclass()) { 1753 ScrollContainerFrame* sf = do_QueryFrame(parent); 1754 if (sf->GetScrolledFrame() == aFrame) { 1755 MOZ_ASSERT(!aFrame->IsTransformed()); 1756 return sf->IsMaybeAsynchronouslyScrolled(); 1757 } 1758 } 1759 1760 return false; 1761 } 1762 1763 nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor( 1764 nsIFrame* aFrame) { 1765 MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDocInProcess( 1766 RootReferenceFrame(), aFrame)); 1767 nsIFrame* cursor = aFrame; 1768 while (cursor != RootReferenceFrame()) { 1769 nsIFrame* next; 1770 if (IsAnimatedGeometryRoot(cursor, &next)) { 1771 return cursor; 1772 } 1773 cursor = next; 1774 } 1775 return cursor; 1776 } 1777 1778 static nsRect ApplyAllClipNonRoundedIntersection( 1779 const DisplayItemClipChain* aClipChain, const nsRect& aRect) { 1780 nsRect result = aRect; 1781 while (aClipChain) { 1782 result = aClipChain->mClip.ApplyNonRoundedIntersection(result); 1783 aClipChain = aClipChain->mParent; 1784 } 1785 return result; 1786 } 1787 1788 void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) { 1789 if (!mWindowDraggingAllowed || !IsForPainting()) { 1790 return; 1791 } 1792 1793 const nsStyleUIReset* styleUI = aFrame->StyleUIReset(); 1794 if (styleUI->mWindowDragging == StyleWindowDragging::Default) { 1795 // This frame has the default value and doesn't influence the window 1796 // dragging region. 1797 return; 1798 } 1799 1800 if (!aFrame->StyleVisibility()->IsVisible()) { 1801 // Invisible frames don't influence the window dragging region. 1802 return; 1803 } 1804 1805 LayoutDeviceToLayoutDeviceMatrix4x4 referenceFrameToRootReferenceFrame; 1806 1807 // The const_cast is for nsLayoutUtils::GetTransformToAncestor. 1808 nsIFrame* referenceFrame = 1809 const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame)); 1810 1811 if (IsInTransform()) { 1812 // Only support 2d rectilinear transforms. Transform support is needed for 1813 // the horizontal flip transform that's applied to the urlbar textbox in 1814 // RTL mode - it should be able to exclude itself from the draggable region. 1815 referenceFrameToRootReferenceFrame = 1816 ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>( 1817 nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame}, 1818 RelativeTo{mReferenceFrame}) 1819 .GetMatrix()); 1820 Matrix referenceFrameToRootReferenceFrame2d; 1821 if (!referenceFrameToRootReferenceFrame.Is2D( 1822 &referenceFrameToRootReferenceFrame2d) || 1823 !referenceFrameToRootReferenceFrame2d.IsRectilinear()) { 1824 return; 1825 } 1826 } else { 1827 MOZ_ASSERT(referenceFrame == mReferenceFrame, 1828 "referenceFrameToRootReferenceFrame needs to be adjusted"); 1829 } 1830 1831 // We do some basic visibility checking on the frame's border box here. 1832 // We intersect it both with the current dirty rect and with the current 1833 // clip. Either one is just a conservative approximation on its own, but 1834 // their intersection luckily works well enough for our purposes, so that 1835 // we don't have to do full-blown visibility computations. 1836 // The most important case we need to handle is the scrolled-off tab: 1837 // If the tab bar overflows, tab parts that are clipped by the scrollbox 1838 // should not be allowed to interfere with the window dragging region. Using 1839 // just the current DisplayItemClip is not enough to cover this case 1840 // completely because clips are reset while building stacking context 1841 // contents, so for example we'd fail to clip frames that have a clip path 1842 // applied to them. But the current dirty rect doesn't get reset in that 1843 // case, so we use it to make this case work. 1844 nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect); 1845 borderBox += ToReferenceFrame(aFrame); 1846 const DisplayItemClipChain* clip = 1847 ClipState().GetCurrentCombinedClipChain(this); 1848 borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox); 1849 if (borderBox.IsEmpty()) { 1850 return; 1851 } 1852 1853 LayoutDeviceRect devPixelBorderBox = LayoutDevicePixel::FromAppUnits( 1854 borderBox, aFrame->PresContext()->AppUnitsPerDevPixel()); 1855 1856 LayoutDeviceRect transformedDevPixelBorderBox = 1857 TransformBy(referenceFrameToRootReferenceFrame, devPixelBorderBox); 1858 transformedDevPixelBorderBox.Round(); 1859 LayoutDeviceIntRect transformedDevPixelBorderBoxInt; 1860 1861 if (!transformedDevPixelBorderBox.ToIntRect( 1862 &transformedDevPixelBorderBoxInt)) { 1863 return; 1864 } 1865 1866 LayoutDeviceIntRegion& region = 1867 styleUI->mWindowDragging == StyleWindowDragging::Drag 1868 ? mWindowDraggingRegion 1869 : mWindowNoDraggingRegion; 1870 1871 if (!IsRetainingDisplayList()) { 1872 region.OrWith(transformedDevPixelBorderBoxInt); 1873 return; 1874 } 1875 1876 gfx::IntRect rect(transformedDevPixelBorderBoxInt.ToUnknownRect()); 1877 if (styleUI->mWindowDragging == StyleWindowDragging::Drag) { 1878 mRetainedWindowDraggingRegion.Add(aFrame, rect); 1879 } else { 1880 mRetainedWindowNoDraggingRegion.Add(aFrame, rect); 1881 } 1882 } 1883 1884 LayoutDeviceIntRegion nsDisplayListBuilder::GetWindowDraggingRegion() const { 1885 LayoutDeviceIntRegion result; 1886 if (!IsRetainingDisplayList()) { 1887 result.Sub(mWindowDraggingRegion, mWindowNoDraggingRegion); 1888 return result; 1889 } 1890 1891 LayoutDeviceIntRegion dragRegion = 1892 mRetainedWindowDraggingRegion.ToLayoutDeviceIntRegion(); 1893 1894 LayoutDeviceIntRegion noDragRegion = 1895 mRetainedWindowNoDraggingRegion.ToLayoutDeviceIntRegion(); 1896 1897 result.Sub(dragRegion, noDragRegion); 1898 return result; 1899 } 1900 1901 void nsDisplayTransform::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const { 1902 nsPaintedDisplayItem::AddSizeOfExcludingThis(aSizes); 1903 aSizes.mLayoutRetainedDisplayListSize += 1904 aSizes.mState.mMallocSizeOf(mTransformPreserves3D.get()); 1905 } 1906 1907 void nsDisplayListBuilder::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const { 1908 mPool.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::DisplayList); 1909 1910 size_t n = 0; 1911 MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf; 1912 n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf); 1913 n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf); 1914 n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf); 1915 n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf); 1916 n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf); 1917 // XXX can't measure mClipDeduplicator since it uses std::unordered_set. 1918 1919 aSizes.mLayoutRetainedDisplayListSize += n; 1920 } 1921 1922 void RetainedDisplayList::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const { 1923 for (nsDisplayItem* item : *this) { 1924 item->AddSizeOfExcludingThis(aSizes); 1925 if (RetainedDisplayList* children = item->GetChildren()) { 1926 children->AddSizeOfExcludingThis(aSizes); 1927 } 1928 } 1929 1930 size_t n = 0; 1931 1932 n += mDAG.mDirectPredecessorList.ShallowSizeOfExcludingThis( 1933 aSizes.mState.mMallocSizeOf); 1934 n += mDAG.mNodesInfo.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf); 1935 n += mOldItems.ShallowSizeOfExcludingThis(aSizes.mState.mMallocSizeOf); 1936 1937 aSizes.mLayoutRetainedDisplayListSize += n; 1938 } 1939 1940 size_t nsDisplayListBuilder::WeakFrameRegion::SizeOfExcludingThis( 1941 MallocSizeOf aMallocSizeOf) const { 1942 size_t n = 0; 1943 n += mFrames.ShallowSizeOfExcludingThis(aMallocSizeOf); 1944 for (const auto& frame : mFrames) { 1945 const UniquePtr<WeakFrame>& weakFrame = frame.mWeakFrame; 1946 n += aMallocSizeOf(weakFrame.get()); 1947 } 1948 n += mRects.ShallowSizeOfExcludingThis(aMallocSizeOf); 1949 return n; 1950 } 1951 1952 /** 1953 * Removes modified frames and rects from this WeakFrameRegion. 1954 */ 1955 void nsDisplayListBuilder::WeakFrameRegion::RemoveModifiedFramesAndRects() { 1956 MOZ_ASSERT(mFrames.Length() == mRects.Length()); 1957 1958 uint32_t i = 0; 1959 uint32_t length = mFrames.Length(); 1960 1961 while (i < length) { 1962 auto& wrapper = mFrames[i]; 1963 1964 if (!wrapper.mWeakFrame->IsAlive() || 1965 AnyContentAncestorModified(wrapper.mWeakFrame->GetFrame())) { 1966 // To avoid multiple O(n) shifts in the array, move the last element of 1967 // the array to the current position and decrease the array length. 1968 mFrameSet.Remove(wrapper.mFrame); 1969 mFrames[i] = std::move(mFrames[length - 1]); 1970 mRects[i] = std::move(mRects[length - 1]); 1971 length--; 1972 } else { 1973 i++; 1974 } 1975 } 1976 1977 mFrames.TruncateLength(length); 1978 mRects.TruncateLength(length); 1979 } 1980 1981 void nsDisplayListBuilder::RemoveModifiedWindowRegions() { 1982 mRetainedWindowDraggingRegion.RemoveModifiedFramesAndRects(); 1983 mRetainedWindowNoDraggingRegion.RemoveModifiedFramesAndRects(); 1984 mRetainedWindowOpaqueRegion.RemoveModifiedFramesAndRects(); 1985 } 1986 1987 void nsDisplayListBuilder::ClearRetainedWindowRegions() { 1988 mRetainedWindowDraggingRegion.Clear(); 1989 mRetainedWindowNoDraggingRegion.Clear(); 1990 mRetainedWindowOpaqueRegion.Clear(); 1991 } 1992 1993 const uint32_t gWillChangeAreaMultiplier = 3; 1994 static uint32_t GetLayerizationCost(const nsSize& aSize) { 1995 // There's significant overhead for each layer created from Gecko 1996 // (IPC+Shared Objects) and from the backend (like an OpenGL texture). 1997 // Therefore we set a minimum cost threshold of a 64x64 area. 1998 const int minBudgetCost = 64 * 64; 1999 2000 const uint32_t budgetCost = std::max( 2001 minBudgetCost, nsPresContext::AppUnitsToIntCSSPixels(aSize.width) * 2002 nsPresContext::AppUnitsToIntCSSPixels(aSize.height)); 2003 2004 return budgetCost; 2005 } 2006 2007 bool nsDisplayListBuilder::AddToWillChangeBudget(nsIFrame* aFrame, 2008 const nsSize& aSize) { 2009 MOZ_ASSERT(IsForPainting()); 2010 2011 if (aFrame->MayHaveWillChangeBudget()) { 2012 // The frame is already in the will-change budget. 2013 return true; 2014 } 2015 2016 const nsPresContext* presContext = aFrame->PresContext(); 2017 const nsRect area = presContext->GetVisibleArea(); 2018 const uint32_t budgetLimit = 2019 nsPresContext::AppUnitsToIntCSSPixels(area.width) * 2020 nsPresContext::AppUnitsToIntCSSPixels(area.height); 2021 const uint32_t cost = GetLayerizationCost(aSize); 2022 2023 DocumentWillChangeBudget& documentBudget = 2024 mDocumentWillChangeBudgets.LookupOrInsert(presContext); 2025 2026 const bool onBudget = 2027 (documentBudget + cost) / gWillChangeAreaMultiplier < budgetLimit; 2028 2029 if (onBudget) { 2030 documentBudget += cost; 2031 mFrameWillChangeBudgets.InsertOrUpdate( 2032 aFrame, FrameWillChangeBudget(presContext, cost)); 2033 aFrame->SetMayHaveWillChangeBudget(true); 2034 } 2035 2036 return onBudget; 2037 } 2038 2039 bool nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame, 2040 const nsSize& aSize) { 2041 if (!IsForPainting()) { 2042 // If this nsDisplayListBuilder is not for painting, the layerization should 2043 // not matter. Do the simple thing and return false. 2044 return false; 2045 } 2046 2047 const bool onBudget = AddToWillChangeBudget(aFrame, aSize); 2048 if (onBudget) { 2049 return true; 2050 } 2051 2052 auto* pc = aFrame->PresContext(); 2053 auto* doc = pc->Document(); 2054 if (!doc->HasWarnedAbout(Document::eIgnoringWillChangeOverBudget)) { 2055 AutoTArray<nsString, 2> params; 2056 params.AppendElement()->AppendInt(gWillChangeAreaMultiplier); 2057 2058 nsRect area = pc->GetVisibleArea(); 2059 uint32_t budgetLimit = nsPresContext::AppUnitsToIntCSSPixels(area.width) * 2060 nsPresContext::AppUnitsToIntCSSPixels(area.height); 2061 params.AppendElement()->AppendInt(budgetLimit); 2062 2063 doc->WarnOnceAbout(Document::eIgnoringWillChangeOverBudget, false, params); 2064 } 2065 2066 return false; 2067 } 2068 2069 void nsDisplayListBuilder::ClearWillChangeBudgetStatus(nsIFrame* aFrame) { 2070 MOZ_ASSERT(IsForPainting()); 2071 2072 if (!aFrame->MayHaveWillChangeBudget()) { 2073 return; 2074 } 2075 2076 aFrame->SetMayHaveWillChangeBudget(false); 2077 RemoveFromWillChangeBudgets(aFrame); 2078 } 2079 2080 void nsDisplayListBuilder::RemoveFromWillChangeBudgets(const nsIFrame* aFrame) { 2081 if (auto entry = mFrameWillChangeBudgets.Lookup(aFrame)) { 2082 const FrameWillChangeBudget& frameBudget = entry.Data(); 2083 2084 auto documentBudget = 2085 mDocumentWillChangeBudgets.Lookup(frameBudget.mPresContext); 2086 2087 if (documentBudget) { 2088 *documentBudget -= frameBudget.mUsage; 2089 } 2090 2091 entry.Remove(); 2092 } 2093 } 2094 2095 void nsDisplayListBuilder::ClearWillChangeBudgets() { 2096 mFrameWillChangeBudgets.Clear(); 2097 mDocumentWillChangeBudgets.Clear(); 2098 } 2099 2100 void nsDisplayListBuilder::EnterSVGEffectsContents( 2101 nsIFrame* aEffectsFrame, nsDisplayList* aHoistedItemsStorage) { 2102 MOZ_ASSERT(aHoistedItemsStorage); 2103 if (mSVGEffectsFrames.IsEmpty()) { 2104 MOZ_ASSERT(!mScrollInfoItemsForHoisting); 2105 mScrollInfoItemsForHoisting = aHoistedItemsStorage; 2106 } 2107 mSVGEffectsFrames.AppendElement(aEffectsFrame); 2108 } 2109 2110 void nsDisplayListBuilder::ExitSVGEffectsContents() { 2111 MOZ_ASSERT(!mSVGEffectsFrames.IsEmpty()); 2112 mSVGEffectsFrames.RemoveLastElement(); 2113 MOZ_ASSERT(mScrollInfoItemsForHoisting); 2114 if (mSVGEffectsFrames.IsEmpty()) { 2115 mScrollInfoItemsForHoisting = nullptr; 2116 } 2117 } 2118 2119 bool nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting() const { 2120 /* 2121 * Note: if changing the conditions under which scroll info layers 2122 * are created, make a corresponding change to 2123 * ScrollFrameWillBuildScrollInfoLayer() in nsSliderFrame.cpp. 2124 */ 2125 for (nsIFrame* frame : mSVGEffectsFrames) { 2126 if (SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(frame)) { 2127 return true; 2128 } 2129 } 2130 return false; 2131 } 2132 2133 void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting( 2134 nsDisplayScrollInfoLayer* aScrollInfoItem) { 2135 MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting()); 2136 MOZ_ASSERT(mScrollInfoItemsForHoisting); 2137 mScrollInfoItemsForHoisting->AppendToTop(aScrollInfoItem); 2138 } 2139 2140 void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded( 2141 nsIFrame* aFrame, nsDisplayList* aList) { 2142 MOZ_ASSERT(aFrame); 2143 MOZ_ASSERT(aList); 2144 2145 if (!BuildCompositorHitTestInfo()) { 2146 return; 2147 } 2148 2149 const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(this); 2150 if (info != CompositorHitTestInvisibleToHit) { 2151 aList->AppendNewToTop<nsDisplayCompositorHitTestInfo>(this, aFrame); 2152 } 2153 } 2154 2155 void nsDisplayListBuilder::AddReusableDisplayItem(nsDisplayItem* aItem) { 2156 mReuseableItems.Insert(aItem); 2157 } 2158 2159 void nsDisplayListBuilder::RemoveReusedDisplayItem(nsDisplayItem* aItem) { 2160 MOZ_ASSERT(aItem->IsReusedItem()); 2161 mReuseableItems.Remove(aItem); 2162 } 2163 2164 void nsDisplayListBuilder::ClearReuseableDisplayItems() { 2165 const size_t total = mReuseableItems.Count(); 2166 2167 size_t reused = 0; 2168 for (auto* item : mReuseableItems) { 2169 if (item->IsReusedItem()) { 2170 reused++; 2171 item->SetReusable(); 2172 } else { 2173 item->Destroy(this); 2174 } 2175 } 2176 2177 DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total); 2178 mReuseableItems.Clear(); 2179 } 2180 2181 void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) { 2182 const auto* previous = mCurrentContainerASR; 2183 const auto* asr = aItem->GetActiveScrolledRoot(); 2184 mCurrentContainerASR = 2185 ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR); 2186 2187 if (previous != mCurrentContainerASR) { 2188 DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous, 2189 mCurrentContainerASR); 2190 } 2191 2192 aItem->SetReusedItem(); 2193 } 2194 2195 void nsDisplayListSet::CopyTo(const nsDisplayListSet& aDestination) const { 2196 for (size_t i = 0; i < mLists.size(); ++i) { 2197 auto* from = mLists[i]; 2198 auto* to = aDestination.mLists[i]; 2199 2200 from->CopyTo(to); 2201 } 2202 } 2203 2204 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const { 2205 aDestination.BorderBackground()->AppendToTop(BorderBackground()); 2206 aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds()); 2207 aDestination.Floats()->AppendToTop(Floats()); 2208 aDestination.Content()->AppendToTop(Content()); 2209 aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants()); 2210 aDestination.Outlines()->AppendToTop(Outlines()); 2211 } 2212 2213 nsRect nsDisplayList::GetClippedBounds(nsDisplayListBuilder* aBuilder) const { 2214 nsRect bounds; 2215 for (nsDisplayItem* i : *this) { 2216 bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder)); 2217 } 2218 return bounds; 2219 } 2220 2221 nsRect nsDisplayList::GetClippedBoundsWithRespectToASR( 2222 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR, 2223 nsRect* aBuildingRect) const { 2224 nsRect bounds; 2225 for (nsDisplayItem* i : *this) { 2226 nsRect r = i->GetClippedBounds(aBuilder); 2227 if (aASR != i->GetActiveScrolledRoot() && !r.IsEmpty()) { 2228 if (Maybe<nsRect> clip = i->GetClipWithRespectToASR(aBuilder, aASR)) { 2229 r = clip.ref(); 2230 } 2231 } 2232 if (aBuildingRect) { 2233 aBuildingRect->UnionRect(*aBuildingRect, i->GetBuildingRect()); 2234 } 2235 bounds.UnionRect(bounds, r); 2236 } 2237 return bounds; 2238 } 2239 2240 nsRect nsDisplayList::GetBuildingRect() const { 2241 nsRect result; 2242 for (nsDisplayItem* i : *this) { 2243 result.UnionRect(result, i->GetBuildingRect()); 2244 } 2245 return result; 2246 } 2247 2248 WindowRenderer* nsDisplayListBuilder::GetWidgetWindowRenderer() { 2249 if (RootReferenceFrame() != 2250 nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) { 2251 return nullptr; 2252 } 2253 if (nsIWidget* window = RootReferenceFrame()->GetNearestWidget()) { 2254 return window->GetWindowRenderer(); 2255 } 2256 return nullptr; 2257 } 2258 2259 WebRenderLayerManager* nsDisplayListBuilder::GetWidgetLayerManager() { 2260 if (WindowRenderer* renderer = GetWidgetWindowRenderer()) { 2261 return renderer->AsWebRender(); 2262 } 2263 return nullptr; 2264 } 2265 2266 void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx, 2267 int32_t aAppUnitsPerDevPixel) { 2268 FlattenedDisplayListIterator iter(aBuilder, this); 2269 while (iter.HasNext()) { 2270 nsPaintedDisplayItem* item = iter.GetNextItem()->AsPaintedDisplayItem(); 2271 if (!item) { 2272 continue; 2273 } 2274 2275 nsRect visible = item->GetClippedBounds(aBuilder); 2276 visible = visible.Intersect(item->GetPaintRect(aBuilder, aCtx)); 2277 if (visible.IsEmpty()) { 2278 continue; 2279 } 2280 2281 DisplayItemClip currentClip = item->GetClip(); 2282 if (currentClip.HasClip()) { 2283 aCtx->Save(); 2284 if (currentClip.IsRectClippedByRoundedCorner(visible)) { 2285 currentClip.ApplyTo(aCtx, aAppUnitsPerDevPixel); 2286 } else { 2287 currentClip.ApplyRectTo(aCtx, aAppUnitsPerDevPixel); 2288 } 2289 } 2290 aCtx->NewPath(); 2291 2292 item->Paint(aBuilder, aCtx); 2293 2294 if (currentClip.HasClip()) { 2295 aCtx->Restore(); 2296 } 2297 } 2298 } 2299 2300 /** 2301 * We paint by executing a layer manager transaction, constructing a 2302 * single layer representing the display list, and then making it the 2303 * root of the layer manager, drawing into the PaintedLayers. 2304 */ 2305 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx, 2306 uint32_t aFlags, 2307 Maybe<double> aDisplayListBuildTime) { 2308 AUTO_PROFILER_LABEL("nsDisplayList::PaintRoot", GRAPHICS); 2309 2310 RefPtr<WebRenderLayerManager> layerManager; 2311 WindowRenderer* renderer = nullptr; 2312 bool widgetTransaction = false; 2313 bool doBeginTransaction = true; 2314 if (aFlags & PAINT_USE_WIDGET_LAYERS) { 2315 renderer = aBuilder->GetWidgetWindowRenderer(); 2316 if (renderer) { 2317 // The fallback renderer doesn't retain any content, so it's 2318 // not meaningful to use it when drawing to an external context. 2319 if (aCtx && renderer->AsFallback()) { 2320 MOZ_ASSERT(!(aFlags & PAINT_EXISTING_TRANSACTION)); 2321 renderer = nullptr; 2322 } else { 2323 layerManager = renderer->AsWebRender(); 2324 doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION); 2325 widgetTransaction = true; 2326 } 2327 } 2328 } 2329 2330 nsIFrame* frame = aBuilder->RootReferenceFrame(); 2331 nsPresContext* presContext = frame->PresContext(); 2332 PresShell* presShell = presContext->PresShell(); 2333 Document* document = presShell->GetDocument(); 2334 2335 ScopeExit g([&]() { 2336 #ifdef DEBUG 2337 MOZ_ASSERT(!layerManager || !layerManager->GetTarget()); 2338 #endif 2339 2340 // For layers-free mode, we check the invalidation state bits in the 2341 // EndTransaction. So we clear the invalidation state bits after 2342 // EndTransaction. 2343 if (widgetTransaction || 2344 // SVG-as-an-image docs don't paint as part of the retained layer tree, 2345 // but they still need the invalidation state bits cleared in order for 2346 // invalidation for CSS/SMIL animation to work properly. 2347 (document && document->IsBeingUsedAsImage())) { 2348 DL_LOGD("Clearing invalidation state bits"); 2349 frame->ClearInvalidationStateBits(); 2350 } 2351 }); 2352 2353 if (!renderer) { 2354 if (!aCtx) { 2355 NS_WARNING("Nowhere to paint into"); 2356 return; 2357 } 2358 Paint(aBuilder, aCtx, presContext->AppUnitsPerDevPixel()); 2359 2360 return; 2361 } 2362 2363 if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) { 2364 MOZ_ASSERT(layerManager); 2365 if (doBeginTransaction) { 2366 if (aCtx) { 2367 if (!layerManager->BeginTransactionWithTarget(aCtx, nsCString())) { 2368 return; 2369 } 2370 } else { 2371 if (!layerManager->BeginTransaction(nsCString())) { 2372 return; 2373 } 2374 } 2375 } 2376 2377 layerManager->SetTransactionIdAllocator(presContext->RefreshDriver()); 2378 2379 bool sent = false; 2380 if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) { 2381 MOZ_ASSERT(!aCtx); 2382 sent = layerManager->EndEmptyTransaction(); 2383 } 2384 2385 if (!sent) { 2386 auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get()); 2387 2388 nsIDocShell* docShell = presContext->GetDocShell(); 2389 WrFiltersHolder wrFilters; 2390 gfx::Matrix5x4* colorMatrix = 2391 nsDocShell::Cast(docShell)->GetColorMatrix(); 2392 if (colorMatrix) { 2393 // Note: This color matrix was added here in for accessibility in 2394 // https://bugzilla.mozilla.org/show_bug.cgi?id=1431466 , it could be 2395 // done with regular SVG in the document as long as it is accelerated, 2396 // and it's probably best to do this in linearRGB, now that it is 2397 // feasible to do so 2398 // TODO(ahale): Make sure to test this works correctly before enabling 2399 if (StaticPrefs::gfx_webrender_svg_filter_effects() && 2400 StaticPrefs:: 2401 gfx_webrender_svg_filter_effects_also_use_for_docshell_fecolormatrix()) { 2402 // WebRender SVGFE code needs a valid filter region, so use 1<<30 as 2403 // rendering will already be heavily degraded at that range. 2404 static constexpr float kExtent = 1024.0f * 1024.0f * 1024.0f; 2405 wr::LayoutRect subregion = {{-kExtent, -kExtent}, {kExtent, kExtent}}; 2406 auto node = wr::FilterOpGraphNode{}; 2407 node.input.buffer_id = wr::FilterOpGraphPictureBufferId::None(); 2408 node.input2.buffer_id = wr::FilterOpGraphPictureBufferId::None(); 2409 node.subregion = subregion; 2410 wrFilters.filters.AppendElement( 2411 wr::FilterOp::SVGFESourceGraphic(node)); 2412 node.input.buffer_id = wr::FilterOpGraphPictureBufferId::BufferId(0); 2413 wrFilters.filters.AppendElement( 2414 wr::FilterOp::SVGFEColorMatrix(node, colorMatrix->components)); 2415 } else { 2416 wrFilters.filters.AppendElement( 2417 wr::FilterOp::ColorMatrix(colorMatrix->components)); 2418 } 2419 } 2420 2421 wrManager->EndTransactionWithoutLayer(this, aBuilder, 2422 std::move(wrFilters), nullptr, 2423 aDisplayListBuildTime.valueOr(0.0), 2424 aFlags & PAINT_COMPOSITE_OFFSCREEN); 2425 } 2426 2427 if (presContext->RefreshDriver()->IsInRefresh() || 2428 presContext->RefreshDriver()->IsPaintPending()) { 2429 presContext->NotifyInvalidation(layerManager->GetLastTransactionId(), 2430 frame->GetRect()); 2431 } 2432 return; 2433 } 2434 2435 FallbackRenderer* fallback = renderer->AsFallback(); 2436 MOZ_ASSERT(fallback); 2437 2438 if (doBeginTransaction) { 2439 MOZ_ASSERT(!aCtx); 2440 if (!fallback->BeginTransaction()) { 2441 return; 2442 } 2443 } 2444 2445 fallback->EndTransactionWithList(aBuilder, this, 2446 presContext->AppUnitsPerDevPixel(), 2447 WindowRenderer::END_DEFAULT); 2448 } 2449 2450 void nsDisplayList::DeleteAll(nsDisplayListBuilder* aBuilder) { 2451 for (auto* item : TakeItems()) { 2452 item->Destroy(aBuilder); 2453 } 2454 } 2455 2456 static bool IsFrameReceivingPointerEvents(nsIFrame* aFrame) { 2457 return aFrame->Style()->PointerEvents() != StylePointerEvents::None; 2458 } 2459 2460 // A list of frames, and their z depth. Used for sorting 2461 // the results of hit testing. 2462 struct FramesWithDepth { 2463 explicit FramesWithDepth(float aDepth) : mDepth(aDepth) {} 2464 2465 bool operator<(const FramesWithDepth& aOther) const { 2466 // We want to sort so that the shallowest item (highest depth value) is 2467 // first. Round to have some error tolerance (multiply with 8 translates 2468 // effectively to <<3). 2469 double lDepth = round(mDepth * 8.); 2470 double rDepth = round(aOther.mDepth * 8.); 2471 return lDepth > rDepth; 2472 } 2473 bool operator==(const FramesWithDepth& aOther) const { 2474 return this == &aOther; 2475 } 2476 2477 float mDepth; 2478 nsTArray<nsIFrame*> mFrames; 2479 }; 2480 2481 // Sort the frames by depth and then moves all the contained frames to the 2482 // destination 2483 static void FlushFramesArray(nsTArray<FramesWithDepth>& aSource, 2484 nsTArray<nsIFrame*>* aDest) { 2485 if (aSource.IsEmpty()) { 2486 return; 2487 } 2488 aSource.StableSort(); 2489 uint32_t length = aSource.Length(); 2490 for (uint32_t i = 0; i < length; i++) { 2491 aDest->AppendElements(std::move(aSource[i].mFrames)); 2492 } 2493 aSource.Clear(); 2494 } 2495 2496 void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, 2497 nsDisplayItem::HitTestState* aState, 2498 nsTArray<nsIFrame*>* aOutFrames) const { 2499 nsDisplayItem* item; 2500 2501 if (aState->mGatheringPreserves3DLeaves) { 2502 // Collect leaves of the current 3D rendering context. 2503 for (nsDisplayItem* item : *this) { 2504 auto itemType = item->GetType(); 2505 if (itemType != DisplayItemType::TYPE_TRANSFORM || 2506 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) { 2507 item->HitTest(aBuilder, aRect, aState, aOutFrames); 2508 } else { 2509 // One of leaves in the current 3D rendering context. 2510 aState->mItemBuffer.AppendElement(item); 2511 } 2512 } 2513 return; 2514 } 2515 2516 int32_t itemBufferStart = aState->mItemBuffer.Length(); 2517 for (nsDisplayItem* item : *this) { 2518 aState->mItemBuffer.AppendElement(item); 2519 } 2520 2521 AutoTArray<FramesWithDepth, 16> temp; 2522 for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart; 2523 --i) { 2524 // Pop element off the end of the buffer. We want to shorten the buffer 2525 // so that recursive calls to HitTest have more buffer space. 2526 item = aState->mItemBuffer[i]; 2527 aState->mItemBuffer.SetLength(i); 2528 2529 bool snap; 2530 nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect); 2531 auto itemType = item->GetType(); 2532 bool same3DContext = 2533 (itemType == DisplayItemType::TYPE_TRANSFORM && 2534 static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) || 2535 (itemType == DisplayItemType::TYPE_PERSPECTIVE && 2536 item->Frame()->Extend3DContext()); 2537 if (same3DContext && 2538 (itemType != DisplayItemType::TYPE_TRANSFORM || 2539 !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext())) { 2540 if (!item->GetClip().MayIntersect(aRect)) { 2541 continue; 2542 } 2543 AutoTArray<nsIFrame*, 1> neverUsed; 2544 // Start gathering leaves of the 3D rendering context, and 2545 // append leaves at the end of mItemBuffer. Leaves are 2546 // processed at following iterations. 2547 aState->mGatheringPreserves3DLeaves = true; 2548 item->HitTest(aBuilder, aRect, aState, &neverUsed); 2549 aState->mGatheringPreserves3DLeaves = false; 2550 i = aState->mItemBuffer.Length(); 2551 continue; 2552 } 2553 2554 if (!same3DContext && !item->GetClip().MayIntersect(r)) { 2555 continue; 2556 } 2557 2558 const bool savedTransformHasBackfaceVisible = 2559 aState->mTransformHasBackfaceVisible; 2560 if (aState->mTransformHasBackfaceVisible && 2561 !item->Combines3DTransformWithAncestors()) { 2562 // exiting a preserve 3d context, the transform is no longer applied to 2563 // this item, so reset the tracking var 2564 aState->mTransformHasBackfaceVisible = false; 2565 } 2566 AutoTArray<nsIFrame*, 16> outFrames; 2567 item->HitTest(aBuilder, aRect, aState, &outFrames); 2568 MOZ_ASSERT(!aState->mTransformHasBackfaceVisible || 2569 !item->In3DContextAndBackfaceIsHidden() || 2570 !outFrames.Contains(item->Frame())); 2571 aState->mTransformHasBackfaceVisible = savedTransformHasBackfaceVisible; 2572 2573 // For 3d transforms with preserve-3d we add hit frames into the temp list 2574 // so we can sort them later, otherwise we add them directly to the output 2575 // list. 2576 nsTArray<nsIFrame*>* writeFrames = aOutFrames; 2577 if (item->GetType() == DisplayItemType::TYPE_TRANSFORM && 2578 static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) { 2579 if (outFrames.Length()) { 2580 nsDisplayTransform* transform = static_cast<nsDisplayTransform*>(item); 2581 nsPoint point = aRect.TopLeft(); 2582 // A 1x1 rect means a point, otherwise use the center of the rect 2583 if (aRect.width != 1 || aRect.height != 1) { 2584 point = aRect.Center(); 2585 } 2586 temp.AppendElement( 2587 FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point))); 2588 writeFrames = &temp[temp.Length() - 1].mFrames; 2589 } 2590 } else { 2591 // We may have just finished a run of consecutive preserve-3d 2592 // transforms, so flush these into the destination array before 2593 // processing our frame list. 2594 FlushFramesArray(temp, aOutFrames); 2595 } 2596 2597 for (uint32_t j = 0; j < outFrames.Length(); j++) { 2598 nsIFrame* f = outFrames.ElementAt(j); 2599 // Filter out some frames depending on the type of hittest 2600 // we are doing. For visibility tests, pass through all frames. 2601 // For pointer tests, only pass through frames that are styled 2602 // to receive pointer events. 2603 if (aBuilder->HitTestIsForVisibility() || 2604 IsFrameReceivingPointerEvents(f)) { 2605 writeFrames->AppendElement(f); 2606 } 2607 } 2608 2609 if (aBuilder->HitTestIsForVisibility()) { 2610 aState->mHitOccludingItem = [&] { 2611 if (aState->mHitOccludingItem) { 2612 // We already hit something before. 2613 return true; 2614 } 2615 if (aState->mCurrentOpacity == 1.0f && 2616 item->GetOpaqueRegion(aBuilder, &snap).Contains(aRect)) { 2617 // An opaque item always occludes everything. Note that we need to 2618 // check wrapping opacity and such as well. 2619 return true; 2620 } 2621 float threshold = aBuilder->VisibilityThreshold(); 2622 if (threshold == 1.0f) { 2623 return false; 2624 } 2625 float itemOpacity = [&] { 2626 switch (item->GetType()) { 2627 case DisplayItemType::TYPE_OPACITY: 2628 return static_cast<nsDisplayOpacity*>(item)->GetOpacity(); 2629 case DisplayItemType::TYPE_BACKGROUND_COLOR: 2630 return static_cast<nsDisplayBackgroundColor*>(item)->GetOpacity(); 2631 default: 2632 // Be conservative and assume it won't occlude other items. 2633 return 0.0f; 2634 } 2635 }(); 2636 return itemOpacity * aState->mCurrentOpacity >= threshold; 2637 }(); 2638 2639 if (aState->mHitOccludingItem) { 2640 // We're exiting early, so pop the remaining items off the buffer. 2641 aState->mItemBuffer.TruncateLength(itemBufferStart); 2642 break; 2643 } 2644 } 2645 } 2646 // Clear any remaining preserve-3d transforms. 2647 FlushFramesArray(temp, aOutFrames); 2648 NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart), 2649 "How did we forget to pop some elements?"); 2650 } 2651 2652 static nsIContent* FindContentInDocument(nsDisplayItem* aItem, Document* aDoc) { 2653 nsIFrame* f = aItem->Frame(); 2654 while (f) { 2655 nsPresContext* pc = f->PresContext(); 2656 if (pc->Document() == aDoc) { 2657 return f->GetContent(); 2658 } 2659 f = nsLayoutUtils::GetCrossDocParentFrameInProcess( 2660 pc->PresShell()->GetRootFrame()); 2661 } 2662 return nullptr; 2663 } 2664 2665 struct ZSortItem { 2666 nsDisplayItem* item; 2667 int32_t zIndex; 2668 2669 explicit ZSortItem(nsDisplayItem* aItem) 2670 : item(aItem), zIndex(aItem->ZIndex()) {} 2671 2672 operator nsDisplayItem*() { return item; } 2673 }; 2674 2675 struct ZOrderComparator { 2676 bool LessThan(const ZSortItem& aLeft, const ZSortItem& aRight) const { 2677 return aLeft.zIndex < aRight.zIndex; 2678 } 2679 }; 2680 2681 void nsDisplayList::SortByZOrder() { Sort<ZSortItem>(ZOrderComparator()); } 2682 2683 struct ContentComparator { 2684 nsIContent* mCommonAncestor; 2685 2686 explicit ContentComparator(nsIContent* aCommonAncestor) 2687 : mCommonAncestor(aCommonAncestor) {} 2688 2689 bool LessThan(nsDisplayItem* aLeft, nsDisplayItem* aRight) const { 2690 // It's possible that the nsIContent for aItem1 or aItem2 is in a 2691 // subdocument of commonAncestor, because display items for subdocuments 2692 // have been mixed into the same list. Ensure that we're looking at content 2693 // in commonAncestor's document. 2694 Document* commonAncestorDoc = mCommonAncestor->OwnerDoc(); 2695 nsIContent* content1 = FindContentInDocument(aLeft, commonAncestorDoc); 2696 nsIContent* content2 = FindContentInDocument(aRight, commonAncestorDoc); 2697 if (!content1 || !content2) { 2698 NS_ERROR("Document trees are mixed up!"); 2699 // Something weird going on 2700 return true; 2701 } 2702 return content1 != content2 && 2703 nsContentUtils::CompareTreePosition<TreeKind::Flat>( 2704 content1, content2, mCommonAncestor) < 0; 2705 } 2706 }; 2707 2708 void nsDisplayList::SortByContentOrder(nsIContent* aCommonAncestor) { 2709 Sort<nsDisplayItem*>(ContentComparator(aCommonAncestor)); 2710 } 2711 2712 #if !defined(DEBUG) && !defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) 2713 static_assert(sizeof(nsDisplayItem) <= 176, "nsDisplayItem has grown"); 2714 #endif 2715 2716 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) 2717 : nsDisplayItem(aBuilder, aFrame, aBuilder->CurrentActiveScrolledRoot()) {} 2718 2719 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, 2720 const ActiveScrolledRoot* aActiveScrolledRoot) 2721 : mFrame(aFrame), mActiveScrolledRoot(aActiveScrolledRoot) { 2722 MOZ_COUNT_CTOR(nsDisplayItem); 2723 MOZ_ASSERT(mFrame); 2724 if (aBuilder->IsRetainingDisplayList()) { 2725 mFrame->AddDisplayItem(this); 2726 } 2727 2728 aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame); 2729 NS_ASSERTION( 2730 aBuilder->GetVisibleRect().width >= 0 || !aBuilder->IsForPainting(), 2731 "visible rect not set"); 2732 2733 mClipChain = aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder); 2734 2735 // The visible rect is for mCurrentFrame, so we have to use 2736 // mCurrentOffsetToReferenceFrame 2737 nsRect visible = aBuilder->GetVisibleRect() + 2738 aBuilder->GetCurrentFrameOffsetToReferenceFrame(); 2739 SetBuildingRect(visible); 2740 2741 const nsStyleDisplay* disp = mFrame->StyleDisplay(); 2742 if (mFrame->BackfaceIsHidden(disp)) { 2743 mItemFlags += ItemFlag::BackfaceHidden; 2744 } 2745 if (mFrame->Combines3DTransformWithAncestors()) { 2746 mItemFlags += ItemFlag::Combines3DTransformWithAncestors; 2747 } 2748 } 2749 2750 void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; } 2751 2752 const ActiveScrolledRoot* nsDisplayItem::GetNearestScrollASR() const { 2753 const ActiveScrolledRoot* asr = GetActiveScrolledRoot(); 2754 if (asr) { 2755 return asr->GetNearestScrollASR(); 2756 } 2757 return nullptr; 2758 } 2759 2760 bool nsDisplayItem::HasDeletedFrame() const { 2761 bool retval = mItemFlags.contains(ItemFlag::DeletedFrame) || 2762 (GetType() == DisplayItemType::TYPE_REMOTE && 2763 !static_cast<const nsDisplayRemote*>(this)->GetFrameLoader()); 2764 MOZ_ASSERT(retval || mFrame); 2765 return retval; 2766 } 2767 2768 /* static */ 2769 bool nsDisplayItem::ForceActiveLayers() { 2770 return StaticPrefs::layers_force_active(); 2771 } 2772 2773 int32_t nsDisplayItem::ZIndex() const { return mFrame->ZIndex().valueOr(0); } 2774 2775 void nsDisplayItem::SetClipChain(const DisplayItemClipChain* aClipChain, 2776 bool aStore) { 2777 mClipChain = aClipChain; 2778 } 2779 2780 Maybe<nsRect> nsDisplayItem::GetClipWithRespectToASR( 2781 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const { 2782 if (const DisplayItemClip* clip = 2783 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) { 2784 return Some(clip->GetClipRect()); 2785 } 2786 2787 return Nothing(); 2788 } 2789 2790 const DisplayItemClip& nsDisplayItem::GetClip() const { 2791 const DisplayItemClip* clip = 2792 DisplayItemClipChain::ClipForASR(mClipChain, mActiveScrolledRoot); 2793 return clip ? *clip : DisplayItemClip::NoClip(); 2794 } 2795 2796 void nsDisplayItem::IntersectClip(nsDisplayListBuilder* aBuilder, 2797 const DisplayItemClipChain* aOther, 2798 bool aStore) { 2799 if (!aOther || mClipChain == aOther) { 2800 return; 2801 } 2802 2803 // aOther might be a reference to a clip on the stack. We need to make sure 2804 // that CreateClipChainIntersection will allocate the actual intersected 2805 // clip in the builder's arena, so for the mClipChain == nullptr case, 2806 // we supply nullptr as the common ancestor so that 2807 // CreateClipChainIntersection clones the whole chain. 2808 const DisplayItemClipChain* ancestorClip = 2809 mClipChain ? FindCommonAncestorClipForIntersection(mClipChain, aOther) 2810 : nullptr; 2811 2812 SetClipChain( 2813 aBuilder->CreateClipChainIntersection(ancestorClip, mClipChain, aOther), 2814 aStore); 2815 } 2816 2817 nsRect nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) const { 2818 bool snap; 2819 nsRect r = GetBounds(aBuilder, &snap); 2820 return GetClip().ApplyNonRoundedIntersection(r); 2821 } 2822 2823 nsDisplayContainer::nsDisplayContainer( 2824 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, 2825 const ActiveScrolledRoot* aActiveScrolledRoot, 2826 ContainerASRType aContainerASRType, nsDisplayList* aList) 2827 : nsDisplayItem(aBuilder, aFrame, aActiveScrolledRoot), 2828 mChildren(aBuilder), 2829 mFrameASR(aContainerASRType == ContainerASRType::AncestorOfContained 2830 ? aBuilder->CurrentActiveScrolledRoot() 2831 : nullptr), 2832 mContainerASRType(aContainerASRType) { 2833 MOZ_COUNT_CTOR(nsDisplayContainer); 2834 mChildren.AppendToTop(aList); 2835 UpdateBounds(aBuilder); 2836 2837 // Clear and store the clip chain set by nsDisplayItem constructor. 2838 nsDisplayItem::SetClipChain(nullptr, true); 2839 } 2840 2841 nsRect nsDisplayItem::GetPaintRect(nsDisplayListBuilder* aBuilder, 2842 gfxContext* aCtx) { 2843 bool dummy; 2844 nsRect result = GetBounds(aBuilder, &dummy); 2845 if (aCtx) { 2846 result.IntersectRect(result, 2847 nsLayoutUtils::RoundGfxRectToAppRect( 2848 aCtx->GetClipExtents(), 2849 mFrame->PresContext()->AppUnitsPerDevPixel())); 2850 } 2851 return result; 2852 } 2853 2854 bool nsDisplayContainer::CreateWebRenderCommands( 2855 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 2856 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 2857 nsDisplayListBuilder* aDisplayListBuilder) { 2858 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList( 2859 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources, 2860 false); 2861 return true; 2862 } 2863 2864 nsRect nsDisplayContainer::GetBounds(nsDisplayListBuilder* aBuilder, 2865 bool* aSnap) const { 2866 *aSnap = false; 2867 return mBounds; 2868 } 2869 2870 nsRect nsDisplayContainer::GetComponentAlphaBounds( 2871 nsDisplayListBuilder* aBuilder) const { 2872 return mChildren.GetComponentAlphaBounds(aBuilder); 2873 } 2874 2875 static nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, 2876 nsDisplayList* aList, 2877 const nsRect& aListBounds) { 2878 return aList->GetOpaqueRegion(aBuilder); 2879 } 2880 2881 nsRegion nsDisplayContainer::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, 2882 bool* aSnap) const { 2883 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), 2884 GetBounds(aBuilder, aSnap)); 2885 } 2886 2887 Maybe<nsRect> nsDisplayContainer::GetClipWithRespectToASR( 2888 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const { 2889 // Our children should have finite bounds with respect to |aASR|. 2890 if (aASR == mActiveScrolledRoot) { 2891 return Some(mBounds); 2892 } 2893 2894 return Some(mChildren.GetClippedBoundsWithRespectToASR(aBuilder, aASR)); 2895 } 2896 2897 void nsDisplayContainer::HitTest(nsDisplayListBuilder* aBuilder, 2898 const nsRect& aRect, HitTestState* aState, 2899 nsTArray<nsIFrame*>* aOutFrames) { 2900 mChildren.HitTest(aBuilder, aRect, aState, aOutFrames); 2901 } 2902 2903 void nsDisplayContainer::UpdateBounds(nsDisplayListBuilder* aBuilder) { 2904 // Container item bounds are expected to be clipped. 2905 mBounds = 2906 mChildren.GetClippedBoundsWithRespectToASR(aBuilder, mActiveScrolledRoot); 2907 } 2908 2909 nsRect nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder, 2910 bool* aSnap) const { 2911 *aSnap = true; 2912 return mBounds; 2913 } 2914 2915 void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder, 2916 gfxContext* aCtx) { 2917 if (!NS_GET_A(mColor)) { 2918 return; 2919 } 2920 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); 2921 DrawTarget* drawTarget = aCtx->GetDrawTarget(); 2922 Rect rect = NSRectToSnappedRect(GetPaintRect(aBuilder, aCtx), 2923 appUnitsPerDevPixel, *drawTarget); 2924 drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(mColor))); 2925 } 2926 2927 void nsDisplaySolidColor::WriteDebugInfo(std::stringstream& aStream) { 2928 aStream << " (rgba " << (int)NS_GET_R(mColor) << "," << (int)NS_GET_G(mColor) 2929 << "," << (int)NS_GET_B(mColor) << "," << (int)NS_GET_A(mColor) 2930 << ")"; 2931 } 2932 2933 bool nsDisplaySolidColor::CreateWebRenderCommands( 2934 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 2935 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 2936 nsDisplayListBuilder* aDisplayListBuilder) { 2937 if (!NS_GET_A(mColor)) { 2938 return true; 2939 } 2940 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits( 2941 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel()); 2942 wr::LayoutRect r = wr::ToLayoutRect(bounds); 2943 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, mIsCheckerboardBackground, 2944 wr::ToColorF(ToDeviceColor(mColor))); 2945 2946 return true; 2947 } 2948 2949 nsRect nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder, 2950 bool* aSnap) const { 2951 *aSnap = true; 2952 return mRegion.GetBounds(); 2953 } 2954 2955 void nsDisplaySolidColorRegion::Paint(nsDisplayListBuilder* aBuilder, 2956 gfxContext* aCtx) { 2957 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); 2958 DrawTarget* drawTarget = aCtx->GetDrawTarget(); 2959 ColorPattern color(ToDeviceColor(mColor)); 2960 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) { 2961 Rect rect = 2962 NSRectToSnappedRect(iter.Get(), appUnitsPerDevPixel, *drawTarget); 2963 drawTarget->FillRect(rect, color); 2964 } 2965 } 2966 2967 void nsDisplaySolidColorRegion::WriteDebugInfo(std::stringstream& aStream) { 2968 aStream << " (rgba " << int(mColor.r * 255) << "," << int(mColor.g * 255) 2969 << "," << int(mColor.b * 255) << "," << mColor.a << ")"; 2970 } 2971 2972 bool nsDisplaySolidColorRegion::CreateWebRenderCommands( 2973 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 2974 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 2975 nsDisplayListBuilder* aDisplayListBuilder) { 2976 for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) { 2977 nsRect rect = iter.Get(); 2978 LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits( 2979 rect, mFrame->PresContext()->AppUnitsPerDevPixel()); 2980 wr::LayoutRect r = wr::ToLayoutRect(layerRects); 2981 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false, 2982 wr::ToColorF(ToDeviceColor(mColor))); 2983 } 2984 2985 return true; 2986 } 2987 2988 static void RegisterThemeGeometry(nsDisplayListBuilder* aBuilder, 2989 nsDisplayItem* aItem, nsIFrame* aFrame, 2990 nsITheme::ThemeGeometryType aType) { 2991 if (aBuilder->IsInChromeDocumentOrPopup()) { 2992 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame); 2993 bool preservesAxisAlignedRectangles = false; 2994 nsRect borderBox = nsLayoutUtils::TransformFrameRectToAncestor( 2995 aFrame, aFrame->GetRectRelativeToSelf(), displayRoot, 2996 &preservesAxisAlignedRectangles); 2997 if (preservesAxisAlignedRectangles) { 2998 aBuilder->RegisterThemeGeometry( 2999 aType, aItem, 3000 LayoutDeviceIntRect::FromUnknownRect(borderBox.ToNearestPixels( 3001 aFrame->PresContext()->AppUnitsPerDevPixel()))); 3002 } 3003 } 3004 } 3005 3006 // Return the bounds of the viewport relative to |aFrame|'s reference frame. 3007 // Returns Nothing() if transforming into |aFrame|'s coordinate space fails. 3008 static Maybe<nsRect> GetViewportRectRelativeToReferenceFrame( 3009 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) { 3010 nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame(); 3011 nsRect rootRect = rootFrame->GetRectRelativeToSelf(); 3012 if (nsLayoutUtils::TransformRect(rootFrame, aFrame, rootRect) == 3013 nsLayoutUtils::TRANSFORM_SUCCEEDED) { 3014 return Some(rootRect + aBuilder->ToReferenceFrame(aFrame)); 3015 } 3016 return Nothing(); 3017 } 3018 3019 /* static */ nsDisplayBackgroundImage::InitData 3020 nsDisplayBackgroundImage::GetInitData(nsDisplayListBuilder* aBuilder, 3021 nsIFrame* aFrame, uint16_t aLayer, 3022 const nsRect& aBackgroundRect, 3023 const ComputedStyle* aBackgroundStyle) { 3024 nsPresContext* presContext = aFrame->PresContext(); 3025 uint32_t flags = aBuilder->GetBackgroundPaintFlags(); 3026 const nsStyleImageLayers::Layer& layer = 3027 aBackgroundStyle->StyleBackground()->mImage.mLayers[aLayer]; 3028 3029 bool isTransformedFixed; 3030 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer( 3031 presContext, aFrame, flags, aBackgroundRect, aBackgroundRect, layer, 3032 &isTransformedFixed); 3033 3034 // background-attachment:fixed is treated as background-attachment:scroll 3035 // if it's affected by a transform. 3036 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521. 3037 bool shouldTreatAsFixed = 3038 layer.mAttachment == StyleImageLayerAttachment::Fixed && 3039 !isTransformedFixed; 3040 3041 bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsNone(); 3042 bool isRasterImage = state.mImageRenderer.IsRasterImage(); 3043 nsCOMPtr<imgIContainer> image; 3044 if (isRasterImage) { 3045 image = state.mImageRenderer.GetImage(); 3046 } 3047 return InitData{aBuilder, aBackgroundStyle, image, 3048 aBackgroundRect, state.mFillArea, state.mDestArea, 3049 aLayer, isRasterImage, shouldFixToViewport}; 3050 } 3051 3052 nsDisplayBackgroundImage::nsDisplayBackgroundImage( 3053 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aInitData, 3054 nsIFrame* aFrameForBounds) 3055 : nsPaintedDisplayItem(aBuilder, aFrame), 3056 mBackgroundStyle(aInitData.backgroundStyle), 3057 mImage(aInitData.image), 3058 mDependentFrame(nullptr), 3059 mBackgroundRect(aInitData.backgroundRect), 3060 mFillRect(aInitData.fillArea), 3061 mDestRect(aInitData.destArea), 3062 mLayer(aInitData.layer), 3063 mIsRasterImage(aInitData.isRasterImage) { 3064 MOZ_COUNT_CTOR(nsDisplayBackgroundImage); 3065 #ifdef DEBUG 3066 if (mBackgroundStyle && mBackgroundStyle != mFrame->Style()) { 3067 // If this changes, then you also need to adjust css::ImageLoader to 3068 // invalidate mFrame as needed. 3069 MOZ_ASSERT(mFrame->IsCanvasFrame() || mFrame->IsTablePart()); 3070 } 3071 #endif 3072 3073 mBounds = GetBoundsInternal(aInitData.builder, aFrameForBounds); 3074 if (aInitData.shouldFixToViewport) { 3075 // Expand the item's visible rect to cover the entire bounds, limited to the 3076 // viewport rect. This is necessary because the background's clip can move 3077 // asynchronously. 3078 if (Maybe<nsRect> viewportRect = GetViewportRectRelativeToReferenceFrame( 3079 aInitData.builder, mFrame)) { 3080 SetBuildingRect(mBounds.Intersect(*viewportRect)); 3081 } 3082 } 3083 } 3084 3085 void nsDisplayBackgroundImage::Destroy(nsDisplayListBuilder* aBuilder) { 3086 RemoveDisplayItemFromFrame(aBuilder, mDependentFrame); 3087 nsPaintedDisplayItem::Destroy(aBuilder); 3088 } 3089 3090 static void SetBackgroundClipRegion( 3091 DisplayListClipState::AutoSaveRestore& aClipState, nsIFrame* aFrame, 3092 const nsStyleImageLayers::Layer& aLayer, const nsRect& aBackgroundRect, 3093 bool aWillPaintBorder) { 3094 nsCSSRendering::ImageLayerClipState clip; 3095 nsCSSRendering::GetImageLayerClip( 3096 aLayer, aFrame, *aFrame->StyleBorder(), aBackgroundRect, aBackgroundRect, 3097 aWillPaintBorder, aFrame->PresContext()->AppUnitsPerDevPixel(), &clip); 3098 3099 if (clip.mHasAdditionalBGClipArea) { 3100 aClipState.ClipContentDescendants( 3101 clip.mAdditionalBGClipArea, clip.mBGClipArea, 3102 clip.mHasRoundedCorners ? &clip.mRadii : nullptr); 3103 } else { 3104 aClipState.ClipContentDescendants( 3105 clip.mBGClipArea, clip.mHasRoundedCorners ? &clip.mRadii : nullptr); 3106 } 3107 } 3108 3109 /** 3110 * This is used for the find bar highlighter overlay. It's only accessible 3111 * through the AnonymousContent API, so it's not exposed to general web pages. 3112 */ 3113 static bool SpecialCutoutRegionCase(nsDisplayListBuilder* aBuilder, 3114 nsIFrame* aFrame, 3115 const nsRect& aBackgroundRect, 3116 nsDisplayList* aList, nscolor aColor) { 3117 nsIContent* content = aFrame->GetContent(); 3118 if (!content) { 3119 return false; 3120 } 3121 3122 void* cutoutRegion = content->GetProperty(nsGkAtoms::cutoutregion); 3123 if (!cutoutRegion) { 3124 return false; 3125 } 3126 3127 if (NS_GET_A(aColor) == 0) { 3128 return true; 3129 } 3130 3131 nsRegion region; 3132 region.Sub(aBackgroundRect, *static_cast<nsRegion*>(cutoutRegion)); 3133 region.MoveBy(aBuilder->ToReferenceFrame(aFrame)); 3134 aList->AppendNewToTop<nsDisplaySolidColorRegion>(aBuilder, aFrame, region, 3135 aColor); 3136 3137 return true; 3138 } 3139 3140 enum class TableType : uint8_t { 3141 Table, 3142 TableCol, 3143 TableColGroup, 3144 TableRow, 3145 TableRowGroup, 3146 TableCell, 3147 3148 MAX, 3149 }; 3150 3151 enum class TableTypeBits : uint8_t { Count = 3 }; 3152 3153 static_assert(static_cast<uint8_t>(TableType::MAX) < 3154 (1 << (static_cast<uint8_t>(TableTypeBits::Count) + 1)), 3155 "TableType cannot fit with TableTypeBits::Count"); 3156 TableType GetTableTypeFromFrame(nsIFrame* aFrame); 3157 3158 static uint16_t CalculateTablePerFrameKey(const uint16_t aIndex, 3159 const TableType aType) { 3160 const uint32_t key = (aIndex << static_cast<uint8_t>(TableTypeBits::Count)) | 3161 static_cast<uint8_t>(aType); 3162 3163 return static_cast<uint16_t>(key); 3164 } 3165 3166 static nsDisplayBackgroundImage* CreateBackgroundImage( 3167 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame, 3168 const nsDisplayBackgroundImage::InitData& aBgData) { 3169 const auto index = aBgData.layer; 3170 3171 if (aSecondaryFrame) { 3172 const auto tableType = GetTableTypeFromFrame(aFrame); 3173 const uint16_t tableItemIndex = CalculateTablePerFrameKey(index, tableType); 3174 3175 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundImage>( 3176 aBuilder, aSecondaryFrame, tableItemIndex, aBgData, aFrame); 3177 } 3178 3179 return MakeDisplayItemWithIndex<nsDisplayBackgroundImage>(aBuilder, aFrame, 3180 index, aBgData); 3181 } 3182 3183 static nsDisplayThemedBackground* CreateThemedBackground( 3184 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame, 3185 const nsRect& aBgRect) { 3186 if (aSecondaryFrame) { 3187 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame)); 3188 return MakeDisplayItemWithIndex<nsDisplayTableThemedBackground>( 3189 aBuilder, aSecondaryFrame, index, aBgRect, aFrame); 3190 } 3191 3192 return MakeDisplayItem<nsDisplayThemedBackground>(aBuilder, aFrame, aBgRect); 3193 } 3194 3195 static nsDisplayBackgroundColor* CreateBackgroundColor( 3196 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame, 3197 nsRect& aBgRect, const ComputedStyle* aBgSC, nscolor aColor) { 3198 if (aSecondaryFrame) { 3199 const uint16_t index = static_cast<uint16_t>(GetTableTypeFromFrame(aFrame)); 3200 return MakeDisplayItemWithIndex<nsDisplayTableBackgroundColor>( 3201 aBuilder, aSecondaryFrame, index, aBgRect, aBgSC, aColor, aFrame); 3202 } 3203 3204 return MakeDisplayItem<nsDisplayBackgroundColor>(aBuilder, aFrame, aBgRect, 3205 aBgSC, aColor); 3206 } 3207 3208 static void DealWithWindowsAppearanceHacks(nsIFrame* aFrame, 3209 nsDisplayListBuilder* aBuilder) { 3210 if (!XRE_IsParentProcess()) { 3211 return; 3212 } 3213 3214 const auto& disp = *aFrame->StyleDisplay(); 3215 3216 // We use default appearance rather than effective appearance because we want 3217 // to handle when titlebar buttons that have appearance: none. 3218 const auto defaultAppearance = disp.mDefaultAppearance; 3219 if (MOZ_LIKELY(defaultAppearance == StyleAppearance::None)) { 3220 return; 3221 } 3222 3223 if (auto type = disp.GetWindowButtonType()) { 3224 if (auto* widget = aFrame->GetNearestWidget()) { 3225 auto rect = LayoutDevicePixel::FromAppUnitsToNearest( 3226 nsRect(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize()), 3227 aFrame->PresContext()->AppUnitsPerDevPixel()); 3228 widget->SetWindowButtonRect(*type, rect); 3229 } 3230 } 3231 } 3232 3233 /*static*/ 3234 AppendedBackgroundType nsDisplayBackgroundImage::AppendBackgroundItemsToTop( 3235 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, 3236 const nsRect& aBackgroundRect, nsDisplayList* aList, 3237 bool aAllowWillPaintBorderOptimization, const nsRect& aBackgroundOriginRect, 3238 nsIFrame* aSecondaryReferenceFrame, 3239 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>* 3240 aAutoBuildingDisplayList) { 3241 MOZ_ASSERT(!aFrame->IsCanvasFrame(), 3242 "We don't expect propagated canvas backgrounds here"); 3243 #ifdef DEBUG 3244 { 3245 nsIFrame* bgFrame = nsCSSRendering::FindBackgroundFrame(aFrame); 3246 MOZ_ASSERT( 3247 !bgFrame || bgFrame == aFrame, 3248 "Should only suppress backgrounds, never propagate to another frame"); 3249 } 3250 #endif 3251 3252 DealWithWindowsAppearanceHacks(aFrame, aBuilder); 3253 3254 const bool isThemed = aFrame->IsThemed(); 3255 3256 const ComputedStyle* bgSC = aFrame->Style(); 3257 const nsStyleBackground* bg = bgSC->StyleBackground(); 3258 const bool needsBackgroundColor = 3259 aBuilder->IsForEventDelivery() || 3260 (EffectCompositor::HasAnimationsForCompositor( 3261 aFrame, DisplayItemType::TYPE_BACKGROUND_COLOR) && 3262 !isThemed); 3263 if (!needsBackgroundColor && !isThemed && bg->IsTransparent(bgSC)) { 3264 return AppendedBackgroundType::None; 3265 } 3266 3267 bool drawBackgroundColor = false; 3268 bool drawBackgroundImage = false; 3269 nscolor color = NS_RGBA(0, 0, 0, 0); 3270 // Don't get background color / images if we propagated our background to the 3271 // canvas (that is, if FindBackgroundFrame is null). But don't early return 3272 // yet, since we might still need a background-color item for hit-testing. 3273 if (!isThemed && nsCSSRendering::FindBackgroundFrame(aFrame)) { 3274 color = nsCSSRendering::DetermineBackgroundColor( 3275 aFrame->PresContext(), bgSC, aFrame, drawBackgroundImage, 3276 drawBackgroundColor); 3277 } 3278 3279 if (SpecialCutoutRegionCase(aBuilder, aFrame, aBackgroundRect, aList, 3280 color)) { 3281 return AppendedBackgroundType::None; 3282 } 3283 3284 const nsStyleBorder& border = *aFrame->StyleBorder(); 3285 const bool willPaintBorder = 3286 aAllowWillPaintBorderOptimization && !isThemed && 3287 !aFrame->StyleEffects()->HasBoxShadowWithInset(true) && 3288 border.HasBorder(); 3289 3290 auto EnsureBuildingDisplayList = [&] { 3291 if (!aAutoBuildingDisplayList || *aAutoBuildingDisplayList) { 3292 return; 3293 } 3294 nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(aFrame); 3295 aAutoBuildingDisplayList->emplace(aBuilder, aFrame, 3296 aBuilder->GetVisibleRect() + offset, 3297 aBuilder->GetDirtyRect() + offset); 3298 }; 3299 3300 // An auxiliary list is necessary in case we have background blending; if that 3301 // is the case, background items need to be wrapped by a blend container to 3302 // isolate blending to the background 3303 nsDisplayList bgItemList(aBuilder); 3304 // Even if we don't actually have a background color to paint, we may still 3305 // need to create an item for hit testing and we still need to create an item 3306 // for background-color animations. 3307 if ((drawBackgroundColor && color != NS_RGBA(0, 0, 0, 0)) || 3308 needsBackgroundColor) { 3309 EnsureBuildingDisplayList(); 3310 Maybe<DisplayListClipState::AutoSaveRestore> clipState; 3311 nsRect bgColorRect = aBackgroundRect; 3312 if (!isThemed && !aBuilder->IsForEventDelivery()) { 3313 // Disable the will-paint-border optimization for background 3314 // colors with no border-radius. Enabling it for background colors 3315 // doesn't help much (there are no tiling issues) and clipping the 3316 // background breaks detection of the element's border-box being 3317 // opaque. For nonzero border-radius we still need it because we 3318 // want to inset the background if possible to avoid antialiasing 3319 // artifacts along the rounded corners. 3320 const bool useWillPaintBorderOptimization = 3321 willPaintBorder && 3322 nsLayoutUtils::HasNonZeroCorner(border.mBorderRadius); 3323 3324 nsCSSRendering::ImageLayerClipState clip; 3325 nsCSSRendering::GetImageLayerClip( 3326 bg->BottomLayer(), aFrame, border, aBackgroundRect, aBackgroundRect, 3327 useWillPaintBorderOptimization, 3328 aFrame->PresContext()->AppUnitsPerDevPixel(), &clip); 3329 3330 bgColorRect = bgColorRect.Intersect(clip.mBGClipArea); 3331 if (clip.mHasAdditionalBGClipArea) { 3332 bgColorRect = bgColorRect.Intersect(clip.mAdditionalBGClipArea); 3333 } 3334 if (clip.mHasRoundedCorners) { 3335 clipState.emplace(aBuilder); 3336 clipState->ClipContentDescendants(clip.mBGClipArea, &clip.mRadii); 3337 } 3338 } 3339 3340 nsDisplayBackgroundColor* bgItem = CreateBackgroundColor( 3341 aBuilder, aFrame, aSecondaryReferenceFrame, bgColorRect, bgSC, 3342 drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0)); 3343 3344 if (bgItem) { 3345 bgItemList.AppendToTop(bgItem); 3346 } 3347 } 3348 3349 if (isThemed) { 3350 nsDisplayThemedBackground* bgItem = CreateThemedBackground( 3351 aBuilder, aFrame, aSecondaryReferenceFrame, aBackgroundRect); 3352 3353 if (bgItem) { 3354 bgItem->Init(aBuilder); 3355 bgItemList.AppendToTop(bgItem); 3356 } 3357 3358 if (!bgItemList.IsEmpty()) { 3359 aList->AppendToTop(&bgItemList); 3360 return AppendedBackgroundType::ThemedBackground; 3361 } 3362 3363 return AppendedBackgroundType::None; 3364 } 3365 3366 if (!drawBackgroundImage) { 3367 if (!bgItemList.IsEmpty()) { 3368 aList->AppendToTop(&bgItemList); 3369 return AppendedBackgroundType::Background; 3370 } 3371 3372 return AppendedBackgroundType::None; 3373 } 3374 3375 const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot(); 3376 3377 bool needBlendContainer = false; 3378 const nsRect& bgOriginRect = 3379 aBackgroundOriginRect.IsEmpty() ? aBackgroundRect : aBackgroundOriginRect; 3380 3381 // Passing bg == nullptr in this macro will result in one iteration with 3382 // i = 0. 3383 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, bg->mImage) { 3384 if (bg->mImage.mLayers[i].mImage.IsNone()) { 3385 continue; 3386 } 3387 3388 EnsureBuildingDisplayList(); 3389 3390 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) { 3391 needBlendContainer = true; 3392 } 3393 3394 DisplayListClipState::AutoSaveRestore clipState(aBuilder); 3395 if (!aBuilder->IsForEventDelivery()) { 3396 const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i]; 3397 SetBackgroundClipRegion(clipState, aFrame, layer, aBackgroundRect, 3398 willPaintBorder); 3399 } 3400 3401 nsDisplayList thisItemList(aBuilder); 3402 nsDisplayBackgroundImage::InitData bgData = 3403 nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect, 3404 bgSC); 3405 3406 if (bgData.shouldFixToViewport) { 3407 auto* displayData = aBuilder->GetCurrentFixedBackgroundDisplayData(); 3408 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList( 3409 aBuilder, aFrame, aBuilder->GetVisibleRect(), 3410 aBuilder->GetDirtyRect()); 3411 3412 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter( 3413 aBuilder); 3414 if (displayData) { 3415 asrSetter.SetCurrentActiveScrolledRoot( 3416 displayData->mContainingBlockActiveScrolledRoot); 3417 asrSetter.SetCurrentScrollParentId(displayData->mScrollParentId); 3418 if (nsLayoutUtils::UsesAsyncScrolling(aFrame)) { 3419 // Override the dirty rect on the builder to be the dirty rect of 3420 // the viewport. 3421 // displayData->mDirtyRect is relative to the presshell's viewport 3422 // frame (the root frame), and we need it to be relative to aFrame. 3423 nsIFrame* rootFrame = 3424 aBuilder->CurrentPresShellState()->mPresShell->GetRootFrame(); 3425 // There cannot be any transforms between aFrame and rootFrame 3426 // because then bgData.shouldFixToViewport would have been false. 3427 nsRect visibleRect = 3428 displayData->mVisibleRect + aFrame->GetOffsetTo(rootFrame); 3429 aBuilder->SetVisibleRect(visibleRect); 3430 nsRect dirtyRect = 3431 displayData->mDirtyRect + aFrame->GetOffsetTo(rootFrame); 3432 aBuilder->SetDirtyRect(dirtyRect); 3433 } 3434 } 3435 3436 nsDisplayBackgroundImage* bgItem = nullptr; 3437 { 3438 // The clip is captured by the nsDisplayFixedPosition, so clear the 3439 // clip for the nsDisplayBackgroundImage inside. 3440 DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder); 3441 bgImageClip.Clear(); 3442 bgItem = CreateBackgroundImage(aBuilder, aFrame, 3443 aSecondaryReferenceFrame, bgData); 3444 } 3445 if (bgItem) { 3446 const ActiveScrolledRoot* scrollTargetASR = 3447 asr ? asr->GetNearestScrollASR() : nullptr; 3448 thisItemList.AppendToTop( 3449 nsDisplayFixedPosition::CreateForFixedBackground( 3450 aBuilder, aFrame, aSecondaryReferenceFrame, bgItem, i, 3451 scrollTargetASR)); 3452 } 3453 } else { // bgData.shouldFixToViewport == false 3454 nsDisplayBackgroundImage* bgItem = CreateBackgroundImage( 3455 aBuilder, aFrame, aSecondaryReferenceFrame, bgData); 3456 if (bgItem) { 3457 thisItemList.AppendToTop(bgItem); 3458 } 3459 } 3460 3461 if (bg->mImage.mLayers[i].mBlendMode != StyleBlend::Normal) { 3462 // asr is scrolled. Even if we wrap a fixed background layer, that's 3463 // fine, because the item will have a scrolled clip that limits the 3464 // item with respect to asr. 3465 if (aSecondaryReferenceFrame) { 3466 const auto tableType = GetTableTypeFromFrame(aFrame); 3467 const uint16_t index = CalculateTablePerFrameKey(i + 1, tableType); 3468 3469 thisItemList.AppendNewToTopWithIndex<nsDisplayTableBlendMode>( 3470 aBuilder, aSecondaryReferenceFrame, index, &thisItemList, 3471 bg->mImage.mLayers[i].mBlendMode, asr, ContainerASRType::Constant, 3472 aFrame, true); 3473 } else { 3474 thisItemList.AppendNewToTopWithIndex<nsDisplayBlendMode>( 3475 aBuilder, aFrame, i + 1, &thisItemList, 3476 bg->mImage.mLayers[i].mBlendMode, asr, ContainerASRType::Constant, 3477 true); 3478 } 3479 } 3480 bgItemList.AppendToTop(&thisItemList); 3481 } 3482 3483 if (needBlendContainer) { 3484 bgItemList.AppendToTop( 3485 nsDisplayBlendContainer::CreateForBackgroundBlendMode( 3486 aBuilder, aFrame, aSecondaryReferenceFrame, &bgItemList, asr, 3487 nsDisplayItem::ContainerASRType::Constant)); 3488 } 3489 3490 if (!bgItemList.IsEmpty()) { 3491 aList->AppendToTop(&bgItemList); 3492 return AppendedBackgroundType::Background; 3493 } 3494 3495 return AppendedBackgroundType::None; 3496 } 3497 3498 // Check that the rounded border of aFrame, added to aToReferenceFrame, 3499 // intersects aRect. Assumes that the unrounded border has already 3500 // been checked for intersection. 3501 static bool RoundedBorderIntersectsRect(nsIFrame* aFrame, 3502 const nsPoint& aFrameToReferenceFrame, 3503 const nsRect& aTestRect) { 3504 if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize()) 3505 .Intersects(aTestRect)) { 3506 return false; 3507 } 3508 3509 nsRectCornerRadii radii; 3510 return !aFrame->GetBorderRadii(radii) || 3511 nsLayoutUtils::RoundedRectIntersectsRect( 3512 nsRect(aFrameToReferenceFrame, aFrame->GetSize()), radii, 3513 aTestRect); 3514 } 3515 3516 // Returns TRUE if aContainedRect is guaranteed to be contained in 3517 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are 3518 // handled conservatively by returning FALSE in some situations where 3519 // a more thorough analysis could return TRUE. 3520 // 3521 // See also RoundedRectIntersectsRect. 3522 static bool RoundedRectContainsRect(const nsRect& aRoundedRect, 3523 const nsRectCornerRadii& aRadii, 3524 const nsRect& aContainedRect) { 3525 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii, 3526 aContainedRect); 3527 return rgn.Contains(aContainedRect); 3528 } 3529 3530 bool nsDisplayBackgroundImage::CanApplyOpacity( 3531 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const { 3532 return CanBuildWebRenderDisplayItems(aManager, aBuilder); 3533 } 3534 3535 bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems( 3536 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const { 3537 return mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip != 3538 StyleGeometryBox::Text && 3539 nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer( 3540 aManager, *StyleFrame()->PresContext(), StyleFrame(), 3541 mBackgroundStyle->StyleBackground(), mLayer, 3542 aBuilder->GetBackgroundPaintFlags()); 3543 } 3544 3545 bool nsDisplayBackgroundImage::CreateWebRenderCommands( 3546 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 3547 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 3548 nsDisplayListBuilder* aDisplayListBuilder) { 3549 if (!CanBuildWebRenderDisplayItems(aManager->LayerManager(), 3550 aDisplayListBuilder)) { 3551 return false; 3552 } 3553 3554 uint32_t paintFlags = aDisplayListBuilder->GetBackgroundPaintFlags(); 3555 bool dummy; 3556 nsCSSRendering::PaintBGParams params = 3557 nsCSSRendering::PaintBGParams::ForSingleLayer( 3558 *StyleFrame()->PresContext(), GetBounds(aDisplayListBuilder, &dummy), 3559 mBackgroundRect, StyleFrame(), paintFlags, mLayer, 3560 CompositionOp::OP_OVER, aBuilder.GetInheritedOpacity()); 3561 params.bgClipRect = &mBounds; 3562 ImgDrawResult result = 3563 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer( 3564 params, aBuilder, aResources, aSc, aManager, this); 3565 if (result == ImgDrawResult::NOT_SUPPORTED) { 3566 return false; 3567 } 3568 3569 if (nsIContent* content = StyleFrame()->GetContent()) { 3570 if (imgRequestProxy* requestProxy = mBackgroundStyle->StyleBackground() 3571 ->mImage.mLayers[mLayer] 3572 .mImage.GetImageRequest()) { 3573 // LCP don't consider gradient backgrounds. 3574 LCPHelpers::FinalizeLCPEntryForImage(content->AsElement(), requestProxy, 3575 mBounds - ToReferenceFrame()); 3576 } 3577 } 3578 3579 return true; 3580 } 3581 3582 void nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder, 3583 const nsRect& aRect, 3584 HitTestState* aState, 3585 nsTArray<nsIFrame*>* aOutFrames) { 3586 if (ShouldIgnoreForBackfaceHidden(aState)) { 3587 return; 3588 } 3589 3590 if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) { 3591 aOutFrames->AppendElement(mFrame); 3592 } 3593 } 3594 3595 static nsRect GetInsideClipRect(const nsDisplayItem* aItem, 3596 StyleGeometryBox aClip, const nsRect& aRect, 3597 const nsRect& aBackgroundRect) { 3598 if (aRect.IsEmpty()) { 3599 return {}; 3600 } 3601 3602 nsIFrame* frame = aItem->Frame(); 3603 3604 nsRect clipRect = aBackgroundRect; 3605 if (frame->IsCanvasFrame()) { 3606 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame); 3607 clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame(); 3608 } else if (aClip == StyleGeometryBox::PaddingBox || 3609 aClip == StyleGeometryBox::ContentBox) { 3610 nsMargin border = frame->GetUsedBorder(); 3611 if (aClip == StyleGeometryBox::ContentBox) { 3612 border += frame->GetUsedPadding(); 3613 } 3614 border.ApplySkipSides(frame->GetSkipSides()); 3615 clipRect.Deflate(border); 3616 } 3617 3618 return clipRect.Intersect(aRect); 3619 } 3620 3621 nsRegion nsDisplayBackgroundImage::GetOpaqueRegion( 3622 nsDisplayListBuilder* aBuilder, bool* aSnap) const { 3623 nsRegion result; 3624 *aSnap = false; 3625 3626 if (!mBackgroundStyle) { 3627 return result; 3628 } 3629 3630 *aSnap = true; 3631 3632 // For StyleBoxDecorationBreak::Slice, don't try to optimize here, since 3633 // this could easily lead to O(N^2) behavior inside InlineBackgroundData, 3634 // which expects frames to be sent to it in content order, not reverse 3635 // content order which we'll produce here. 3636 // Of course, if there's only one frame in the flow, it doesn't matter. 3637 if (mFrame->StyleBorder()->mBoxDecorationBreak == 3638 StyleBoxDecorationBreak::Clone || 3639 (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) { 3640 const nsStyleImageLayers::Layer& layer = 3641 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer]; 3642 if (layer.mImage.IsOpaque() && layer.mBlendMode == StyleBlend::Normal && 3643 layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space && 3644 layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space && 3645 layer.mClip != StyleGeometryBox::Text) { 3646 result = GetInsideClipRect(this, layer.mClip, mBounds, mBackgroundRect); 3647 } 3648 } 3649 3650 return result; 3651 } 3652 3653 Maybe<nscolor> nsDisplayBackgroundImage::IsUniform( 3654 nsDisplayListBuilder* aBuilder) const { 3655 if (!mBackgroundStyle) { 3656 return Some(NS_RGBA(0, 0, 0, 0)); 3657 } 3658 return Nothing(); 3659 } 3660 3661 nsRect nsDisplayBackgroundImage::GetPositioningArea() const { 3662 if (!mBackgroundStyle) { 3663 return nsRect(); 3664 } 3665 nsIFrame* attachedToFrame; 3666 bool transformedFixed; 3667 return nsCSSRendering::ComputeImageLayerPositioningArea( 3668 mFrame->PresContext(), mFrame, mBackgroundRect, 3669 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer], 3670 &attachedToFrame, &transformedFixed) + 3671 ToReferenceFrame(); 3672 } 3673 3674 bool nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange() 3675 const { 3676 if (!mBackgroundStyle) { 3677 return false; 3678 } 3679 3680 nsRectCornerRadii radii; 3681 if (mFrame->GetBorderRadii(radii)) { 3682 // A change in the size of the positioning area might change the position 3683 // of the rounded corners. 3684 return true; 3685 } 3686 3687 const nsStyleImageLayers::Layer& layer = 3688 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer]; 3689 return layer.RenderingMightDependOnPositioningAreaSizeChange(); 3690 } 3691 3692 void nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder, 3693 gfxContext* aCtx) { 3694 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), &mBounds); 3695 } 3696 3697 void nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder, 3698 gfxContext* aCtx, 3699 const nsRect& aBounds, 3700 nsRect* aClipRect) { 3701 gfxContext* ctx = aCtx; 3702 StyleGeometryBox clip = 3703 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer].mClip; 3704 3705 if (clip == StyleGeometryBox::Text) { 3706 if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect, 3707 aBuilder)) { 3708 return; 3709 } 3710 } 3711 3712 nsCSSRendering::PaintBGParams params = 3713 nsCSSRendering::PaintBGParams::ForSingleLayer( 3714 *StyleFrame()->PresContext(), aBounds, mBackgroundRect, StyleFrame(), 3715 aBuilder->GetBackgroundPaintFlags(), mLayer, CompositionOp::OP_OVER, 3716 1.0f); 3717 params.bgClipRect = aClipRect; 3718 (void)nsCSSRendering::PaintStyleImageLayer(params, *aCtx); 3719 3720 if (clip == StyleGeometryBox::Text) { 3721 ctx->PopGroupAndBlend(); 3722 } 3723 } 3724 3725 void nsDisplayBackgroundImage::ComputeInvalidationRegion( 3726 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, 3727 nsRegion* aInvalidRegion) const { 3728 if (!mBackgroundStyle) { 3729 return; 3730 } 3731 3732 const auto* geometry = 3733 static_cast<const nsDisplayBackgroundGeometry*>(aGeometry); 3734 3735 bool snap; 3736 nsRect bounds = GetBounds(aBuilder, &snap); 3737 nsRect positioningArea = GetPositioningArea(); 3738 if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() || 3739 (positioningArea.Size() != geometry->mPositioningArea.Size() && 3740 RenderingMightDependOnPositioningAreaSizeChange())) { 3741 // Positioning area changed in a way that could cause everything to change, 3742 // so invalidate everything (both old and new painting areas). 3743 aInvalidRegion->Or(bounds, geometry->mBounds); 3744 return; 3745 } 3746 if (!mDestRect.IsEqualInterior(geometry->mDestRect)) { 3747 // Dest area changed in a way that could cause everything to change, 3748 // so invalidate everything (both old and new painting areas). 3749 aInvalidRegion->Or(bounds, geometry->mBounds); 3750 return; 3751 } 3752 if (!bounds.IsEqualInterior(geometry->mBounds)) { 3753 // Positioning area is unchanged, so invalidate just the change in the 3754 // painting area. 3755 aInvalidRegion->Xor(bounds, geometry->mBounds); 3756 } 3757 } 3758 3759 nsRect nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder, 3760 bool* aSnap) const { 3761 *aSnap = true; 3762 return mBounds; 3763 } 3764 3765 nsRect nsDisplayBackgroundImage::GetBoundsInternal( 3766 nsDisplayListBuilder* aBuilder, nsIFrame* aFrameForBounds) { 3767 // This allows nsDisplayTableBackgroundImage to change the frame used for 3768 // bounds calculation. 3769 nsIFrame* frame = aFrameForBounds ? aFrameForBounds : mFrame; 3770 3771 nsPresContext* presContext = frame->PresContext(); 3772 3773 if (!mBackgroundStyle) { 3774 return nsRect(); 3775 } 3776 3777 nsRect clipRect = mBackgroundRect; 3778 if (frame->IsCanvasFrame()) { 3779 nsCanvasFrame* canvasFrame = static_cast<nsCanvasFrame*>(frame); 3780 clipRect = canvasFrame->CanvasArea() + ToReferenceFrame(); 3781 } 3782 const nsStyleImageLayers::Layer& layer = 3783 mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer]; 3784 return nsCSSRendering::GetBackgroundLayerRect( 3785 presContext, frame, mBackgroundRect, clipRect, layer, 3786 aBuilder->GetBackgroundPaintFlags()); 3787 } 3788 3789 nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage( 3790 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const InitData& aData, 3791 nsIFrame* aCellFrame) 3792 : nsDisplayBackgroundImage(aBuilder, aFrame, aData, aCellFrame), 3793 mStyleFrame(aCellFrame) { 3794 if (aBuilder->IsRetainingDisplayList()) { 3795 mStyleFrame->AddDisplayItem(this); 3796 } 3797 } 3798 3799 void nsDisplayTableBackgroundImage::Destroy(nsDisplayListBuilder* aBuilder) { 3800 RemoveDisplayItemFromFrame(aBuilder, mStyleFrame); 3801 nsDisplayBackgroundImage::Destroy(aBuilder); 3802 } 3803 3804 bool nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) const { 3805 bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false; 3806 aRect += ToReferenceFrame(); 3807 return result; 3808 } 3809 3810 nsDisplayThemedBackground::nsDisplayThemedBackground( 3811 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, 3812 const nsRect& aBackgroundRect) 3813 : nsPaintedDisplayItem(aBuilder, aFrame), mBackgroundRect(aBackgroundRect) { 3814 MOZ_COUNT_CTOR(nsDisplayThemedBackground); 3815 } 3816 3817 void nsDisplayThemedBackground::Init(nsDisplayListBuilder* aBuilder) { 3818 const nsStyleDisplay* disp = StyleFrame()->StyleDisplay(); 3819 mAppearance = disp->EffectiveAppearance(); 3820 StyleFrame()->IsThemed(disp, &mThemeTransparency); 3821 3822 // Perform necessary RegisterThemeGeometry 3823 nsITheme* theme = StyleFrame()->PresContext()->Theme(); 3824 nsITheme::ThemeGeometryType type = 3825 theme->ThemeGeometryTypeForWidget(StyleFrame(), mAppearance); 3826 if (type != nsITheme::eThemeGeometryTypeUnknown) { 3827 RegisterThemeGeometry(aBuilder, this, StyleFrame(), type); 3828 } 3829 3830 mBounds = GetBoundsInternal(); 3831 } 3832 3833 void nsDisplayThemedBackground::WriteDebugInfo(std::stringstream& aStream) { 3834 aStream << " (themed, appearance:" << (int)mAppearance << ")"; 3835 } 3836 3837 void nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder, 3838 const nsRect& aRect, 3839 HitTestState* aState, 3840 nsTArray<nsIFrame*>* aOutFrames) { 3841 if (ShouldIgnoreForBackfaceHidden(aState)) { 3842 return; 3843 } 3844 3845 // Assume that any point in our background rect is a hit. 3846 if (mBackgroundRect.Intersects(aRect)) { 3847 aOutFrames->AppendElement(mFrame); 3848 } 3849 } 3850 3851 nsRegion nsDisplayThemedBackground::GetOpaqueRegion( 3852 nsDisplayListBuilder* aBuilder, bool* aSnap) const { 3853 nsRegion result; 3854 *aSnap = false; 3855 3856 if (mThemeTransparency == nsITheme::eOpaque) { 3857 *aSnap = true; 3858 result = mBackgroundRect; 3859 } 3860 return result; 3861 } 3862 3863 Maybe<nscolor> nsDisplayThemedBackground::IsUniform( 3864 nsDisplayListBuilder* aBuilder) const { 3865 return Nothing(); 3866 } 3867 3868 nsRect nsDisplayThemedBackground::GetPositioningArea() const { 3869 return mBackgroundRect; 3870 } 3871 3872 void nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder, 3873 gfxContext* aCtx) { 3874 PaintInternal(aBuilder, aCtx, GetPaintRect(aBuilder, aCtx), nullptr); 3875 } 3876 3877 void nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder, 3878 gfxContext* aCtx, 3879 const nsRect& aBounds, 3880 nsRect* aClipRect) { 3881 // XXXzw this ignores aClipRect. 3882 nsPresContext* presContext = StyleFrame()->PresContext(); 3883 nsITheme* theme = presContext->Theme(); 3884 nsRect drawing(mBackgroundRect); 3885 theme->GetWidgetOverflow(presContext->DeviceContext(), StyleFrame(), 3886 mAppearance, &drawing); 3887 drawing.IntersectRect(drawing, aBounds); 3888 theme->DrawWidgetBackground(aCtx, StyleFrame(), mAppearance, mBackgroundRect, 3889 drawing); 3890 } 3891 3892 bool nsDisplayThemedBackground::CreateWebRenderCommands( 3893 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 3894 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 3895 nsDisplayListBuilder* aDisplayListBuilder) { 3896 nsITheme* theme = StyleFrame()->PresContext()->Theme(); 3897 return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc, 3898 aManager, StyleFrame(), 3899 mAppearance, mBackgroundRect); 3900 } 3901 3902 bool nsDisplayThemedBackground::IsWindowActive() const { 3903 return !mFrame->PresContext()->Document()->IsTopLevelWindowInactive(); 3904 } 3905 3906 void nsDisplayThemedBackground::ComputeInvalidationRegion( 3907 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, 3908 nsRegion* aInvalidRegion) const { 3909 const auto* geometry = 3910 static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry); 3911 3912 bool snap; 3913 nsRect bounds = GetBounds(aBuilder, &snap); 3914 nsRect positioningArea = GetPositioningArea(); 3915 if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) { 3916 // Invalidate everything (both old and new painting areas). 3917 aInvalidRegion->Or(bounds, geometry->mBounds); 3918 return; 3919 } 3920 if (!bounds.IsEqualInterior(geometry->mBounds)) { 3921 // Positioning area is unchanged, so invalidate just the change in the 3922 // painting area. 3923 aInvalidRegion->Xor(bounds, geometry->mBounds); 3924 } 3925 nsITheme* theme = StyleFrame()->PresContext()->Theme(); 3926 if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) && 3927 IsWindowActive() != geometry->mWindowIsActive) { 3928 aInvalidRegion->Or(*aInvalidRegion, bounds); 3929 } 3930 } 3931 3932 nsRect nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder, 3933 bool* aSnap) const { 3934 *aSnap = true; 3935 return mBounds; 3936 } 3937 3938 nsRect nsDisplayThemedBackground::GetBoundsInternal() { 3939 nsPresContext* presContext = mFrame->PresContext(); 3940 3941 nsRect r = mBackgroundRect - ToReferenceFrame(); 3942 presContext->Theme()->GetWidgetOverflow( 3943 presContext->DeviceContext(), mFrame, 3944 mFrame->StyleDisplay()->EffectiveAppearance(), &r); 3945 return r + ToReferenceFrame(); 3946 } 3947 3948 void nsDisplayTableThemedBackground::Destroy(nsDisplayListBuilder* aBuilder) { 3949 RemoveDisplayItemFromFrame(aBuilder, mAncestorFrame); 3950 nsDisplayThemedBackground::Destroy(aBuilder); 3951 } 3952 3953 #if defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF) 3954 void nsDisplayReflowCount::Paint(nsDisplayListBuilder* aBuilder, 3955 gfxContext* aCtx) { 3956 mFrame->PresShell()->PaintCount(mFrameName, aCtx, mFrame->PresContext(), 3957 mFrame, ToReferenceFrame(), mColor); 3958 } 3959 #endif 3960 3961 void nsDisplayBackgroundColor::Destroy(nsDisplayListBuilder* aBuilder) { 3962 RemoveDisplayItemFromFrame(aBuilder, mDependentFrame); 3963 nsPaintedDisplayItem::Destroy(aBuilder); 3964 } 3965 3966 bool nsDisplayBackgroundColor::CanApplyOpacity( 3967 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder) const { 3968 // Don't apply opacity if the background color is animated since the color is 3969 // going to be changed on the compositor. 3970 return !EffectCompositor::HasAnimationsForCompositor( 3971 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR); 3972 } 3973 3974 bool nsDisplayBackgroundColor::CreateWebRenderCommands( 3975 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 3976 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 3977 nsDisplayListBuilder* aDisplayListBuilder) { 3978 gfx::sRGBColor color = mColor; 3979 color.a *= aBuilder.GetInheritedOpacity(); 3980 3981 if (color == sRGBColor() && 3982 !EffectCompositor::HasAnimationsForCompositor( 3983 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) { 3984 return true; 3985 } 3986 3987 if (HasBackgroundClipText()) { 3988 return false; 3989 } 3990 3991 uint64_t animationsId = 0; 3992 // We don't support background-color animations on table elements yet. 3993 if (GetType() == DisplayItemType::TYPE_BACKGROUND_COLOR) { 3994 animationsId = 3995 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder); 3996 } 3997 3998 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits( 3999 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel()); 4000 wr::LayoutRect r = wr::ToLayoutRect(bounds); 4001 4002 if (animationsId) { 4003 wr::WrAnimationProperty prop{ 4004 wr::WrAnimationType::BackgroundColor, 4005 animationsId, 4006 }; 4007 aBuilder.PushRectWithAnimation(r, r, !BackfaceIsHidden(), 4008 wr::ToColorF(ToDeviceColor(color)), &prop); 4009 } else { 4010 aBuilder.StartGroup(this); 4011 aBuilder.PushRect(r, r, !BackfaceIsHidden(), false, false, 4012 wr::ToColorF(ToDeviceColor(color))); 4013 aBuilder.FinishGroup(); 4014 } 4015 4016 return true; 4017 } 4018 4019 void nsDisplayBackgroundColor::PaintWithClip(nsDisplayListBuilder* aBuilder, 4020 gfxContext* aCtx, 4021 const DisplayItemClip& aClip) { 4022 MOZ_ASSERT(!HasBackgroundClipText()); 4023 4024 if (mColor == sRGBColor()) { 4025 return; 4026 } 4027 4028 nsRect fillRect = mBackgroundRect; 4029 if (aClip.HasClip()) { 4030 fillRect.IntersectRect(fillRect, aClip.GetClipRect()); 4031 } 4032 4033 DrawTarget* dt = aCtx->GetDrawTarget(); 4034 int32_t A2D = mFrame->PresContext()->AppUnitsPerDevPixel(); 4035 Rect bounds = ToRect(nsLayoutUtils::RectToGfxRect(fillRect, A2D)); 4036 MaybeSnapToDevicePixels(bounds, *dt); 4037 ColorPattern fill(ToDeviceColor(mColor)); 4038 4039 if (aClip.GetRoundedRectCount()) { 4040 MOZ_ASSERT(aClip.GetRoundedRectCount() == 1); 4041 4042 AutoTArray<DisplayItemClip::RoundedRect, 1> roundedRect; 4043 aClip.AppendRoundedRects(&roundedRect); 4044 4045 bool pushedClip = false; 4046 if (!fillRect.Contains(roundedRect[0].mRect)) { 4047 dt->PushClipRect(bounds); 4048 pushedClip = true; 4049 } 4050 4051 RectCornerRadii pixelRadii; 4052 nsCSSRendering::ComputePixelRadii(roundedRect[0].mRadii, A2D, &pixelRadii); 4053 dt->FillRoundedRect( 4054 RoundedRect(NSRectToSnappedRect(roundedRect[0].mRect, A2D, *dt), 4055 pixelRadii), 4056 fill); 4057 if (pushedClip) { 4058 dt->PopClip(); 4059 } 4060 } else { 4061 dt->FillRect(bounds, fill); 4062 } 4063 } 4064 4065 void nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder, 4066 gfxContext* aCtx) { 4067 if (mColor == sRGBColor()) { 4068 return; 4069 } 4070 4071 #if 0 4072 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1148418#c21 for why this 4073 // results in a precision induced rounding issue that makes the rect one 4074 // pixel shorter in rare cases. Disabled in favor of the old code for now. 4075 // Note that the pref layout.css.devPixelsPerPx needs to be set to 1 to 4076 // reproduce the bug. 4077 // 4078 // TODO: 4079 // This new path does not include support for background-clip:text; need to 4080 // be fixed if/when we switch to this new code path. 4081 4082 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget(); 4083 4084 Rect rect = NSRectToSnappedRect(mBackgroundRect, 4085 mFrame->PresContext()->AppUnitsPerDevPixel(), 4086 aDrawTarget); 4087 ColorPattern color(ToDeviceColor(mColor)); 4088 aDrawTarget.FillRect(rect, color); 4089 #else 4090 gfxContext* ctx = aCtx; 4091 gfxRect bounds = nsLayoutUtils::RectToGfxRect( 4092 mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel()); 4093 4094 if (HasBackgroundClipText()) { 4095 if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) { 4096 return; 4097 } 4098 4099 ctx->SetColor(mColor); 4100 ctx->NewPath(); 4101 ctx->SnappedRectangle(bounds); 4102 ctx->Fill(); 4103 ctx->PopGroupAndBlend(); 4104 return; 4105 } 4106 4107 ctx->SetColor(mColor); 4108 ctx->NewPath(); 4109 ctx->SnappedRectangle(bounds); 4110 ctx->Fill(); 4111 #endif 4112 } 4113 4114 nsRegion nsDisplayBackgroundColor::GetOpaqueRegion( 4115 nsDisplayListBuilder* aBuilder, bool* aSnap) const { 4116 *aSnap = false; 4117 4118 if (mColor.a != 1 || 4119 // Even if the current alpha channel is 1, we treat this item as if it's 4120 // non-opaque if there is a background-color animation since the animation 4121 // might change the alpha channel. 4122 EffectCompositor::HasAnimationsForCompositor( 4123 mFrame, DisplayItemType::TYPE_BACKGROUND_COLOR)) { 4124 return nsRegion(); 4125 } 4126 4127 if (!mHasStyle || HasBackgroundClipText()) { 4128 return nsRegion(); 4129 } 4130 4131 *aSnap = true; 4132 return GetInsideClipRect(this, mBottomLayerClip, mBackgroundRect, 4133 mBackgroundRect); 4134 } 4135 4136 Maybe<nscolor> nsDisplayBackgroundColor::IsUniform( 4137 nsDisplayListBuilder* aBuilder) const { 4138 return Some(mColor.ToABGR()); 4139 } 4140 4141 void nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder, 4142 const nsRect& aRect, 4143 HitTestState* aState, 4144 nsTArray<nsIFrame*>* aOutFrames) { 4145 if (ShouldIgnoreForBackfaceHidden(aState)) { 4146 return; 4147 } 4148 4149 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) { 4150 // aRect doesn't intersect our border-radius curve. 4151 return; 4152 } 4153 4154 aOutFrames->AppendElement(mFrame); 4155 } 4156 4157 void nsDisplayBackgroundColor::WriteDebugInfo(std::stringstream& aStream) { 4158 aStream << " (rgba " << mColor.r << "," << mColor.g << "," << mColor.b << "," 4159 << mColor.a << ")"; 4160 aStream << " backgroundRect" << mBackgroundRect; 4161 } 4162 4163 nsRect nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder, 4164 bool* aSnap) const { 4165 *aSnap = false; 4166 return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame(); 4167 } 4168 4169 nsRect nsDisplayOutline::GetInnerRect() const { 4170 if (nsRect* savedOutlineInnerRect = 4171 mFrame->GetProperty(nsIFrame::OutlineInnerRectProperty())) { 4172 return *savedOutlineInnerRect; 4173 } 4174 return mFrame->GetRectRelativeToSelf(); 4175 } 4176 4177 void nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) { 4178 // TODO join outlines together 4179 MOZ_ASSERT(mFrame->StyleOutline()->ShouldPaintOutline(), 4180 "Should have not created a nsDisplayOutline!"); 4181 4182 nsRect rect = GetInnerRect() + ToReferenceFrame(); 4183 nsPresContext* pc = mFrame->PresContext(); 4184 if (IsThemedOutline()) { 4185 pc->Theme()->DrawWidgetBackground(aCtx, mFrame, 4186 StyleAppearance::FocusOutline, rect, 4187 GetPaintRect(aBuilder, aCtx)); 4188 return; 4189 } 4190 4191 nsCSSRendering::PaintNonThemedOutline( 4192 pc, *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), rect, mFrame->Style()); 4193 } 4194 4195 bool nsDisplayOutline::IsThemedOutline() const { 4196 #ifdef DEBUG 4197 nsPresContext* pc = mFrame->PresContext(); 4198 MOZ_ASSERT( 4199 pc->Theme()->ThemeSupportsWidget(pc, mFrame, 4200 StyleAppearance::FocusOutline), 4201 "All of our supported platforms have support for themed focus-outlines"); 4202 #endif 4203 return mFrame->StyleOutline()->mOutlineStyle.IsAuto(); 4204 } 4205 4206 bool nsDisplayOutline::CreateWebRenderCommands( 4207 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 4208 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 4209 nsDisplayListBuilder* aDisplayListBuilder) { 4210 nsPresContext* pc = mFrame->PresContext(); 4211 nsRect rect = GetInnerRect() + ToReferenceFrame(); 4212 if (IsThemedOutline()) { 4213 return pc->Theme()->CreateWebRenderCommandsForWidget( 4214 aBuilder, aResources, aSc, aManager, mFrame, 4215 StyleAppearance::FocusOutline, rect); 4216 } 4217 4218 bool dummy; 4219 Maybe<nsCSSBorderRenderer> borderRenderer = 4220 nsCSSRendering::CreateBorderRendererForNonThemedOutline( 4221 pc, /* aDrawTarget = */ nullptr, mFrame, 4222 GetBounds(aDisplayListBuilder, &dummy), rect, mFrame->Style()); 4223 4224 if (!borderRenderer) { 4225 // No border renderer means "there is no outline". 4226 // Paint nothing and return success. 4227 return true; 4228 } 4229 4230 borderRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc); 4231 return true; 4232 } 4233 4234 bool nsDisplayOutline::HasRadius() const { 4235 const auto& radius = mFrame->StyleBorder()->mBorderRadius; 4236 return !nsLayoutUtils::HasNonZeroCorner(radius); 4237 } 4238 4239 bool nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect) const { 4240 nsRect borderBox(ToReferenceFrame(), mFrame->GetSize()); 4241 // aRect is entirely inside the border-rect, and the outline isn't rendered 4242 // inside the border-rect, so the outline is not visible. 4243 return borderBox.Contains(aRect) && !HasRadius() && 4244 mFrame->StyleOutline()->mOutlineOffset >= 0; 4245 } 4246 4247 void nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder, 4248 const nsRect& aRect, HitTestState* aState, 4249 nsTArray<nsIFrame*>* aOutFrames) { 4250 if (ShouldIgnoreForBackfaceHidden(aState)) { 4251 return; 4252 } 4253 4254 if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) { 4255 // aRect doesn't intersect our border-radius curve. 4256 return; 4257 } 4258 4259 aOutFrames->AppendElement(mFrame); 4260 } 4261 4262 bool nsDisplayCompositorHitTestInfo::CreateWebRenderCommands( 4263 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 4264 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 4265 nsDisplayListBuilder* aDisplayListBuilder) { 4266 return true; 4267 } 4268 4269 int32_t nsDisplayCompositorHitTestInfo::ZIndex() const { 4270 return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex(); 4271 } 4272 4273 void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) { 4274 mOverrideZIndex = Some(aZIndex); 4275 } 4276 4277 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder, 4278 nsIFrame* aCaretFrame) 4279 : nsPaintedDisplayItem(aBuilder, aCaretFrame), 4280 mCaret(aBuilder->GetCaret()), 4281 mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) { 4282 MOZ_COUNT_CTOR(nsDisplayCaret); 4283 // The presence of a caret doesn't change the overflow rect 4284 // of the owning frame, so the normal building rect might not 4285 // include the caret at all. We use MarkFrameForDisplay to ensure 4286 // we build this item, and here we override the building rect 4287 // to cover the pixels we're going to draw. 4288 SetBuildingRect(mBounds); 4289 } 4290 4291 nsRect nsDisplayCaret::GetBounds(nsDisplayListBuilder* aBuilder, 4292 bool* aSnap) const { 4293 *aSnap = true; 4294 // The caret returns a rect in the coordinates of mFrame. 4295 return mBounds; 4296 } 4297 4298 void nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) { 4299 // Note: Because we exist, we know that the caret is visible, so we don't 4300 // need to check for the caret's visibility. 4301 mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame()); 4302 } 4303 4304 bool nsDisplayCaret::CreateWebRenderCommands( 4305 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 4306 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 4307 nsDisplayListBuilder* aDisplayListBuilder) { 4308 using namespace layers; 4309 nsRect caretRect; 4310 nsRect hookRect; 4311 nscolor caretColor; 4312 nsIFrame* frame = 4313 mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor); 4314 if (NS_WARN_IF(!frame) || NS_WARN_IF(frame != mFrame)) { 4315 return true; 4316 } 4317 4318 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel(); 4319 gfx::DeviceColor color = ToDeviceColor(caretColor); 4320 LayoutDeviceRect devCaretRect = LayoutDeviceRect::FromAppUnits( 4321 caretRect + ToReferenceFrame(), appUnitsPerDevPixel); 4322 LayoutDeviceRect devHookRect = LayoutDeviceRect::FromAppUnits( 4323 hookRect + ToReferenceFrame(), appUnitsPerDevPixel); 4324 4325 wr::LayoutRect caret = wr::ToLayoutRect(devCaretRect); 4326 wr::LayoutRect hook = wr::ToLayoutRect(devHookRect); 4327 4328 // Note, WR will pixel snap anything that is layout aligned. 4329 aBuilder.PushRect(caret, caret, !BackfaceIsHidden(), false, false, 4330 wr::ToColorF(color)); 4331 4332 if (!devHookRect.IsEmpty()) { 4333 aBuilder.PushRect(hook, hook, !BackfaceIsHidden(), false, false, 4334 wr::ToColorF(color)); 4335 } 4336 return true; 4337 } 4338 4339 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder, 4340 nsIFrame* aFrame) 4341 : nsPaintedDisplayItem(aBuilder, aFrame) { 4342 MOZ_COUNT_CTOR(nsDisplayBorder); 4343 4344 mBounds = CalculateBounds<nsRect>(*mFrame->StyleBorder()); 4345 } 4346 4347 bool nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const { 4348 nsRect paddingRect = GetPaddingRect(); 4349 const nsStyleBorder* styleBorder; 4350 if (paddingRect.Contains(aRect) && 4351 !(styleBorder = mFrame->StyleBorder())->IsBorderImageSizeAvailable() && 4352 !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) { 4353 // aRect is entirely inside the content rect, and no part 4354 // of the border is rendered inside the content rect, so we are not 4355 // visible 4356 // Skip this if there's a border-image (which draws a background 4357 // too) or if there is a border-radius (which makes the border draw 4358 // further in). 4359 return true; 4360 } 4361 4362 return false; 4363 } 4364 4365 nsDisplayItemGeometry* nsDisplayBorder::AllocateGeometry( 4366 nsDisplayListBuilder* aBuilder) { 4367 return new nsDisplayBorderGeometry(this, aBuilder); 4368 } 4369 4370 void nsDisplayBorder::ComputeInvalidationRegion( 4371 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, 4372 nsRegion* aInvalidRegion) const { 4373 const auto* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry); 4374 bool snap; 4375 4376 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap))) { 4377 // We can probably get away with only invalidating the difference 4378 // between the border and padding rects, but the XUL ui at least 4379 // is apparently painting a background with this? 4380 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds); 4381 } 4382 } 4383 4384 bool nsDisplayBorder::CreateWebRenderCommands( 4385 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 4386 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 4387 nsDisplayListBuilder* aDisplayListBuilder) { 4388 nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize()); 4389 4390 ImgDrawResult drawResult = nsCSSRendering::CreateWebRenderCommandsForBorder( 4391 this, mFrame, rect, aBuilder, aResources, aSc, aManager, 4392 aDisplayListBuilder); 4393 4394 if (drawResult == ImgDrawResult::NOT_SUPPORTED) { 4395 return false; 4396 } 4397 return true; 4398 }; 4399 4400 void nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) { 4401 nsPoint offset = ToReferenceFrame(); 4402 4403 PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages() 4404 ? PaintBorderFlags::SyncDecodeImages 4405 : PaintBorderFlags(); 4406 4407 (void)nsCSSRendering::PaintBorder( 4408 mFrame->PresContext(), *aCtx, mFrame, GetPaintRect(aBuilder, aCtx), 4409 nsRect(offset, mFrame->GetSize()), mFrame->Style(), flags, 4410 mFrame->GetSkipSides()); 4411 } 4412 4413 nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder, 4414 bool* aSnap) const { 4415 *aSnap = true; 4416 return mBounds; 4417 } 4418 4419 void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder, 4420 gfxContext* aCtx) { 4421 nsPoint offset = ToReferenceFrame(); 4422 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset; 4423 nsPresContext* presContext = mFrame->PresContext(); 4424 4425 AUTO_PROFILER_LABEL("nsDisplayBoxShadowOuter::Paint", GRAPHICS); 4426 4427 nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, borderRect, 4428 GetPaintRect(aBuilder, aCtx), 1.0f); 4429 } 4430 4431 nsRect nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, 4432 bool* aSnap) const { 4433 *aSnap = false; 4434 return mBounds; 4435 } 4436 4437 nsRect nsDisplayBoxShadowOuter::GetBoundsInternal() { 4438 return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) + 4439 ToReferenceFrame(); 4440 } 4441 4442 bool nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect) const { 4443 nsPoint origin = ToReferenceFrame(); 4444 nsRect frameRect(origin, mFrame->GetSize()); 4445 if (!frameRect.Contains(aRect)) { 4446 return false; 4447 } 4448 4449 // the visible region is entirely inside the border-rect, and box shadows 4450 // never render within the border-rect (unless there's a border radius). 4451 nsRectCornerRadii twipsRadii; 4452 bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii); 4453 if (!hasBorderRadii) { 4454 return true; 4455 } 4456 4457 return RoundedRectContainsRect(frameRect, twipsRadii, aRect); 4458 } 4459 4460 bool nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() const { 4461 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan(); 4462 if (shadows.IsEmpty()) { 4463 return false; 4464 } 4465 4466 bool hasBorderRadius; 4467 // We don't support native themed things yet like box shadows around 4468 // input buttons. 4469 // 4470 // TODO(emilio): The non-native theme could provide the right rect+radius 4471 // instead relatively painlessly, if we find this causes performance issues or 4472 // what not. 4473 return !nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius); 4474 } 4475 4476 bool nsDisplayBoxShadowOuter::CreateWebRenderCommands( 4477 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 4478 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 4479 nsDisplayListBuilder* aDisplayListBuilder) { 4480 if (!CanBuildWebRenderDisplayItems()) { 4481 return false; 4482 } 4483 4484 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); 4485 nsPoint offset = ToReferenceFrame(); 4486 nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset; 4487 bool snap; 4488 nsRect bounds = GetBounds(aDisplayListBuilder, &snap); 4489 4490 bool hasBorderRadius; 4491 bool nativeTheme = 4492 nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius); 4493 4494 // Don't need the full size of the shadow rect like we do in 4495 // nsCSSRendering since WR takes care of calculations for blur 4496 // and spread radius. 4497 nsRect frameRect = 4498 nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame); 4499 4500 RectCornerRadii borderRadii; 4501 if (hasBorderRadius) { 4502 hasBorderRadius = nsCSSRendering::GetBorderRadii(frameRect, borderRect, 4503 mFrame, borderRadii); 4504 } 4505 4506 // Everything here is in app units, change to device units. 4507 LayoutDeviceRect clipRect = 4508 LayoutDeviceRect::FromAppUnits(bounds, appUnitsPerDevPixel); 4509 auto shadows = mFrame->StyleEffects()->mBoxShadow.AsSpan(); 4510 MOZ_ASSERT(!shadows.IsEmpty()); 4511 4512 for (const auto& shadow : Reversed(shadows)) { 4513 if (shadow.inset) { 4514 continue; 4515 } 4516 4517 float blurRadius = 4518 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel); 4519 gfx::sRGBColor shadowColor = nsCSSRendering::GetShadowColor( 4520 shadow.base, mFrame, aBuilder.GetInheritedOpacity()); 4521 4522 // We don't move the shadow rect here since WR does it for us 4523 // Now translate everything to device pixels. 4524 const nsRect& shadowRect = frameRect; 4525 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits( 4526 nsPoint(shadow.base.horizontal.ToAppUnits(), 4527 shadow.base.vertical.ToAppUnits()), 4528 appUnitsPerDevPixel); 4529 4530 LayoutDeviceRect deviceBox = 4531 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel); 4532 wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox); 4533 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect); 4534 4535 LayoutDeviceSize zeroSize; 4536 wr::BorderRadius borderRadius = 4537 wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize); 4538 if (hasBorderRadius) { 4539 borderRadius = wr::ToBorderRadius( 4540 LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()), 4541 LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()), 4542 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()), 4543 LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight())); 4544 } 4545 4546 float spreadRadius = 4547 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel); 4548 4549 aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(), 4550 deviceBoxRect, wr::ToLayoutVector2D(shadowOffset), 4551 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius, 4552 spreadRadius, borderRadius, 4553 wr::BoxShadowClipMode::Outset); 4554 } 4555 4556 return true; 4557 } 4558 4559 void nsDisplayBoxShadowOuter::ComputeInvalidationRegion( 4560 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, 4561 nsRegion* aInvalidRegion) const { 4562 const auto* geometry = 4563 static_cast<const nsDisplayItemGenericGeometry*>(aGeometry); 4564 bool snap; 4565 if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) || 4566 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) { 4567 nsRegion oldShadow, newShadow; 4568 nsRectCornerRadii dontCare; 4569 bool hasBorderRadius = mFrame->GetBorderRadii(dontCare); 4570 if (hasBorderRadius) { 4571 // If we have rounded corners then we need to invalidate the frame area 4572 // too since we paint into it. 4573 oldShadow = geometry->mBounds; 4574 newShadow = GetBounds(aBuilder, &snap); 4575 } else { 4576 oldShadow.Sub(geometry->mBounds, geometry->mBorderRect); 4577 newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect()); 4578 } 4579 aInvalidRegion->Or(oldShadow, newShadow); 4580 } 4581 } 4582 4583 void nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder, 4584 gfxContext* aCtx) { 4585 nsPoint offset = ToReferenceFrame(); 4586 nsRect borderRect = nsRect(offset, mFrame->GetSize()); 4587 nsPresContext* presContext = mFrame->PresContext(); 4588 4589 AUTO_PROFILER_LABEL("nsDisplayBoxShadowInner::Paint", GRAPHICS); 4590 4591 nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, borderRect); 4592 } 4593 4594 bool nsDisplayBoxShadowInner::CanCreateWebRenderCommands( 4595 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, 4596 const nsPoint& aReferenceOffset) { 4597 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan(); 4598 if (shadows.IsEmpty()) { 4599 // Means we don't have to paint anything 4600 return true; 4601 } 4602 4603 bool hasBorderRadius; 4604 bool nativeTheme = 4605 nsCSSRendering::HasBoxShadowNativeTheme(aFrame, hasBorderRadius); 4606 4607 // We don't support native themed things yet like box shadows around 4608 // input buttons. 4609 return !nativeTheme; 4610 } 4611 4612 /* static */ 4613 void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands( 4614 wr::DisplayListBuilder& aBuilder, const StackingContextHelper& aSc, 4615 nsRect& aVisibleRect, nsIFrame* aFrame, const nsRect& aBorderRect) { 4616 if (!nsCSSRendering::ShouldPaintBoxShadowInner(aFrame)) { 4617 return; 4618 } 4619 4620 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); 4621 4622 auto shadows = aFrame->StyleEffects()->mBoxShadow.AsSpan(); 4623 4624 LayoutDeviceRect clipRect = 4625 LayoutDeviceRect::FromAppUnits(aVisibleRect, appUnitsPerDevPixel); 4626 4627 for (const auto& shadow : Reversed(shadows)) { 4628 if (!shadow.inset) { 4629 continue; 4630 } 4631 4632 nsRect shadowRect = 4633 nsCSSRendering::GetBoxShadowInnerPaddingRect(aFrame, aBorderRect); 4634 RectCornerRadii innerRadii; 4635 nsCSSRendering::GetShadowInnerRadii(aFrame, aBorderRect, innerRadii); 4636 4637 // Now translate everything to device pixels. 4638 LayoutDeviceRect deviceBoxRect = 4639 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel); 4640 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect); 4641 sRGBColor shadowColor = 4642 nsCSSRendering::GetShadowColor(shadow.base, aFrame, 1.0); 4643 4644 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits( 4645 nsPoint(shadow.base.horizontal.ToAppUnits(), 4646 shadow.base.vertical.ToAppUnits()), 4647 appUnitsPerDevPixel); 4648 4649 float blurRadius = 4650 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel); 4651 4652 wr::BorderRadius borderRadius = wr::ToBorderRadius( 4653 LayoutDeviceSize::FromUnknownSize(innerRadii.TopLeft()), 4654 LayoutDeviceSize::FromUnknownSize(innerRadii.TopRight()), 4655 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomLeft()), 4656 LayoutDeviceSize::FromUnknownSize(innerRadii.BottomRight())); 4657 // NOTE: Any spread radius > 0 will render nothing. WR Bug. 4658 float spreadRadius = 4659 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel); 4660 4661 aBuilder.PushBoxShadow( 4662 wr::ToLayoutRect(deviceBoxRect), deviceClipRect, 4663 !aFrame->BackfaceIsHidden(), wr::ToLayoutRect(deviceBoxRect), 4664 wr::ToLayoutVector2D(shadowOffset), 4665 wr::ToColorF(ToDeviceColor(shadowColor)), blurRadius, spreadRadius, 4666 borderRadius, wr::BoxShadowClipMode::Inset); 4667 } 4668 } 4669 4670 bool nsDisplayBoxShadowInner::CreateWebRenderCommands( 4671 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 4672 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 4673 nsDisplayListBuilder* aDisplayListBuilder) { 4674 if (!CanCreateWebRenderCommands(aDisplayListBuilder, mFrame, 4675 ToReferenceFrame())) { 4676 return false; 4677 } 4678 4679 bool snap; 4680 nsRect visible = GetBounds(aDisplayListBuilder, &snap); 4681 nsPoint offset = ToReferenceFrame(); 4682 nsRect borderRect = nsRect(offset, mFrame->GetSize()); 4683 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands( 4684 aBuilder, aSc, visible, mFrame, borderRect); 4685 4686 return true; 4687 } 4688 4689 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder, 4690 nsIFrame* aFrame, nsDisplayList* aList, 4691 bool aClearClipChain) 4692 : nsDisplayWrapList(aBuilder, aFrame, aList, 4693 aBuilder->CurrentActiveScrolledRoot(), 4694 ContainerASRType::Constant, aClearClipChain) {} 4695 4696 nsDisplayWrapList::nsDisplayWrapList( 4697 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 4698 const ActiveScrolledRoot* aActiveScrolledRoot, 4699 ContainerASRType aContainerASRType, bool aClearClipChain) 4700 : nsPaintedDisplayItem(aBuilder, aFrame, aActiveScrolledRoot), 4701 mList(aBuilder), 4702 mFrameASR(aContainerASRType == ContainerASRType::AncestorOfContained 4703 ? aBuilder->CurrentActiveScrolledRoot() 4704 : nullptr), 4705 mOverrideZIndex(0), 4706 mContainerASRType(aContainerASRType), 4707 mHasZIndexOverride(false), 4708 mClearingClipChain(aClearClipChain) { 4709 MOZ_COUNT_CTOR(nsDisplayWrapList); 4710 4711 mBaseBuildingRect = GetBuildingRect(); 4712 4713 mListPtr = &mList; 4714 mListPtr->AppendToTop(aList); 4715 mOriginalClipChain = mClipChain; 4716 nsDisplayWrapList::UpdateBounds(aBuilder); 4717 } 4718 4719 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder, 4720 nsIFrame* aFrame, nsDisplayItem* aItem) 4721 : nsPaintedDisplayItem(aBuilder, aFrame), 4722 mList(aBuilder), 4723 mOverrideZIndex(0), 4724 mHasZIndexOverride(false) { 4725 MOZ_COUNT_CTOR(nsDisplayWrapList); 4726 4727 mBaseBuildingRect = GetBuildingRect(); 4728 4729 mListPtr = &mList; 4730 mListPtr->AppendToTop(aItem); 4731 mOriginalClipChain = mClipChain; 4732 nsDisplayWrapList::UpdateBounds(aBuilder); 4733 4734 if (!aFrame || !aFrame->IsTransformed()) { 4735 return; 4736 } 4737 4738 // See the previous nsDisplayWrapList constructor 4739 if (aItem->Frame() == aFrame) { 4740 mToReferenceFrame = aItem->ToReferenceFrame(); 4741 } 4742 4743 nsRect visible = aBuilder->GetVisibleRect() + 4744 aBuilder->GetCurrentFrameOffsetToReferenceFrame(); 4745 4746 SetBuildingRect(visible); 4747 } 4748 4749 void nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, 4750 const nsRect& aRect, HitTestState* aState, 4751 nsTArray<nsIFrame*>* aOutFrames) { 4752 mListPtr->HitTest(aBuilder, aRect, aState, aOutFrames); 4753 } 4754 4755 nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder, 4756 bool* aSnap) const { 4757 *aSnap = false; 4758 return mBounds; 4759 } 4760 4761 nsRegion nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, 4762 bool* aSnap) const { 4763 *aSnap = false; 4764 bool snap; 4765 return ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), 4766 GetBounds(aBuilder, &snap)); 4767 } 4768 4769 Maybe<nscolor> nsDisplayWrapList::IsUniform( 4770 nsDisplayListBuilder* aBuilder) const { 4771 // We could try to do something but let's conservatively just return Nothing. 4772 return Nothing(); 4773 } 4774 4775 void nsDisplayWrapper::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) { 4776 NS_ERROR("nsDisplayWrapper should have been flattened away for painting"); 4777 } 4778 4779 nsRect nsDisplayWrapList::GetComponentAlphaBounds( 4780 nsDisplayListBuilder* aBuilder) const { 4781 return mListPtr->GetComponentAlphaBounds(aBuilder); 4782 } 4783 4784 bool nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption( 4785 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 4786 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 4787 nsDisplayListBuilder* aDisplayListBuilder, bool aNewClipList) { 4788 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList( 4789 GetChildren(), this, aDisplayListBuilder, aSc, aBuilder, aResources, 4790 aNewClipList); 4791 return true; 4792 } 4793 4794 static nsresult WrapDisplayList(nsDisplayListBuilder* aBuilder, 4795 nsIFrame* aFrame, nsDisplayList* aList, 4796 nsDisplayItemWrapper* aWrapper) { 4797 if (!aList->GetTop()) { 4798 return NS_OK; 4799 } 4800 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList); 4801 if (!item) { 4802 return NS_ERROR_OUT_OF_MEMORY; 4803 } 4804 // aList was emptied 4805 aList->AppendToTop(item); 4806 return NS_OK; 4807 } 4808 4809 static nsresult WrapEachDisplayItem(nsDisplayListBuilder* aBuilder, 4810 nsDisplayList* aList, 4811 nsDisplayItemWrapper* aWrapper) { 4812 for (nsDisplayItem* item : aList->TakeItems()) { 4813 item = aWrapper->WrapItem(aBuilder, item); 4814 if (!item) { 4815 return NS_ERROR_OUT_OF_MEMORY; 4816 } 4817 aList->AppendToTop(item); 4818 } 4819 // aList was emptied 4820 return NS_OK; 4821 } 4822 4823 nsresult nsDisplayItemWrapper::WrapLists(nsDisplayListBuilder* aBuilder, 4824 nsIFrame* aFrame, 4825 const nsDisplayListSet& aIn, 4826 const nsDisplayListSet& aOut) { 4827 nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn); 4828 NS_ENSURE_SUCCESS(rv, rv); 4829 4830 if (&aOut == &aIn) { 4831 return NS_OK; 4832 } 4833 aOut.BorderBackground()->AppendToTop(aIn.BorderBackground()); 4834 aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds()); 4835 aOut.Floats()->AppendToTop(aIn.Floats()); 4836 aOut.Content()->AppendToTop(aIn.Content()); 4837 aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants()); 4838 aOut.Outlines()->AppendToTop(aIn.Outlines()); 4839 return NS_OK; 4840 } 4841 4842 nsresult nsDisplayItemWrapper::WrapListsInPlace( 4843 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, 4844 const nsDisplayListSet& aLists) { 4845 nsresult rv; 4846 if (WrapBorderBackground()) { 4847 // Our border-backgrounds are in-flow 4848 rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this); 4849 NS_ENSURE_SUCCESS(rv, rv); 4850 } 4851 // Our block border-backgrounds are in-flow 4852 rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this); 4853 NS_ENSURE_SUCCESS(rv, rv); 4854 // The floats are not in flow 4855 rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this); 4856 NS_ENSURE_SUCCESS(rv, rv); 4857 // Our child content is in flow 4858 rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this); 4859 NS_ENSURE_SUCCESS(rv, rv); 4860 // The positioned descendants may not be in-flow 4861 rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this); 4862 NS_ENSURE_SUCCESS(rv, rv); 4863 // The outlines may not be in-flow 4864 return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this); 4865 } 4866 4867 nsDisplayOpacity::nsDisplayOpacity( 4868 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 4869 const ActiveScrolledRoot* aActiveScrolledRoot, 4870 ContainerASRType aContainerASRType, bool aForEventsOnly, 4871 bool aNeedsActiveLayer, bool aWrapsBackdropFilter, bool aForceIsolation) 4872 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, 4873 aContainerASRType, true), 4874 mOpacity(aFrame->StyleEffects()->mOpacity), 4875 mForEventsOnly(aForEventsOnly), 4876 mNeedsActiveLayer(aNeedsActiveLayer), 4877 mChildOpacityState(ChildOpacityState::Unknown), 4878 mWrapsBackdropFilter(aWrapsBackdropFilter), 4879 mForceIsolation(aForceIsolation) { 4880 MOZ_COUNT_CTOR(nsDisplayOpacity); 4881 } 4882 4883 void nsDisplayOpacity::HitTest(nsDisplayListBuilder* aBuilder, 4884 const nsRect& aRect, 4885 nsDisplayItem::HitTestState* aState, 4886 nsTArray<nsIFrame*>* aOutFrames) { 4887 AutoRestore<float> opacity(aState->mCurrentOpacity); 4888 aState->mCurrentOpacity *= mOpacity; 4889 4890 // TODO(emilio): special-casing zero is a bit arbitrary... Maybe we should 4891 // only consider fully opaque items? Or make this configurable somehow? 4892 if (aBuilder->HitTestIsForVisibility() && mOpacity == 0.0f) { 4893 return; 4894 } 4895 nsDisplayWrapList::HitTest(aBuilder, aRect, aState, aOutFrames); 4896 } 4897 4898 nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, 4899 bool* aSnap) const { 4900 *aSnap = false; 4901 // The only time where mOpacity == 1.0 should be when we have will-change. 4902 // We could report this as opaque then but when the will-change value starts 4903 // animating the element would become non opaque and could cause repaints. 4904 return nsRegion(); 4905 } 4906 4907 void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) { 4908 if (GetOpacity() == 0.0f) { 4909 return; 4910 } 4911 4912 int32_t apd = mFrame->PresContext()->AppUnitsPerDevPixel(); 4913 4914 if (GetOpacity() == 1.0f) { 4915 GetChildren()->Paint(aBuilder, aCtx, apd); 4916 return; 4917 } 4918 4919 bool unusedSnap = false; 4920 auto deviceSpaceBounds = IntRect::FromUnknownRect( 4921 RoundedOut(ToRect(aCtx->UserToDevice(nsLayoutUtils::RectToGfxRect( 4922 GetBounds(aBuilder, &unusedSnap), apd))))); 4923 4924 aCtx->GetDrawTarget()->PushLayer(false, GetOpacity(), nullptr, gfx::Matrix(), 4925 deviceSpaceBounds); 4926 GetChildren()->Paint(aBuilder, aCtx, apd); 4927 aCtx->GetDrawTarget()->PopLayer(); 4928 } 4929 4930 /* static */ 4931 bool nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder, 4932 nsIFrame* aFrame) { 4933 return EffectCompositor::HasAnimationsForCompositor( 4934 aFrame, DisplayItemType::TYPE_OPACITY) || 4935 ActiveLayerTracker::IsStyleAnimated( 4936 aBuilder, aFrame, nsCSSPropertyIDSet::OpacityProperties()); 4937 } 4938 4939 bool nsDisplayOpacity::CanApplyOpacity(WebRenderLayerManager* aManager, 4940 nsDisplayListBuilder* aBuilder) const { 4941 return !EffectCompositor::HasAnimationsForCompositor( 4942 mFrame, DisplayItemType::TYPE_OPACITY); 4943 } 4944 4945 // Only try folding our opacity down if we have at most |kOpacityMaxChildCount| 4946 // children that don't overlap and can all apply the opacity to themselves. 4947 static const size_t kOpacityMaxChildCount = 3; 4948 4949 // |kOpacityMaxListSize| defines an early exit condition for opacity items that 4950 // are likely have more child items than |kOpacityMaxChildCount|. 4951 static const size_t kOpacityMaxListSize = kOpacityMaxChildCount * 2; 4952 4953 /** 4954 * Recursively iterates through |aList| and collects at most 4955 * |kOpacityMaxChildCount| display item pointers to items that return true for 4956 * CanApplyOpacity(). The item pointers are added to |aArray|. 4957 * 4958 * LayerEventRegions and WrapList items are ignored. 4959 * 4960 * We need to do this recursively, because the child display items might contain 4961 * nested nsDisplayWrapLists. 4962 * 4963 * Returns false if there are more than |kOpacityMaxChildCount| items, or if an 4964 * item that returns false for CanApplyOpacity() is encountered. 4965 * Otherwise returns true. 4966 */ 4967 static bool CollectItemsWithOpacity(WebRenderLayerManager* aManager, 4968 nsDisplayListBuilder* aBuilder, 4969 nsDisplayList* aList, 4970 nsTArray<nsPaintedDisplayItem*>& aArray) { 4971 if (aList->Length() > kOpacityMaxListSize) { 4972 // Exit early, since |aList| will likely contain more than 4973 // |kOpacityMaxChildCount| items. 4974 return false; 4975 } 4976 4977 for (nsDisplayItem* i : *aList) { 4978 const DisplayItemType type = i->GetType(); 4979 4980 if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { 4981 continue; 4982 } 4983 4984 // Descend only into wraplists. 4985 if (type == DisplayItemType::TYPE_WRAP_LIST || 4986 type == DisplayItemType::TYPE_CONTAINER) { 4987 // The current display item has children, process them first. 4988 if (!CollectItemsWithOpacity(aManager, aBuilder, i->GetChildren(), 4989 aArray)) { 4990 return false; 4991 } 4992 4993 continue; 4994 } 4995 4996 if (aArray.Length() == kOpacityMaxChildCount) { 4997 return false; 4998 } 4999 5000 auto* item = i->AsPaintedDisplayItem(); 5001 if (!item || !item->CanApplyOpacity(aManager, aBuilder)) { 5002 return false; 5003 } 5004 5005 aArray.AppendElement(item); 5006 } 5007 5008 return true; 5009 } 5010 5011 bool nsDisplayOpacity::CanApplyToChildren(WebRenderLayerManager* aManager, 5012 nsDisplayListBuilder* aBuilder) { 5013 if (mChildOpacityState == ChildOpacityState::Deferred) { 5014 return false; 5015 } 5016 5017 // Iterate through the child display list and copy at most 5018 // |kOpacityMaxChildCount| child display item pointers to a temporary list. 5019 AutoTArray<nsPaintedDisplayItem*, kOpacityMaxChildCount> items; 5020 if (!CollectItemsWithOpacity(aManager, aBuilder, &mList, items)) { 5021 mChildOpacityState = ChildOpacityState::Deferred; 5022 return false; 5023 } 5024 5025 struct { 5026 nsPaintedDisplayItem* item{}; 5027 nsRect bounds; 5028 } children[kOpacityMaxChildCount]; 5029 5030 bool snap; 5031 size_t childCount = 0; 5032 for (nsPaintedDisplayItem* item : items) { 5033 children[childCount].item = item; 5034 children[childCount].bounds = item->GetBounds(aBuilder, &snap); 5035 childCount++; 5036 } 5037 5038 for (size_t i = 0; i < childCount; i++) { 5039 for (size_t j = i + 1; j < childCount; j++) { 5040 if (children[i].bounds.Intersects(children[j].bounds)) { 5041 mChildOpacityState = ChildOpacityState::Deferred; 5042 return false; 5043 } 5044 } 5045 } 5046 5047 mChildOpacityState = ChildOpacityState::Applied; 5048 return true; 5049 } 5050 5051 /** 5052 * Returns true if this nsDisplayOpacity contains only a filter or a mask item 5053 * that has the same frame as the opacity item, and that supports painting with 5054 * opacity. In this case the opacity item can be optimized away. 5055 */ 5056 bool nsDisplayOpacity::ApplyToMask() { 5057 if (mList.Length() != 1) { 5058 return false; 5059 } 5060 5061 nsDisplayItem* item = mList.GetBottom(); 5062 if (item->Frame() != mFrame) { 5063 // The effect item needs to have the same frame as the opacity item. 5064 return false; 5065 } 5066 5067 const DisplayItemType type = item->GetType(); 5068 if (type == DisplayItemType::TYPE_MASK) { 5069 return true; 5070 } 5071 5072 return false; 5073 } 5074 5075 bool nsDisplayOpacity::CanApplyOpacityToChildren( 5076 WebRenderLayerManager* aManager, nsDisplayListBuilder* aBuilder, 5077 float aInheritedOpacity) { 5078 if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() || 5079 mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) { 5080 // If we've been split, then we might need to merge, so 5081 // don't flatten us away. 5082 return false; 5083 } 5084 5085 if (mNeedsActiveLayer || mOpacity == 0.0) { 5086 // If our opacity is zero then we'll discard all descendant display items 5087 // except for layer event regions, so there's no point in doing this 5088 // optimization (and if we do do it, then invalidations of those descendants 5089 // might trigger repainting). 5090 return false; 5091 } 5092 5093 if (mList.IsEmpty()) { 5094 return false; 5095 } 5096 5097 // We can only flatten opacity items into a mask if we haven't 5098 // already flattened an earlier ancestor, since the SVG code pulls the opacity 5099 // from style directly, and won't know about the outer opacity value. 5100 if (aInheritedOpacity == 1.0f && ApplyToMask()) { 5101 MOZ_ASSERT(SVGIntegrationUtils::UsingEffectsForFrame(mFrame)); 5102 mChildOpacityState = ChildOpacityState::Applied; 5103 return true; 5104 } 5105 5106 // Return true if we successfully applied opacity to child items. 5107 return CanApplyToChildren(aManager, aBuilder); 5108 } 5109 5110 void nsDisplayOpacity::ComputeInvalidationRegion( 5111 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, 5112 nsRegion* aInvalidRegion) const { 5113 const auto* geometry = 5114 static_cast<const nsDisplayOpacityGeometry*>(aGeometry); 5115 5116 bool snap; 5117 if (mOpacity != geometry->mOpacity) { 5118 aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds); 5119 } 5120 } 5121 5122 void nsDisplayOpacity::WriteDebugInfo(std::stringstream& aStream) { 5123 aStream << " (opacity " << mOpacity << ", mChildOpacityState: "; 5124 switch (mChildOpacityState) { 5125 case ChildOpacityState::Unknown: 5126 aStream << "Unknown"; 5127 break; 5128 case ChildOpacityState::Applied: 5129 aStream << "Applied"; 5130 break; 5131 case ChildOpacityState::Deferred: 5132 aStream << "Deferred"; 5133 break; 5134 default: 5135 break; 5136 } 5137 5138 aStream << ")"; 5139 } 5140 5141 bool nsDisplayOpacity::CreateWebRenderCommands( 5142 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 5143 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 5144 nsDisplayListBuilder* aDisplayListBuilder) { 5145 MOZ_ASSERT(mChildOpacityState != ChildOpacityState::Applied); 5146 float oldOpacity = aBuilder.GetInheritedOpacity(); 5147 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain(); 5148 aBuilder.SetInheritedOpacity(1.0f); 5149 aBuilder.SetInheritedClipChain(nullptr); 5150 float opacity = mOpacity * oldOpacity; 5151 float* opacityForSC = &opacity; 5152 5153 uint64_t animationsId = 5154 AddAnimationsForWebRender(this, aManager, aDisplayListBuilder); 5155 wr::WrAnimationProperty prop{ 5156 wr::WrAnimationType::Opacity, 5157 animationsId, 5158 }; 5159 5160 wr::StackingContextParams params; 5161 params.animation = animationsId ? &prop : nullptr; 5162 params.opacity = opacityForSC; 5163 params.clip = 5164 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId()); 5165 if (mWrapsBackdropFilter) { 5166 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER; 5167 } 5168 if (mForceIsolation) { 5169 params.flags |= wr::StackingContextFlags::FORCED_ISOLATION; 5170 } 5171 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, 5172 params); 5173 5174 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList( 5175 &mList, this, aDisplayListBuilder, sc, aBuilder, aResources); 5176 aBuilder.SetInheritedOpacity(oldOpacity); 5177 aBuilder.SetInheritedClipChain(oldClipChain); 5178 return true; 5179 } 5180 5181 nsDisplayBlendMode::nsDisplayBlendMode( 5182 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 5183 StyleBlend aBlendMode, const ActiveScrolledRoot* aActiveScrolledRoot, 5184 ContainerASRType aContainerASRType, const bool aIsForBackground) 5185 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, 5186 aContainerASRType, true), 5187 mBlendMode(aBlendMode), 5188 mIsForBackground(aIsForBackground) { 5189 MOZ_COUNT_CTOR(nsDisplayBlendMode); 5190 } 5191 5192 nsRegion nsDisplayBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, 5193 bool* aSnap) const { 5194 *aSnap = false; 5195 // We are never considered opaque 5196 return nsRegion(); 5197 } 5198 5199 bool nsDisplayBlendMode::CreateWebRenderCommands( 5200 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 5201 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 5202 nsDisplayListBuilder* aDisplayListBuilder) { 5203 wr::StackingContextParams params; 5204 params.mix_blend_mode = 5205 wr::ToMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode)); 5206 params.clip = 5207 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId()); 5208 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, 5209 params); 5210 5211 return nsDisplayWrapList::CreateWebRenderCommands( 5212 aBuilder, aResources, sc, aManager, aDisplayListBuilder); 5213 } 5214 5215 void nsDisplayBlendMode::Paint(nsDisplayListBuilder* aBuilder, 5216 gfxContext* aCtx) { 5217 // This should be switched to use PushLayerWithBlend, once it's 5218 // been implemented for all DrawTarget backends. 5219 DrawTarget* dt = aCtx->GetDrawTarget(); 5220 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); 5221 Rect rect = NSRectToRect(GetPaintRect(aBuilder, aCtx), appUnitsPerDevPixel); 5222 rect.RoundOut(); 5223 5224 // Create a temporary DrawTarget that is clipped to the area that 5225 // we're going to draw to. This will include the same transform as 5226 // is currently on |dt|. 5227 RefPtr<DrawTarget> temp = 5228 dt->CreateClippedDrawTarget(rect, SurfaceFormat::B8G8R8A8); 5229 if (!temp) { 5230 return; 5231 } 5232 5233 gfxContext ctx(temp, /* aPreserveTransform */ true); 5234 5235 GetChildren()->Paint(aBuilder, &ctx, 5236 mFrame->PresContext()->AppUnitsPerDevPixel()); 5237 5238 // Draw the temporary DT to the real destination, applying the blend mode, but 5239 // no transform. 5240 temp->Flush(); 5241 RefPtr<SourceSurface> surface = temp->Snapshot(); 5242 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx); 5243 dt->SetTransform(Matrix()); 5244 dt->DrawSurface( 5245 surface, Rect(surface->GetRect()), Rect(surface->GetRect()), 5246 DrawSurfaceOptions(), 5247 DrawOptions(1.0f, nsCSSRendering::GetGFXBlendMode(mBlendMode))); 5248 } 5249 5250 gfx::CompositionOp nsDisplayBlendMode::BlendMode() { 5251 return nsCSSRendering::GetGFXBlendMode(mBlendMode); 5252 } 5253 5254 bool nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const { 5255 // Items for the same content element should be merged into a single 5256 // compositing group. 5257 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) || 5258 !HasSameContent(aItem)) { 5259 return false; 5260 } 5261 5262 const auto* item = static_cast<const nsDisplayBlendMode*>(aItem); 5263 if (mIsForBackground || item->mIsForBackground) { 5264 // Don't merge background-blend-mode items 5265 return false; 5266 } 5267 5268 return true; 5269 } 5270 5271 /* static */ 5272 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForMixBlendMode( 5273 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 5274 const ActiveScrolledRoot* aActiveScrolledRoot, 5275 ContainerASRType aContainerASRType) { 5276 return MakeDisplayItemWithIndex<nsDisplayBlendContainer>( 5277 aBuilder, aFrame, uint16_t(BlendContainerType::MixBlendMode), aList, 5278 aActiveScrolledRoot, aContainerASRType, BlendContainerType::MixBlendMode); 5279 } 5280 5281 /* static */ 5282 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForBackgroundBlendMode( 5283 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame, 5284 nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot, 5285 ContainerASRType aContainerASRType) { 5286 if (aSecondaryFrame) { 5287 auto type = GetTableTypeFromFrame(aFrame); 5288 auto index = static_cast<uint16_t>(type); 5289 5290 return MakeDisplayItemWithIndex<nsDisplayTableBlendContainer>( 5291 aBuilder, aSecondaryFrame, index, aList, aActiveScrolledRoot, 5292 aContainerASRType, BlendContainerType::BackgroundBlendMode, aFrame); 5293 } 5294 5295 return MakeDisplayItemWithIndex<nsDisplayBlendContainer>( 5296 aBuilder, aFrame, uint16_t(BlendContainerType::BackgroundBlendMode), 5297 aList, aActiveScrolledRoot, aContainerASRType, 5298 BlendContainerType::BackgroundBlendMode); 5299 } 5300 5301 /* static */ 5302 nsDisplayBlendContainer* nsDisplayBlendContainer::CreateForIsolation( 5303 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 5304 const ActiveScrolledRoot* aActiveScrolledRoot, 5305 ContainerASRType aContainerASRType, bool aNeedsIsolation) { 5306 auto type = aNeedsIsolation ? BlendContainerType::NeedsIsolationNeedsContainer 5307 : BlendContainerType::NeedsIsolationNothing; 5308 return MakeDisplayItemWithIndex<nsDisplayBlendContainer>( 5309 aBuilder, aFrame, uint16_t(BlendContainerType::NeedsIsolationNothing), 5310 aList, aActiveScrolledRoot, aContainerASRType, type); 5311 } 5312 5313 nsDisplayBlendContainer::nsDisplayBlendContainer( 5314 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 5315 const ActiveScrolledRoot* aActiveScrolledRoot, 5316 ContainerASRType aContainerASRType, BlendContainerType aBlendContainerType) 5317 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, 5318 aContainerASRType, true), 5319 mBlendContainerType(aBlendContainerType) { 5320 MOZ_COUNT_CTOR(nsDisplayBlendContainer); 5321 } 5322 5323 void nsDisplayBlendContainer::Paint(nsDisplayListBuilder* aBuilder, 5324 gfxContext* aCtx) { 5325 aCtx->GetDrawTarget()->PushLayer(false, 1.0, nullptr, gfx::Matrix()); 5326 GetChildren()->Paint(aBuilder, aCtx, 5327 mFrame->PresContext()->AppUnitsPerDevPixel()); 5328 aCtx->GetDrawTarget()->PopLayer(); 5329 } 5330 5331 bool nsDisplayBlendContainer::CreateWebRenderCommands( 5332 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 5333 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 5334 nsDisplayListBuilder* aDisplayListBuilder) { 5335 Maybe<StackingContextHelper> layer; 5336 const StackingContextHelper* sc = &aSc; 5337 if (CreatesStackingContextHelper()) { 5338 wr::StackingContextParams params; 5339 params.flags |= wr::StackingContextFlags::IS_BLEND_CONTAINER; 5340 params.clip = 5341 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId()); 5342 layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params); 5343 sc = layer.ptr(); 5344 } 5345 5346 return nsDisplayWrapList::CreateWebRenderCommandsNewClipListOption( 5347 aBuilder, aResources, *sc, aManager, aDisplayListBuilder, layer.isSome()); 5348 } 5349 5350 void nsDisplayTableBlendContainer::Destroy(nsDisplayListBuilder* aBuilder) { 5351 RemoveDisplayItemFromFrame(aBuilder, mAncestorFrame); 5352 nsDisplayBlendContainer::Destroy(aBuilder); 5353 } 5354 5355 void nsDisplayTableBlendMode::Destroy(nsDisplayListBuilder* aBuilder) { 5356 RemoveDisplayItemFromFrame(aBuilder, mAncestorFrame); 5357 nsDisplayBlendMode::Destroy(aBuilder); 5358 } 5359 5360 nsDisplayOwnLayer::nsDisplayOwnLayer( 5361 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 5362 const ActiveScrolledRoot* aActiveScrolledRoot, 5363 ContainerASRType aContainerASRType, nsDisplayOwnLayerFlags aFlags, 5364 const ScrollbarData& aScrollbarData, bool aForceActive, 5365 bool aClearClipChain) 5366 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, 5367 aContainerASRType, aClearClipChain), 5368 mFlags(aFlags), 5369 mScrollbarData(aScrollbarData), 5370 mForceActive(aForceActive), 5371 mWrAnimationId(0) { 5372 MOZ_COUNT_CTOR(nsDisplayOwnLayer); 5373 } 5374 5375 bool nsDisplayOwnLayer::IsScrollThumbLayer() const { 5376 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Thumb; 5377 } 5378 5379 bool nsDisplayOwnLayer::IsScrollbarContainer() const { 5380 return mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container; 5381 } 5382 5383 bool nsDisplayOwnLayer::IsRootScrollbarContainer() const { 5384 return IsScrollbarContainer() && IsScrollbarLayerForRoot(); 5385 } 5386 5387 bool nsDisplayOwnLayer::IsScrollbarLayerForRoot() const { 5388 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() && 5389 mScrollbarData.mTargetViewId == 5390 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()); 5391 } 5392 5393 bool nsDisplayOwnLayer::IsZoomingLayer() const { 5394 return GetType() == DisplayItemType::TYPE_ASYNC_ZOOM; 5395 } 5396 5397 bool nsDisplayOwnLayer::IsFixedPositionLayer() const { 5398 return GetType() == DisplayItemType::TYPE_FIXED_POSITION || 5399 GetType() == DisplayItemType::TYPE_TABLE_FIXED_POSITION; 5400 } 5401 5402 bool nsDisplayOwnLayer::IsStickyPositionLayer() const { 5403 return GetType() == DisplayItemType::TYPE_STICKY_POSITION; 5404 } 5405 5406 /* static */ 5407 bool nsDisplayOwnLayer::HasDynamicToolbar(nsIFrame* aFrame) { 5408 if (!aFrame->PresContext()->IsRootContentDocumentCrossProcess()) { 5409 return false; 5410 } 5411 return aFrame->PresContext()->HasDynamicToolbar() || 5412 // For tests on Android, this pref is set to simulate the dynamic 5413 // toolbar 5414 StaticPrefs::apz_fixed_margin_override_enabled(); 5415 } 5416 5417 bool nsDisplayOwnLayer::HasDynamicToolbar() const { 5418 return HasDynamicToolbar(mFrame); 5419 } 5420 5421 bool nsDisplayOwnLayer::CreateWebRenderCommands( 5422 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 5423 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 5424 nsDisplayListBuilder* aDisplayListBuilder, bool aForceIsolation) { 5425 Maybe<wr::WrAnimationProperty> prop; 5426 const bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() && 5427 (IsScrollThumbLayer() || IsZoomingLayer() || 5428 ShouldGetFixedAnimationId() || 5429 (IsRootScrollbarContainer() && HasDynamicToolbar())); 5430 5431 if (needsProp) { 5432 // APZ is enabled and this is a scroll thumb or zooming layer, so we need 5433 // to create and set an animation id. That way APZ can adjust the position/ 5434 // zoom of this content asynchronously as needed. 5435 RefPtr<WebRenderAPZAnimationData> animationData = 5436 aManager->CommandBuilder() 5437 .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(this); 5438 mWrAnimationId = animationData->GetAnimationId(); 5439 5440 prop.emplace(); 5441 prop->id = mWrAnimationId; 5442 prop->key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(), 5443 wr::SpatialKeyKind::APZ); 5444 prop->effect_type = wr::WrAnimationType::Transform; 5445 } 5446 5447 wr::StackingContextParams params; 5448 params.animation = prop.ptrOr(nullptr); 5449 params.clip = 5450 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId()); 5451 const bool rootScrollbarContainer = IsRootScrollbarContainer(); 5452 if (rootScrollbarContainer) { 5453 params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER; 5454 } 5455 if (aForceIsolation) { 5456 params.flags |= wr::StackingContextFlags::FORCED_ISOLATION; 5457 } 5458 if (IsZoomingLayer() || ShouldGetFixedAnimationId() || 5459 (rootScrollbarContainer && HasDynamicToolbar())) { 5460 params.is_2d_scale_translation = true; 5461 params.should_snap = true; 5462 } 5463 5464 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, 5465 params); 5466 5467 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager, 5468 aDisplayListBuilder); 5469 return true; 5470 } 5471 5472 bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData, 5473 WebRenderLayerScrollData* aLayerData) { 5474 bool isRelevantToApz = (IsScrollThumbLayer() || IsScrollbarContainer() || 5475 IsZoomingLayer() || ShouldGetFixedAnimationId()); 5476 5477 if (!isRelevantToApz) { 5478 return false; 5479 } 5480 5481 if (!aLayerData) { 5482 return true; 5483 } 5484 5485 if (IsZoomingLayer()) { 5486 aLayerData->SetZoomAnimationId(mWrAnimationId); 5487 return true; 5488 } 5489 5490 if (IsFixedPositionLayer() && ShouldGetFixedAnimationId()) { 5491 aLayerData->SetFixedPositionAnimationId(mWrAnimationId); 5492 return true; 5493 } 5494 5495 MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer()); 5496 5497 aLayerData->SetScrollbarData(mScrollbarData); 5498 5499 if (IsRootScrollbarContainer() && HasDynamicToolbar()) { 5500 aLayerData->SetScrollbarAnimationId(mWrAnimationId); 5501 return true; 5502 } 5503 5504 if (IsScrollThumbLayer()) { 5505 aLayerData->SetScrollbarAnimationId(mWrAnimationId); 5506 LayoutDeviceRect bounds = LayoutDeviceIntRect::FromAppUnits( 5507 mBounds, mFrame->PresContext()->AppUnitsPerDevPixel()); 5508 // Subframe scrollbars are subject to the pinch-zoom scale, 5509 // but root scrollbars are not because they are outside of the 5510 // region that is zoomed. 5511 const float resolution = 5512 IsScrollbarLayerForRoot() 5513 ? 1.0f 5514 : mFrame->PresShell()->GetCumulativeResolution(); 5515 LayerIntRect layerBounds = 5516 RoundedOut(bounds * LayoutDeviceToLayerScale(resolution)); 5517 aLayerData->SetVisibleRect(layerBounds); 5518 } 5519 return true; 5520 } 5521 5522 void nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream) { 5523 aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")", 5524 (int)mFlags, mScrollbarData.mTargetViewId) 5525 .get(); 5526 } 5527 5528 bool nsDisplayViewTransitionCapture::CreateWebRenderCommands( 5529 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 5530 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 5531 nsDisplayListBuilder* aDisplayListBuilder) { 5532 Maybe<wr::SnapshotInfo> si; 5533 nsPresContext* pc = mFrame->PresContext(); 5534 nsIFrame* capturedFrame = 5535 mIsRoot ? pc->FrameConstructor()->GetRootElementStyleFrame() : mFrame; 5536 auto captureRect = 5537 ViewTransition::CapturedInkOverflowRectForFrame(mFrame, mIsRoot); 5538 auto* vt = pc->Document()->GetActiveViewTransition(); 5539 auto key = [&]() -> Maybe<wr::SnapshotImageKey> { 5540 if (NS_WARN_IF(!vt)) { 5541 return Nothing(); 5542 } 5543 const auto* key = 5544 vt->GetImageKeyForCapturedFrame(capturedFrame, aManager, aResources); 5545 return key ? Some(wr::SnapshotImageKey{*key}) : Nothing(); 5546 }(); 5547 VT_LOG_DEBUG( 5548 "nsDisplayViewTransitionCapture::CreateWebrenderCommands(%s, key=%s)", 5549 capturedFrame->ListTag().get(), ToString(key).c_str()); 5550 wr::StackingContextParams params; 5551 params.clip = 5552 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId()); 5553 5554 // This is for the case if this frame is transformed. In this case, the wr 5555 // display list looks like: 5556 // 5557 // PushStackingContext(...) // For the root 5558 // ... 5559 // nsDisplayTransform // For the captured element if it is transformed 5560 // PushReferenceFrame // If the transform matrix is not identity 5561 // PushStackingContext(...) 5562 // VTCapture // For the captured element 5563 // PushReferenceFrame 5564 // PushStackingContext 5565 // ... 5566 // Other display items // For the descendants of the captured element 5567 // ... 5568 // PopStackingContext 5569 // PopReferenceFrame 5570 // PopStackingContext 5571 // PopReferenceFrame 5572 // ... 5573 // PopStackingContext 5574 // 5575 // The wrapper, nsDisplayTransform, creates a new coordinate system whose 5576 // origin is from ToReferenceFrame(mFrame). The VTCapture item is inside this 5577 // nsDisplayTransform, so it's in this coordinate system. This works well if 5578 // the transform matrix is not identity. However, if the transform matrix is 5579 // identity, we don't create a new coordinate system (because we don't push a 5580 // reference frame in WR), at this moment, the |ref_frame_offset| of the 5581 // stacking context of this VTCapture becomes the |origin| of the 5582 // nsDisplayTransform (see wr_dp_push_stacking_context() and 5583 // push_stacking_context() for more details), so this offset may makes the 5584 // position of this VTCapture wrong. (Note that the ToReferenceFrame(mFrame) 5585 // of this VTCapture is the frame itself, but ToReferenceFrame(mFrame) of the 5586 // nsDisplayTransform is its containing frame, e.g. viewport frame.) 5587 // 5588 // Therefore, we push a reference frame for this display item if the frame is 5589 // transformed. This makes this VTCapture creates a new coordinate system, so 5590 // its |ref_frame_offset| will be (0, 0), just fit the nsDisplayTransform's 5591 // position. 5592 // 5593 // FIXME: We can avoid pushing a reference frame if the transform matrix is 5594 // not identity, because we generate a new coordinate system already from the 5595 // nsDisplayTransform (see nsDisplayTransform::CreateWebRenderCommands() for 5596 // more details). 5597 wr::WrTransformInfo info; 5598 if (mFrame->IsTransformed()) { 5599 // Use an identity matrix to force to push a reference frame to create a new 5600 // coordinate system for view transition captured frame. 5601 params.mTransformPtr = [&]() { 5602 info.transform = wr::ToLayoutTransform(gfx::Matrix4x4()); 5603 info.key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(), 5604 wr::SpatialKeyKind::ViewTransition); 5605 return &info; 5606 }(); 5607 params.reference_frame_kind = wr::WrReferenceFrameKind::Transform; 5608 } 5609 5610 if (key) { 5611 vt->UpdateActiveRectForCapturedFrame(capturedFrame, aSc.GetInheritedScale(), 5612 captureRect); 5613 5614 si.emplace(wr::SnapshotInfo{ 5615 .key = *key, 5616 .area = wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits( 5617 captureRect + ToReferenceFrame(), pc->AppUnitsPerDevPixel())), 5618 .detached = true, 5619 }); 5620 params.snapshot = si.ptr(); 5621 } 5622 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, 5623 params); 5624 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager, 5625 aDisplayListBuilder); 5626 return true; 5627 } 5628 5629 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder, 5630 nsIFrame* aFrame, 5631 nsSubDocumentFrame* aSubDocFrame, 5632 nsDisplayList* aList, 5633 nsDisplayOwnLayerFlags aFlags) 5634 : nsDisplayOwnLayer(aBuilder, aFrame, aList, 5635 aBuilder->CurrentActiveScrolledRoot(), 5636 ContainerASRType::Constant, aFlags), 5637 mShouldFlatten(false), 5638 mSubDocFrame(aSubDocFrame) { 5639 MOZ_COUNT_CTOR(nsDisplaySubDocument); 5640 5641 if (aBuilder->IsRetainingDisplayList() && mSubDocFrame && 5642 mSubDocFrame != mFrame) { 5643 mSubDocFrame->AddDisplayItem(this); 5644 } 5645 } 5646 5647 void nsDisplaySubDocument::Destroy(nsDisplayListBuilder* aBuilder) { 5648 RemoveDisplayItemFromFrame(aBuilder, mSubDocFrame); 5649 nsDisplayOwnLayer::Destroy(aBuilder); 5650 } 5651 5652 nsIFrame* nsDisplaySubDocument::FrameForInvalidation() const { 5653 return mSubDocFrame ? mSubDocFrame : mFrame; 5654 } 5655 5656 void nsDisplaySubDocument::RemoveFrame(nsIFrame* aFrame) { 5657 if (aFrame == mSubDocFrame) { 5658 mSubDocFrame = nullptr; 5659 SetDeletedFrame(); 5660 } 5661 nsDisplayOwnLayer::RemoveFrame(aFrame); 5662 } 5663 5664 static bool UseDisplayPortForViewport(nsDisplayListBuilder* aBuilder, 5665 nsIFrame* aFrame) { 5666 return aBuilder->IsPaintingToWindow() && 5667 DisplayPortUtils::ViewportHasDisplayPort(aFrame->PresContext()); 5668 } 5669 5670 nsRect nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder, 5671 bool* aSnap) const { 5672 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame); 5673 5674 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) && 5675 usingDisplayPort) { 5676 *aSnap = false; 5677 return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame); 5678 } 5679 5680 return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap); 5681 } 5682 5683 nsRegion nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, 5684 bool* aSnap) const { 5685 bool usingDisplayPort = UseDisplayPortForViewport(aBuilder, mFrame); 5686 5687 if ((mFlags & nsDisplayOwnLayerFlags::GenerateScrollableLayer) && 5688 usingDisplayPort) { 5689 *aSnap = false; 5690 return nsRegion(); 5691 } 5692 5693 return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap); 5694 } 5695 5696 /* static */ 5697 nsDisplayFixedPosition* nsDisplayFixedPosition::CreateForFixedBackground( 5698 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aSecondaryFrame, 5699 nsDisplayBackgroundImage* aImage, const uint16_t aIndex, 5700 const ActiveScrolledRoot* aScrollTargetASR) { 5701 nsDisplayList temp(aBuilder); 5702 temp.AppendToTop(aImage); 5703 5704 if (aSecondaryFrame) { 5705 auto tableType = GetTableTypeFromFrame(aFrame); 5706 const uint16_t index = CalculateTablePerFrameKey(aIndex + 1, tableType); 5707 return MakeDisplayItemWithIndex<nsDisplayTableFixedPosition>( 5708 aBuilder, aSecondaryFrame, index, &temp, aFrame, aScrollTargetASR); 5709 } 5710 5711 return MakeDisplayItemWithIndex<nsDisplayFixedPosition>( 5712 aBuilder, aFrame, aIndex + 1, &temp, aScrollTargetASR); 5713 } 5714 5715 nsDisplayFixedPosition::nsDisplayFixedPosition( 5716 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 5717 const ActiveScrolledRoot* aActiveScrolledRoot, 5718 ContainerASRType aContainerASRType, 5719 const ActiveScrolledRoot* aScrollTargetASR, bool aForceIsolation) 5720 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot, 5721 aContainerASRType), 5722 mScrollTargetASR(aScrollTargetASR), 5723 mIsFixedBackground(false), 5724 mForceIsolation(aForceIsolation) { 5725 MOZ_COUNT_CTOR(nsDisplayFixedPosition); 5726 MOZ_ASSERT_IF(mScrollTargetASR, 5727 mScrollTargetASR->mKind == ActiveScrolledRoot::ASRKind::Scroll); 5728 } 5729 5730 nsDisplayFixedPosition::nsDisplayFixedPosition( 5731 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 5732 const ActiveScrolledRoot* aScrollTargetASR) 5733 : nsDisplayOwnLayer(aBuilder, aFrame, aList, 5734 aBuilder->CurrentActiveScrolledRoot(), 5735 ContainerASRType::Constant), 5736 // For fixed backgrounds, this is the ASR for the nearest scroll frame. 5737 mScrollTargetASR(aScrollTargetASR), 5738 mIsFixedBackground(true), 5739 mForceIsolation(false) { 5740 MOZ_COUNT_CTOR(nsDisplayFixedPosition); 5741 MOZ_ASSERT_IF(mScrollTargetASR, 5742 mScrollTargetASR->mKind == ActiveScrolledRoot::ASRKind::Scroll); 5743 } 5744 5745 ScrollableLayerGuid::ViewID nsDisplayFixedPosition::GetScrollTargetId() const { 5746 if (mScrollTargetASR && 5747 (mIsFixedBackground || !nsLayoutUtils::IsReallyFixedPos(mFrame))) { 5748 return mScrollTargetASR->GetViewId(); 5749 } 5750 return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()); 5751 } 5752 5753 bool nsDisplayFixedPosition::CreateWebRenderCommands( 5754 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 5755 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 5756 nsDisplayListBuilder* aDisplayListBuilder) { 5757 SideBits sides = SideBits::eNone; 5758 if (!mIsFixedBackground) { 5759 sides = nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame); 5760 } 5761 5762 // We install this RAII scrolltarget tracker so that any 5763 // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that 5764 // share the same ASR as this item) use the correct scroll target. That way 5765 // attempts to scroll on those items will scroll the root scroll frame. 5766 wr::DisplayListBuilder::FixedPosScrollTargetTracker tracker( 5767 aBuilder, GetActiveScrolledRoot(), GetScrollTargetId(), sides); 5768 return nsDisplayOwnLayer::CreateWebRenderCommands( 5769 aBuilder, aResources, aSc, aManager, aDisplayListBuilder); 5770 } 5771 5772 bool nsDisplayFixedPosition::UpdateScrollData( 5773 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) { 5774 if (aLayerData) { 5775 if (!mIsFixedBackground) { 5776 aLayerData->SetFixedPositionSides( 5777 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame)); 5778 } 5779 aLayerData->SetFixedPositionScrollContainerId(GetScrollTargetId()); 5780 } 5781 nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData); 5782 return true; 5783 } 5784 5785 bool nsDisplayFixedPosition::ShouldGetFixedAnimationId() { 5786 #if defined(MOZ_WIDGET_ANDROID) 5787 return mFrame->PresContext()->IsRootContentDocumentCrossProcess() && 5788 nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) == 5789 GetScrollTargetId(); 5790 #else 5791 return HasDynamicToolbar() && 5792 (nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) == 5793 GetScrollTargetId()); 5794 #endif 5795 } 5796 5797 void nsDisplayFixedPosition::WriteDebugInfo(std::stringstream& aStream) { 5798 aStream << nsPrintfCString( 5799 " (containerASR %s) (scrolltarget %" PRIu64 ")", 5800 ActiveScrolledRoot::ToString(mScrollTargetASR).get(), 5801 GetScrollTargetId()) 5802 .get(); 5803 } 5804 5805 TableType GetTableTypeFromFrame(nsIFrame* aFrame) { 5806 if (aFrame->IsTableFrame()) { 5807 return TableType::Table; 5808 } 5809 5810 if (aFrame->IsTableColFrame()) { 5811 return TableType::TableCol; 5812 } 5813 5814 if (aFrame->IsTableColGroupFrame()) { 5815 return TableType::TableColGroup; 5816 } 5817 5818 if (aFrame->IsTableRowFrame()) { 5819 return TableType::TableRow; 5820 } 5821 5822 if (aFrame->IsTableRowGroupFrame()) { 5823 return TableType::TableRowGroup; 5824 } 5825 5826 if (aFrame->IsTableCellFrame()) { 5827 return TableType::TableCell; 5828 } 5829 5830 MOZ_ASSERT_UNREACHABLE("Invalid frame."); 5831 return TableType::Table; 5832 } 5833 5834 nsDisplayTableFixedPosition::nsDisplayTableFixedPosition( 5835 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 5836 nsIFrame* aAncestorFrame, const ActiveScrolledRoot* aScrollTargetASR) 5837 : nsDisplayFixedPosition(aBuilder, aFrame, aList, aScrollTargetASR), 5838 mAncestorFrame(aAncestorFrame) { 5839 if (aBuilder->IsRetainingDisplayList()) { 5840 mAncestorFrame->AddDisplayItem(this); 5841 } 5842 } 5843 5844 void nsDisplayTableFixedPosition::Destroy(nsDisplayListBuilder* aBuilder) { 5845 RemoveDisplayItemFromFrame(aBuilder, mAncestorFrame); 5846 nsDisplayFixedPosition::Destroy(aBuilder); 5847 } 5848 5849 nsDisplayStickyPosition::nsDisplayStickyPosition( 5850 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 5851 const ActiveScrolledRoot* aActiveScrolledRoot, 5852 ContainerASRType aContainerASRType, const ActiveScrolledRoot* aContainerASR) 5853 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot, 5854 aContainerASRType, nsDisplayOwnLayerFlags::None, 5855 layers::ScrollbarData{}, 5856 /*aForceActive=*/true, /*aClearClipChain=*/true), 5857 mContainerASR(aContainerASR), 5858 mShouldFlatten(false) { 5859 MOZ_COUNT_CTOR(nsDisplayStickyPosition); 5860 } 5861 5862 StickyScrollContainer* nsDisplayStickyPosition::GetStickyScrollContainer() { 5863 auto* ssc = StickyScrollContainer::GetOrCreateForFrame(mFrame); 5864 if (!ssc) { 5865 return nullptr; 5866 } 5867 // If there's no ASR for the scrollframe that this sticky item is attached 5868 // to, then don't create a WR sticky item for it either. Trying to do so 5869 // will end in sadness because WR will interpret some coordinates as 5870 // relative to the nearest enclosing scrollframe, which will correspond 5871 // to the nearest ancestor ASR on the gecko side. That ASR will not be the 5872 // same as the scrollframe this sticky item is actually supposed to be 5873 // attached to, thus the sadness. 5874 // Not sending WR the sticky item is ok, because the enclosing scrollframe 5875 // will never be asynchronously scrolled. Instead we will always position 5876 // the sticky items correctly on the gecko side and WR will never need to 5877 // adjust their position itself. 5878 MOZ_ASSERT(ssc->ScrollContainer()->IsMaybeAsynchronouslyScrolled()); 5879 if (!ssc->ScrollContainer()->IsMaybeAsynchronouslyScrolled()) { 5880 return nullptr; 5881 } 5882 return ssc; 5883 } 5884 5885 bool nsDisplayStickyPosition::CreateWebRenderCommands( 5886 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 5887 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 5888 nsDisplayListBuilder* aDisplayListBuilder) { 5889 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer(); 5890 5891 Maybe<wr::SpaceAndClipChainHelper> saccHelper; 5892 5893 if (stickyScrollContainer && !stickyScrollContainer->ShouldFlattenAway()) { 5894 const ActiveScrolledRoot* stickyAsr = 5895 ActiveScrolledRoot::GetStickyASRFromFrame(mFrame); 5896 MOZ_ASSERT(stickyAsr); 5897 auto spatialId = aBuilder.GetSpatialIdForDefinedStickyLayer(stickyAsr); 5898 MOZ_ASSERT(spatialId.isSome()); 5899 saccHelper.emplace(aBuilder, *spatialId); 5900 } 5901 5902 { 5903 wr::StackingContextParams params; 5904 params.clip = 5905 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId()); 5906 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, 5907 aBuilder, params); 5908 nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, sc, 5909 aManager, aDisplayListBuilder); 5910 } 5911 5912 return true; 5913 } 5914 5915 void nsDisplayStickyPosition::CalculateLayerScrollRanges( 5916 StickyScrollContainer* aStickyScrollContainer, float aAppUnitsPerDevPixel, 5917 float aScaleX, float aScaleY, LayerRectAbsolute& aStickyOuter, 5918 LayerRectAbsolute& aStickyInner) { 5919 nsRectAbsolute outer; 5920 nsRectAbsolute inner; 5921 aStickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner); 5922 aStickyOuter.SetBox( 5923 NSAppUnitsToFloatPixels(outer.X(), aAppUnitsPerDevPixel) * aScaleX, 5924 NSAppUnitsToFloatPixels(outer.Y(), aAppUnitsPerDevPixel) * aScaleY, 5925 NSAppUnitsToFloatPixels(outer.XMost(), aAppUnitsPerDevPixel) * aScaleX, 5926 NSAppUnitsToFloatPixels(outer.YMost(), aAppUnitsPerDevPixel) * aScaleY); 5927 aStickyInner.SetBox( 5928 NSAppUnitsToFloatPixels(inner.X(), aAppUnitsPerDevPixel) * aScaleX, 5929 NSAppUnitsToFloatPixels(inner.Y(), aAppUnitsPerDevPixel) * aScaleY, 5930 NSAppUnitsToFloatPixels(inner.XMost(), aAppUnitsPerDevPixel) * aScaleX, 5931 NSAppUnitsToFloatPixels(inner.YMost(), aAppUnitsPerDevPixel) * aScaleY); 5932 } 5933 5934 bool nsDisplayStickyPosition::UpdateScrollData( 5935 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) { 5936 bool hasDynamicToolbar = HasDynamicToolbar(); 5937 if (aLayerData && hasDynamicToolbar) { 5938 StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer(); 5939 if (stickyScrollContainer) { 5940 float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); 5941 float cumulativeResolution = 5942 mFrame->PresShell()->GetCumulativeResolution(); 5943 LayerRectAbsolute stickyOuter; 5944 LayerRectAbsolute stickyInner; 5945 CalculateLayerScrollRanges(stickyScrollContainer, auPerDevPixel, 5946 cumulativeResolution, cumulativeResolution, 5947 stickyOuter, stickyInner); 5948 aLayerData->SetStickyScrollRangeOuter(stickyOuter); 5949 aLayerData->SetStickyScrollRangeInner(stickyInner); 5950 5951 SideBits sides = 5952 nsLayoutUtils::GetSideBitsForFixedPositionContent(mFrame); 5953 aLayerData->SetFixedPositionSides(sides); 5954 5955 ScrollableLayerGuid::ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor( 5956 stickyScrollContainer->ScrollContainer() 5957 ->GetScrolledFrame() 5958 ->GetContent()); 5959 aLayerData->SetStickyPositionScrollContainerId(scrollId); 5960 } 5961 5962 if (ShouldGetStickyAnimationId()) { 5963 // TODO(follow-up to bug 1730749): Should there be a 5964 // "GetWebRenderUserData" method we should be calling here, rather than 5965 // "CreateOrRecycle"? 5966 RefPtr<WebRenderAPZAnimationData> animationData = 5967 aData->GetManager() 5968 ->CommandBuilder() 5969 .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>( 5970 this); 5971 MOZ_ASSERT(animationData); 5972 aLayerData->SetStickyPositionAnimationId(animationData->GetAnimationId()); 5973 } 5974 } 5975 // Return true if either there is a dynamic toolbar affecting this sticky 5976 // item or the OwnLayer base implementation returns true for some other 5977 // reason. 5978 bool ret = hasDynamicToolbar; 5979 ret |= nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData); 5980 return ret; 5981 } 5982 5983 bool nsDisplayStickyPosition::ShouldGetStickyAnimationId( 5984 nsIFrame* aStickyFrame) { 5985 // Also implies being in the cross-process RCD. 5986 // The animation id is how we translate the layer by the dynamic toolbar 5987 // offset. It's not needed for regular sticky positioning, that's handled 5988 // by WebRender without an animation id. 5989 return nsDisplayOwnLayer::HasDynamicToolbar(aStickyFrame); 5990 } 5991 5992 bool nsDisplayStickyPosition::ShouldGetStickyAnimationId() const { 5993 return ShouldGetStickyAnimationId(mFrame); 5994 } 5995 5996 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer( 5997 nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame, 5998 nsIFrame* aScrollFrame, const CompositorHitTestInfo& aHitInfo, 5999 const nsRect& aHitArea) 6000 : nsDisplayWrapList(aBuilder, aScrollFrame), 6001 mScrollFrame(aScrollFrame), 6002 mScrolledFrame(aScrolledFrame), 6003 mScrollParentId(aBuilder->GetCurrentScrollParentId()), 6004 mHitInfo(aHitInfo), 6005 mHitArea(aHitArea) { 6006 #ifdef NS_BUILD_REFCNT_LOGGING 6007 MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer); 6008 #endif 6009 } 6010 6011 UniquePtr<ScrollMetadata> nsDisplayScrollInfoLayer::ComputeScrollMetadata( 6012 nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager) { 6013 ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata( 6014 mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), Frame(), 6015 ToReferenceFrame(), aLayerManager, mScrollParentId, 6016 mScrollFrame->GetSize(), false); 6017 metadata.GetMetrics().SetIsScrollInfoLayer(true); 6018 ScrollContainerFrame* scrollContainerFrame = 6019 mScrollFrame->GetScrollTargetFrame(); 6020 if (scrollContainerFrame) { 6021 aBuilder->AddScrollContainerFrameToNotify(scrollContainerFrame); 6022 } 6023 6024 return UniquePtr<ScrollMetadata>(new ScrollMetadata(metadata)); 6025 } 6026 6027 bool nsDisplayScrollInfoLayer::UpdateScrollData( 6028 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) { 6029 if (aLayerData) { 6030 UniquePtr<ScrollMetadata> metadata = 6031 ComputeScrollMetadata(aData->GetBuilder(), aData->GetManager()); 6032 MOZ_ASSERT(aData); 6033 MOZ_ASSERT(metadata); 6034 aLayerData->AppendScrollMetadata(*aData, *metadata); 6035 } 6036 return true; 6037 } 6038 6039 bool nsDisplayScrollInfoLayer::CreateWebRenderCommands( 6040 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 6041 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 6042 nsDisplayListBuilder* aDisplayListBuilder) { 6043 ScrollableLayerGuid::ViewID scrollId = 6044 nsLayoutUtils::FindOrCreateIDFor(mScrollFrame->GetContent()); 6045 6046 const LayoutDeviceRect devRect = LayoutDeviceRect::FromAppUnits( 6047 mHitArea, mScrollFrame->PresContext()->AppUnitsPerDevPixel()); 6048 6049 const wr::LayoutRect rect = wr::ToLayoutRect(devRect); 6050 6051 aBuilder.PushHitTest(rect, rect, !BackfaceIsHidden(), scrollId, mHitInfo, 6052 SideBits::eNone); 6053 6054 return true; 6055 } 6056 6057 void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) { 6058 aStream << " (scrollframe " << mScrollFrame << " scrolledFrame " 6059 << mScrolledFrame << ")"; 6060 } 6061 6062 nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, 6063 nsSubDocumentFrame* aSubDocFrame, 6064 nsDisplayList* aList, int32_t aAPD, 6065 int32_t aParentAPD, nsDisplayOwnLayerFlags aFlags) 6066 : nsDisplaySubDocument(aBuilder, aFrame, aSubDocFrame, aList, aFlags), 6067 mAPD(aAPD), 6068 mParentAPD(aParentAPD) { 6069 MOZ_COUNT_CTOR(nsDisplayZoom); 6070 } 6071 6072 nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder, 6073 bool* aSnap) const { 6074 nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap); 6075 *aSnap = false; 6076 return bounds.ScaleToOtherAppUnitsRoundOut(mAPD, mParentAPD); 6077 } 6078 6079 void nsDisplayZoom::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, 6080 HitTestState* aState, 6081 nsTArray<nsIFrame*>* aOutFrames) { 6082 nsRect rect; 6083 // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1 6084 // rect as well instead of possibly rounding the width or height to zero. 6085 if (aRect.width == 1 && aRect.height == 1) { 6086 rect.MoveTo(aRect.TopLeft().ScaleToOtherAppUnits(mParentAPD, mAPD)); 6087 rect.width = rect.height = 1; 6088 } else { 6089 rect = aRect.ScaleToOtherAppUnitsRoundOut(mParentAPD, mAPD); 6090 } 6091 mList.HitTest(aBuilder, rect, aState, aOutFrames); 6092 } 6093 6094 nsDisplayAsyncZoom::nsDisplayAsyncZoom( 6095 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 6096 const ActiveScrolledRoot* aActiveScrolledRoot, 6097 ContainerASRType aContainerASRType, FrameMetrics::ViewID aViewID) 6098 : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot, 6099 aContainerASRType), 6100 mViewID(aViewID) { 6101 MOZ_COUNT_CTOR(nsDisplayAsyncZoom); 6102 } 6103 6104 void nsDisplayAsyncZoom::HitTest(nsDisplayListBuilder* aBuilder, 6105 const nsRect& aRect, HitTestState* aState, 6106 nsTArray<nsIFrame*>* aOutFrames) { 6107 #ifdef DEBUG 6108 ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(mFrame); 6109 MOZ_ASSERT(scrollContainerFrame && 6110 ViewportUtils::IsZoomedContentRoot( 6111 scrollContainerFrame->GetScrolledFrame())); 6112 #endif 6113 nsRect rect = ViewportUtils::VisualToLayout(aRect, mFrame->PresShell()); 6114 mList.HitTest(aBuilder, rect, aState, aOutFrames); 6115 } 6116 6117 bool nsDisplayAsyncZoom::UpdateScrollData( 6118 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) { 6119 bool ret = nsDisplayOwnLayer::UpdateScrollData(aData, aLayerData); 6120 MOZ_ASSERT(ret); 6121 if (aLayerData) { 6122 aLayerData->SetAsyncZoomContainerId(mViewID); 6123 } 6124 return ret; 6125 } 6126 6127 /////////////////////////////////////////////////// 6128 // nsDisplayTransform Implementation 6129 // 6130 6131 #ifndef DEBUG 6132 static_assert(sizeof(nsDisplayTransform) <= 512, 6133 "nsDisplayTransform has grown"); 6134 #endif 6135 6136 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, 6137 nsIFrame* aFrame, nsDisplayList* aList, 6138 const nsRect& aChildrenBuildingRect) 6139 : nsPaintedDisplayItem(aBuilder, aFrame), 6140 mChildren(aBuilder), 6141 mTransform(Some(Matrix4x4())), 6142 mChildrenBuildingRect(aChildrenBuildingRect), 6143 mPrerenderDecision(PrerenderDecision::No), 6144 mIsTransformSeparator(true), 6145 mHasTransformGetter(false), 6146 mHasAssociatedPerspective(false), 6147 mContainsASRs(false), 6148 mWrapsBackdropFilter(false), 6149 mForceIsolation(false) { 6150 MOZ_COUNT_CTOR(nsDisplayTransform); 6151 MOZ_ASSERT(aFrame, "Must have a frame!"); 6152 Init(aBuilder, aList); 6153 } 6154 6155 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, 6156 nsIFrame* aFrame, nsDisplayList* aList, 6157 const nsRect& aChildrenBuildingRect, 6158 PrerenderDecision aPrerenderDecision, 6159 bool aWrapsBackdropFilter, 6160 bool aForceIsolation) 6161 : nsPaintedDisplayItem(aBuilder, aFrame), 6162 mChildren(aBuilder), 6163 mChildrenBuildingRect(aChildrenBuildingRect), 6164 mPrerenderDecision(aPrerenderDecision), 6165 mIsTransformSeparator(false), 6166 mHasTransformGetter(false), 6167 mHasAssociatedPerspective(false), 6168 mContainsASRs(false), 6169 mWrapsBackdropFilter(aWrapsBackdropFilter), 6170 mForceIsolation(aForceIsolation) { 6171 MOZ_COUNT_CTOR(nsDisplayTransform); 6172 MOZ_ASSERT(aFrame, "Must have a frame!"); 6173 SetReferenceFrameToAncestor(aBuilder); 6174 Init(aBuilder, aList); 6175 } 6176 6177 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, 6178 nsIFrame* aFrame, nsDisplayList* aList, 6179 const nsRect& aChildrenBuildingRect, 6180 decltype(WithTransformGetter)) 6181 : nsPaintedDisplayItem(aBuilder, aFrame), 6182 mChildren(aBuilder), 6183 mChildrenBuildingRect(aChildrenBuildingRect), 6184 mPrerenderDecision(PrerenderDecision::No), 6185 mIsTransformSeparator(false), 6186 mHasTransformGetter(true), 6187 mHasAssociatedPerspective(false), 6188 mContainsASRs(false), 6189 mWrapsBackdropFilter(false), 6190 mForceIsolation(false) { 6191 MOZ_COUNT_CTOR(nsDisplayTransform); 6192 MOZ_ASSERT(aFrame, "Must have a frame!"); 6193 MOZ_ASSERT(aFrame->GetTransformGetter()); 6194 Init(aBuilder, aList); 6195 } 6196 6197 void nsDisplayTransform::SetReferenceFrameToAncestor( 6198 nsDisplayListBuilder* aBuilder) { 6199 if (mFrame == aBuilder->RootReferenceFrame()) { 6200 return; 6201 } 6202 // We manually recompute mToReferenceFrame without going through the 6203 // builder, since this won't apply the 'additional offset'. Our 6204 // children will already be painting with that applied, and we don't 6205 // want to include it a second time in our transform. We don't recompute 6206 // our visible/building rects, since those should still include the additional 6207 // offset. 6208 // TODO: Are there are things computed using our ToReferenceFrame that should 6209 // have the additional offset applied? Should we instead just manually remove 6210 // the offset from our transform instead of this more general value? 6211 // Can we instead apply the additional offset to us and not our children, like 6212 // we do for all other offsets (and how reference frames are supposed to 6213 // work)? 6214 // The reference frame on the builder has already been set to the ancestor 6215 // reference frame by BuildDisplayListForStackingContext using 6216 // SetReferenceFrameAndCurrentOffset, so we just compute the offset. 6217 MOZ_ASSERT(aBuilder->FindReferenceFrameFor( 6218 nsLayoutUtils::GetCrossDocParentFrameInProcess(mFrame)) == 6219 aBuilder->GetCurrentReferenceFrame()); 6220 mToReferenceFrame = 6221 mFrame->GetOffsetToCrossDoc(aBuilder->GetCurrentReferenceFrame()); 6222 } 6223 6224 void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder, 6225 nsDisplayList* aChildren) { 6226 mChildren.AppendToTop(aChildren); 6227 UpdateBounds(aBuilder); 6228 } 6229 6230 bool nsDisplayTransform::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) { 6231 return false; 6232 } 6233 6234 /* Returns the delta specified by the transform-origin property. 6235 * This is a positive delta, meaning that it indicates the direction to move 6236 * to get from (0, 0) of the frame to the transform origin. This function is 6237 * called off the main thread. 6238 */ 6239 /* static */ 6240 Point3D nsDisplayTransform::GetDeltaToTransformOrigin( 6241 const nsIFrame* aFrame, TransformReferenceBox& aRefBox, 6242 float aAppUnitsPerPixel) { 6243 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!"); 6244 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() || 6245 aFrame->Combines3DTransformWithAncestors(), 6246 "Shouldn't get a delta for an untransformed frame!"); 6247 6248 if (!aFrame->IsTransformed()) { 6249 return Point3D(); 6250 } 6251 6252 /* For both of the coordinates, if the value of transform is a 6253 * percentage, it's relative to the size of the frame. Otherwise, if it's 6254 * a distance, it's already computed for us! 6255 */ 6256 const nsStyleDisplay* display = aFrame->StyleDisplay(); 6257 6258 const StyleTransformOrigin& transformOrigin = display->mTransformOrigin; 6259 CSSPoint origin = nsStyleTransformMatrix::Convert2DPosition( 6260 transformOrigin.horizontal, transformOrigin.vertical, aRefBox); 6261 6262 // Note: 6263 // 1. SVG frames have a reference box that can be (and typically is) offset 6264 // from the TopLeft() of the frame. We need to account for that here. 6265 // 2. If we are using transform-box:content-box in CSS layout, we have the 6266 // offset from TopLeft() of the frame as well. 6267 origin.x += CSSPixel::FromAppUnits(aRefBox.X()); 6268 origin.y += CSSPixel::FromAppUnits(aRefBox.Y()); 6269 6270 float scale = AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel); 6271 float z = transformOrigin.depth._0; 6272 return Point3D(origin.x * scale, origin.y * scale, z * scale); 6273 } 6274 6275 /* static */ 6276 bool nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame, 6277 float aAppUnitsPerPixel, 6278 Matrix4x4& aOutMatrix) { 6279 MOZ_ASSERT(aFrame, "Can't get delta for a null frame!"); 6280 MOZ_ASSERT(aFrame->IsTransformed() || aFrame->BackfaceIsHidden() || 6281 aFrame->Combines3DTransformWithAncestors(), 6282 "Shouldn't get a delta for an untransformed frame!"); 6283 MOZ_ASSERT(aOutMatrix.IsIdentity(), "Must have a blank output matrix"); 6284 6285 if (!aFrame->IsTransformed()) { 6286 return false; 6287 } 6288 6289 // TODO: Is it possible that the perspectiveFrame's bounds haven't been set 6290 // correctly yet (similar to the aBoundsOverride case for 6291 // GetResultingTransformMatrix)? 6292 nsIFrame* perspectiveFrame = 6293 aFrame->GetClosestFlattenedTreeAncestorPrimaryFrame(); 6294 if (!perspectiveFrame) { 6295 return false; 6296 } 6297 6298 /* Grab the values for perspective and perspective-origin (if present) */ 6299 const nsStyleDisplay* perspectiveDisplay = perspectiveFrame->StyleDisplay(); 6300 if (perspectiveDisplay->mChildPerspective.IsNone()) { 6301 return false; 6302 } 6303 6304 MOZ_ASSERT(perspectiveDisplay->mChildPerspective.IsLength()); 6305 float perspective = 6306 perspectiveDisplay->mChildPerspective.AsLength().ToCSSPixels(); 6307 perspective = std::max(1.0f, perspective); 6308 if (perspective < std::numeric_limits<Float>::epsilon()) { 6309 return true; 6310 } 6311 6312 TransformReferenceBox refBox(perspectiveFrame); 6313 6314 Point perspectiveOrigin = nsStyleTransformMatrix::Convert2DPosition( 6315 perspectiveDisplay->mPerspectiveOrigin.horizontal, 6316 perspectiveDisplay->mPerspectiveOrigin.vertical, refBox, 6317 aAppUnitsPerPixel); 6318 6319 /* GetOffsetTo computes the offset required to move from 0,0 in 6320 * perspectiveFrame to 0,0 in aFrame. Although we actually want the inverse of 6321 * this, it's faster to compute this way. 6322 */ 6323 nsPoint frameToPerspectiveOffset = -aFrame->GetOffsetTo(perspectiveFrame); 6324 Point frameToPerspectiveGfxOffset( 6325 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.x, aAppUnitsPerPixel), 6326 NSAppUnitsToFloatPixels(frameToPerspectiveOffset.y, aAppUnitsPerPixel)); 6327 6328 /* Move the perspective origin to be relative to aFrame, instead of relative 6329 * to the containing block which is how it was specified in the style system. 6330 */ 6331 perspectiveOrigin += frameToPerspectiveGfxOffset; 6332 6333 aOutMatrix._34 = 6334 -1.0 / NSAppUnitsToFloatPixels(CSSPixel::ToAppUnits(perspective), 6335 aAppUnitsPerPixel); 6336 6337 aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0)); 6338 return true; 6339 } 6340 6341 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties( 6342 const nsIFrame* aFrame, TransformReferenceBox& aRefBox, 6343 float aAppUnitsPerPixel) 6344 : mFrame(aFrame), 6345 mTranslate(aFrame->StyleDisplay()->mTranslate), 6346 mRotate(aFrame->StyleDisplay()->mRotate), 6347 mScale(aFrame->StyleDisplay()->mScale), 6348 mTransform(aFrame->StyleDisplay()->mTransform), 6349 mMotion(aFrame->StyleDisplay()->mOffsetPath.IsNone() 6350 ? Nothing() 6351 : MotionPathUtils::ResolveMotionPath(aFrame, aRefBox)), 6352 mToTransformOrigin( 6353 GetDeltaToTransformOrigin(aFrame, aRefBox, aAppUnitsPerPixel)) {} 6354 6355 /* Wraps up the transform matrix in a change-of-basis matrix pair that 6356 * translates from local coordinate space to transform coordinate space, then 6357 * hands it back. 6358 */ 6359 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix( 6360 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox, 6361 float aAppUnitsPerPixel) { 6362 return GetResultingTransformMatrixInternal(aProperties, aRefBox, nsPoint(), 6363 aAppUnitsPerPixel, 0); 6364 } 6365 6366 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrix( 6367 const nsIFrame* aFrame, const nsPoint& aOrigin, float aAppUnitsPerPixel, 6368 uint32_t aFlags) { 6369 TransformReferenceBox refBox(aFrame); 6370 FrameTransformProperties props(aFrame, refBox, aAppUnitsPerPixel); 6371 return GetResultingTransformMatrixInternal(props, refBox, aOrigin, 6372 aAppUnitsPerPixel, aFlags); 6373 } 6374 6375 Matrix4x4 nsDisplayTransform::GetResultingTransformMatrixInternal( 6376 const FrameTransformProperties& aProperties, TransformReferenceBox& aRefBox, 6377 const nsPoint& aOrigin, float aAppUnitsPerPixel, uint32_t aFlags) { 6378 const nsIFrame* frame = aProperties.mFrame; 6379 NS_ASSERTION(frame || !(aFlags & INCLUDE_PERSPECTIVE), 6380 "Must have a frame to compute perspective!"); 6381 6382 // IncrementScaleRestyleCountIfNeeded in ActiveLayerTracker.cpp is a 6383 // simplified copy of this function. 6384 6385 // Get the underlying transform matrix: 6386 6387 /* Get the matrix, then change its basis to factor in the origin. */ 6388 Matrix4x4 result; 6389 6390 // See the comment for SVGContainerFrame::HasChildrenOnlyTransform for 6391 // an explanation of what children-only transforms are. 6392 Matrix parentsChildrenOnlyTransform; 6393 const bool parentHasChildrenOnlyTransform = 6394 frame && frame->HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) && 6395 frame->GetParentSVGTransforms(&parentsChildrenOnlyTransform) && 6396 !parentsChildrenOnlyTransform.IsIdentity(); 6397 bool shouldRound = nsLayoutUtils::ShouldSnapToGrid(frame); 6398 6399 /* Transformed frames always have a transform, or are preserving 3d (and might 6400 * still have perspective!) */ 6401 if (aProperties.HasTransform()) { 6402 result = nsStyleTransformMatrix::ReadTransforms( 6403 aProperties.mTranslate, aProperties.mRotate, aProperties.mScale, 6404 aProperties.mMotion.ptrOr(nullptr), aProperties.mTransform, aRefBox, 6405 aAppUnitsPerPixel); 6406 } 6407 6408 // Apply any translation due to 'transform-origin' and/or 'transform-box': 6409 if (aProperties.mToTransformOrigin != gfx::Point3D()) { 6410 result.ChangeBasis(aProperties.mToTransformOrigin); 6411 } 6412 6413 if (parentHasChildrenOnlyTransform) { 6414 float pixelsPerCSSPx = AppUnitsPerCSSPixel() / aAppUnitsPerPixel; 6415 parentsChildrenOnlyTransform._31 *= pixelsPerCSSPx; 6416 parentsChildrenOnlyTransform._32 *= pixelsPerCSSPx; 6417 auto parentsChildrenOnlyTransform3D = 6418 Matrix4x4::From2D(parentsChildrenOnlyTransform); 6419 // <svg> outer anon child frame doesn't need the extra basis change because 6420 // it is the root of the svg rendering (and thus its offset with respect to 6421 // its parent, like border / padding shouldn't be accounted for). 6422 if (frame->GetPosition() != nsPoint() && 6423 !frame->IsSVGOuterSVGAnonChildFrame()) { 6424 const Point3D frameOffset( 6425 NSAppUnitsToFloatPixels(-frame->GetPosition().x, aAppUnitsPerPixel), 6426 NSAppUnitsToFloatPixels(-frame->GetPosition().y, aAppUnitsPerPixel), 6427 0); 6428 parentsChildrenOnlyTransform3D.ChangeBasis(frameOffset); 6429 } 6430 6431 result *= parentsChildrenOnlyTransform3D; 6432 } 6433 6434 Matrix4x4 perspectiveMatrix; 6435 bool hasPerspective = aFlags & INCLUDE_PERSPECTIVE; 6436 if (hasPerspective) { 6437 if (ComputePerspectiveMatrix(frame, aAppUnitsPerPixel, perspectiveMatrix)) { 6438 result *= perspectiveMatrix; 6439 } 6440 } 6441 6442 if ((aFlags & INCLUDE_PRESERVE3D_ANCESTORS) && frame && 6443 frame->Combines3DTransformWithAncestors()) { 6444 // Include the transform set on our parent 6445 nsIFrame* parentFrame = 6446 frame->GetClosestFlattenedTreeAncestorPrimaryFrame(); 6447 NS_ASSERTION(parentFrame && parentFrame->IsTransformed() && 6448 parentFrame->Extend3DContext(), 6449 "Preserve3D mismatch!"); 6450 TransformReferenceBox refBox(parentFrame); 6451 FrameTransformProperties props(parentFrame, refBox, aAppUnitsPerPixel); 6452 6453 // Whenever we are including preserve3d we want to also include perspective 6454 // (if it exists). 6455 uint32_t flags = (INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE); 6456 6457 // If this frame isn't transformed (but we exist for backface-visibility), 6458 // then we're not a reference frame so no offset to origin will be added. 6459 // Otherwise we need to manually translate into our parent's coordinate 6460 // space. 6461 if (frame->IsTransformed()) { 6462 nsLayoutUtils::PostTranslate(result, frame->GetPosition(), 6463 aAppUnitsPerPixel, shouldRound); 6464 } 6465 Matrix4x4 parent = GetResultingTransformMatrixInternal( 6466 props, refBox, nsPoint(0, 0), aAppUnitsPerPixel, flags); 6467 result = result * parent; 6468 } 6469 6470 MOZ_ASSERT((aOrigin == nsPoint()) || (aFlags & OFFSET_BY_ORIGIN)); 6471 if ((aFlags & OFFSET_BY_ORIGIN) && (aOrigin != nsPoint())) { 6472 nsLayoutUtils::PostTranslate(result, aOrigin, aAppUnitsPerPixel, 6473 shouldRound); 6474 } 6475 6476 return result; 6477 } 6478 6479 bool nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) { 6480 static constexpr nsCSSPropertyIDSet opacitySet = 6481 nsCSSPropertyIDSet::OpacityProperties(); 6482 if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, opacitySet)) { 6483 return true; 6484 } 6485 6486 EffectCompositor::SetPerformanceWarning( 6487 mFrame, opacitySet, 6488 AnimationPerformanceWarning( 6489 AnimationPerformanceWarning::Type::OpacityFrameInactive)); 6490 6491 return false; 6492 } 6493 6494 bool nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) { 6495 return mPrerenderDecision != PrerenderDecision::No; 6496 } 6497 6498 bool nsDisplayBackgroundColor::CanUseAsyncAnimations( 6499 nsDisplayListBuilder* aBuilder) { 6500 return StaticPrefs::gfx_omta_background_color(); 6501 } 6502 6503 void nsDisplayTableBackgroundColor::Destroy(nsDisplayListBuilder* aBuilder) { 6504 RemoveDisplayItemFromFrame(aBuilder, mAncestorFrame); 6505 nsDisplayBackgroundColor::Destroy(aBuilder); 6506 } 6507 6508 static bool IsInStickyPositionedSubtree(const nsIFrame* aFrame) { 6509 for (const nsIFrame* frame = aFrame; frame; 6510 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame)) { 6511 if (frame->IsStickyPositioned()) { 6512 return true; 6513 } 6514 } 6515 return false; 6516 } 6517 6518 static bool ShouldUsePartialPrerender(const nsIFrame* aFrame) { 6519 return StaticPrefs::layout_animation_prerender_partial() && 6520 // Bug 1642547: Support partial prerender for position:sticky elements. 6521 !IsInStickyPositionedSubtree(aFrame); 6522 } 6523 6524 /* static */ 6525 auto nsDisplayTransform::ShouldPrerenderTransformedContent( 6526 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect) 6527 -> PrerenderInfo { 6528 PrerenderInfo result; 6529 6530 // Prerendering only makes sense if we are painting to the window so that the 6531 // extra prerendered content can be animated into view by the compositor. 6532 // GenerateGlyphMask (for background-clip: text) uses a nested builder, so it 6533 // can be inside a builder that is painting to window, and it's buggy, so just 6534 // allow it to minimize bugs. 6535 if (!aBuilder->IsPaintingToWindow() && !aBuilder->IsForGenerateGlyphMask()) { 6536 return result; 6537 } 6538 6539 // If we are in a preserve-3d tree, and we've disallowed async animations, we 6540 // return No prerender decision directly. 6541 if ((aFrame->Extend3DContext() || 6542 aFrame->Combines3DTransformWithAncestors()) && 6543 !aBuilder->GetPreserves3DAllowAsyncAnimation()) { 6544 return result; 6545 } 6546 6547 // Elements whose transform has been modified recently, or which 6548 // have a compositor-animated transform, can be prerendered. An element 6549 // might have only just had its transform animated in which case 6550 // the ActiveLayerManager may not have been notified yet. 6551 static constexpr nsCSSPropertyIDSet transformSet = 6552 nsCSSPropertyIDSet::TransformLikeProperties(); 6553 if (!ActiveLayerTracker::IsTransformMaybeAnimated(aFrame) && 6554 !EffectCompositor::HasAnimationsForCompositor( 6555 aFrame, DisplayItemType::TYPE_TRANSFORM)) { 6556 EffectCompositor::SetPerformanceWarning( 6557 aFrame, transformSet, 6558 AnimationPerformanceWarning( 6559 AnimationPerformanceWarning::Type::TransformFrameInactive)); 6560 6561 // This case happens when we're sure that the frame is not animated and its 6562 // preserve-3d ancestors are not, either. So we don't need to pre-render. 6563 // However, this decision shouldn't affect the decisions for other frames in 6564 // the preserve-3d context. We need this flag to determine whether we should 6565 // block async animations on other frames in the current preserve-3d tree. 6566 result.mHasAnimations = false; 6567 return result; 6568 } 6569 6570 // We should not allow prerender if any ancestor container element has 6571 // mask/clip-path effects. 6572 // 6573 // With prerender and async transform animation, we do not need to restyle an 6574 // animated element to respect position changes, since that transform is done 6575 // by layer animation. As a result, the container element is not aware of 6576 // position change of that containing element and loses the chance to update 6577 // the content of mask/clip-path. 6578 // 6579 // Why do we need to update a mask? This is relative to how we generate a 6580 // mask layer in ContainerState::SetupMaskLayerForCSSMask. While creating a 6581 // mask layer, to reduce memory usage, we did not choose the size of the 6582 // masked element as mask size. Instead, we read the union of bounds of all 6583 // children display items by nsDisplayWrapList::GetBounds, which is smaller 6584 // than or equal to the masked element's boundary, and use it as the position 6585 // size of the mask layer. That union bounds is actually affected by the 6586 // geometry of the animated element. To keep the content of mask up to date, 6587 // forbidding of prerender is required. 6588 for (nsIFrame* container = 6589 nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame); 6590 container; 6591 container = nsLayoutUtils::GetCrossDocParentFrameInProcess(container)) { 6592 const nsStyleSVGReset* svgReset = container->StyleSVGReset(); 6593 if (svgReset->HasMask() || svgReset->HasClipPath()) { 6594 return result; 6595 } 6596 } 6597 6598 // If the incoming dirty rect already contains the entire overflow area, 6599 // we are already rendering the entire content. 6600 nsRect overflow = aFrame->InkOverflowRectRelativeToSelf(); 6601 // UntransformRect will not touch the output rect (`&untranformedDirtyRect`) 6602 // in cases of non-invertible transforms, so we set `untransformedRect` to 6603 // `aDirtyRect` as an initial value for such cases. 6604 nsRect untransformedDirtyRect = *aDirtyRect; 6605 UntransformRect(*aDirtyRect, overflow, aFrame, &untransformedDirtyRect); 6606 if (untransformedDirtyRect.Contains(overflow)) { 6607 *aDirtyRect = untransformedDirtyRect; 6608 result.mDecision = PrerenderDecision::Full; 6609 return result; 6610 } 6611 6612 float viewportRatio = 6613 StaticPrefs::layout_animation_prerender_viewport_ratio_limit(); 6614 uint32_t absoluteLimitX = 6615 StaticPrefs::layout_animation_prerender_absolute_limit_x(); 6616 uint32_t absoluteLimitY = 6617 StaticPrefs::layout_animation_prerender_absolute_limit_y(); 6618 nsSize refSize = aBuilder->RootReferenceFrame()->GetSize(); 6619 6620 float resolution = aFrame->PresShell()->GetCumulativeResolution(); 6621 if (resolution < 1.0f) { 6622 refSize.SizeTo( 6623 NSCoordSaturatingNonnegativeMultiply(refSize.width, 1.0f / resolution), 6624 NSCoordSaturatingNonnegativeMultiply(refSize.height, 6625 1.0f / resolution)); 6626 } 6627 6628 // Only prerender if the transformed frame's size is <= a multiple of the 6629 // reference frame size (~viewport), and less than an absolute limit. 6630 // Both the ratio and the absolute limit are configurable. 6631 nscoord maxLength = std::max(nscoord(refSize.width * viewportRatio), 6632 nscoord(refSize.height * viewportRatio)); 6633 nsSize relativeLimit(maxLength, maxLength); 6634 nsSize absoluteLimit( 6635 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX), 6636 aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY)); 6637 nsSize maxSize = Min(relativeLimit, absoluteLimit); 6638 6639 const auto transform = nsLayoutUtils::GetTransformToAncestor( 6640 RelativeTo{aFrame}, 6641 RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)}); 6642 const gfxRect transformedBounds = transform.TransformAndClipBounds( 6643 gfxRect(overflow.x, overflow.y, overflow.width, overflow.height), 6644 gfxRect::MaxIntRect()); 6645 const nsSize frameSize = 6646 nsSize(transformedBounds.width, transformedBounds.height); 6647 6648 uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height; 6649 uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height; 6650 if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) { 6651 *aDirtyRect = overflow; 6652 result.mDecision = PrerenderDecision::Full; 6653 return result; 6654 } 6655 6656 if (ShouldUsePartialPrerender(aFrame)) { 6657 *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea( 6658 aFrame, untransformedDirtyRect, overflow, maxSize); 6659 result.mDecision = PrerenderDecision::Partial; 6660 return result; 6661 } 6662 6663 if (frameArea > maxLimitArea) { 6664 uint64_t appUnitsPerPixel = AppUnitsPerCSSPixel(); 6665 EffectCompositor::SetPerformanceWarning( 6666 aFrame, transformSet, 6667 AnimationPerformanceWarning( 6668 AnimationPerformanceWarning::Type::ContentTooLargeArea, 6669 { 6670 int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)), 6671 int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)), 6672 })); 6673 } else { 6674 EffectCompositor::SetPerformanceWarning( 6675 aFrame, transformSet, 6676 AnimationPerformanceWarning( 6677 AnimationPerformanceWarning::Type::ContentTooLarge, 6678 { 6679 nsPresContext::AppUnitsToIntCSSPixels(frameSize.width), 6680 nsPresContext::AppUnitsToIntCSSPixels(frameSize.height), 6681 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width), 6682 nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height), 6683 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width), 6684 nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height), 6685 })); 6686 } 6687 6688 return result; 6689 } 6690 6691 /* If the matrix is singular, or a hidden backface is shown, the frame won't be 6692 * visible or hit. */ 6693 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix) { 6694 if (aMatrix.IsSingular()) { 6695 return false; 6696 } 6697 if (aFrame->BackfaceIsHidden() && aMatrix.IsBackfaceVisible()) { 6698 return false; 6699 } 6700 return true; 6701 } 6702 6703 const Matrix4x4Flagged& nsDisplayTransform::GetTransform() const { 6704 if (mTransform) { 6705 return *mTransform; 6706 } 6707 6708 float scale = mFrame->PresContext()->AppUnitsPerDevPixel(); 6709 6710 if (mHasTransformGetter) { 6711 mTransform.emplace((mFrame->GetTransformGetter())(mFrame, scale)); 6712 Point3D newOrigin = 6713 Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale), 6714 NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), 0.0f); 6715 mTransform->ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z); 6716 } else if (!mIsTransformSeparator) { 6717 DebugOnly<bool> isReference = mFrame->IsTransformed() || 6718 mFrame->Combines3DTransformWithAncestors() || 6719 mFrame->Extend3DContext(); 6720 MOZ_ASSERT(isReference); 6721 mTransform.emplace( 6722 GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 6723 INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN)); 6724 } else { 6725 // Use identity matrix 6726 mTransform.emplace(); 6727 } 6728 6729 return *mTransform; 6730 } 6731 6732 const Matrix4x4Flagged& nsDisplayTransform::GetInverseTransform() const { 6733 if (mInverseTransform) { 6734 return *mInverseTransform; 6735 } 6736 6737 MOZ_ASSERT(!GetTransform().IsSingular()); 6738 6739 mInverseTransform.emplace(GetTransform().Inverse()); 6740 6741 return *mInverseTransform; 6742 } 6743 6744 Matrix4x4 nsDisplayTransform::GetTransformForRendering( 6745 LayoutDevicePoint* aOutOrigin) const { 6746 if (!mFrame->HasPerspective() || mHasTransformGetter || 6747 mIsTransformSeparator) { 6748 if (!mHasTransformGetter && !mIsTransformSeparator && aOutOrigin) { 6749 // If aOutOrigin is provided, put the offset to origin into it, because 6750 // we need to keep it separate for webrender. The combination of 6751 // *aOutOrigin and the returned matrix here should always be equivalent 6752 // to what GetTransform() would have returned. 6753 float scale = mFrame->PresContext()->AppUnitsPerDevPixel(); 6754 *aOutOrigin = LayoutDevicePoint::FromAppUnits(ToReferenceFrame(), scale); 6755 6756 // The rounding behavior should also be the same as GetTransform(). 6757 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) { 6758 aOutOrigin->Round(); 6759 } 6760 return GetResultingTransformMatrix(mFrame, nsPoint(0, 0), scale, 6761 INCLUDE_PERSPECTIVE); 6762 } 6763 return GetTransform().GetMatrix(); 6764 } 6765 MOZ_ASSERT(!mHasTransformGetter); 6766 6767 float scale = mFrame->PresContext()->AppUnitsPerDevPixel(); 6768 // Don't include perspective transform, or the offset to origin, since 6769 // nsDisplayPerspective will handle both of those. 6770 return GetResultingTransformMatrix(mFrame, nsPoint(), scale, 0); 6771 } 6772 6773 const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform( 6774 nsDisplayListBuilder* aBuilder) { 6775 MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext()); 6776 6777 if (!IsLeafOf3DContext()) { 6778 return GetTransform().GetMatrix(); 6779 } 6780 6781 if (!mTransformPreserves3D) { 6782 const nsIFrame* establisher; // Establisher of the 3D rendering context. 6783 for (establisher = mFrame; 6784 establisher && establisher->Combines3DTransformWithAncestors(); 6785 establisher = 6786 establisher->GetClosestFlattenedTreeAncestorPrimaryFrame()) { 6787 } 6788 const nsIFrame* establisherReference = aBuilder->FindReferenceFrameFor( 6789 nsLayoutUtils::GetCrossDocParentFrameInProcess(establisher)); 6790 6791 nsPoint offset = establisher->GetOffsetToCrossDoc(establisherReference); 6792 float scale = mFrame->PresContext()->AppUnitsPerDevPixel(); 6793 uint32_t flags = 6794 INCLUDE_PRESERVE3D_ANCESTORS | INCLUDE_PERSPECTIVE | OFFSET_BY_ORIGIN; 6795 mTransformPreserves3D = MakeUnique<Matrix4x4>( 6796 GetResultingTransformMatrix(mFrame, offset, scale, flags)); 6797 } 6798 6799 return *mTransformPreserves3D; 6800 } 6801 6802 bool nsDisplayTransform::CreateWebRenderCommands( 6803 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 6804 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 6805 nsDisplayListBuilder* aDisplayListBuilder) { 6806 // We want to make sure we don't pollute the transform property in the WR 6807 // stacking context by including the position of this frame (relative to the 6808 // parent reference frame). We need to keep those separate; the position of 6809 // this frame goes into the stacking context bounds while the transform goes 6810 // into the transform. 6811 LayoutDevicePoint position; 6812 Matrix4x4 newTransformMatrix = GetTransformForRendering(&position); 6813 6814 gfx::Matrix4x4* transformForSC = &newTransformMatrix; 6815 if (newTransformMatrix.IsIdentity()) { 6816 // If the transform is an identity transform, strip it out so that WR 6817 // doesn't turn this stacking context into a reference frame, as it 6818 // affects positioning. 6819 transformForSC = nullptr; 6820 6821 // In ChooseScaleAndSetTransform, we round the offset from the reference 6822 // frame used to adjust the transform, if there is no transform, or it 6823 // is just a translation. We need to do the same here. 6824 if (nsLayoutUtils::ShouldSnapToGrid(mFrame)) { 6825 position.Round(); 6826 } 6827 } 6828 6829 auto key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(), 6830 wr::SpatialKeyKind::Transform); 6831 6832 // We don't send animations for transform separator display items. 6833 uint64_t animationsId = 6834 mIsTransformSeparator 6835 ? 0 6836 : AddAnimationsForWebRender( 6837 this, aManager, aDisplayListBuilder, 6838 IsPartialPrerender() ? Some(position) : Nothing()); 6839 wr::WrAnimationProperty prop{wr::WrAnimationType::Transform, animationsId, 6840 key}; 6841 6842 nsDisplayTransform* deferredTransformItem = nullptr; 6843 if (ShouldDeferTransform()) { 6844 // If it has perspective, we create a new scroll data via the 6845 // UpdateScrollData call because that scenario is more complex. Otherwise, 6846 // if we don't contain any ASRs then just stash the transform on the 6847 // StackingContextHelper and apply it to any scroll data that are created 6848 // inside this nsDisplayTransform. 6849 deferredTransformItem = this; 6850 } 6851 6852 // Determine if we're possibly animated (= would need an active layer in FLB). 6853 bool animated = !mIsTransformSeparator && 6854 ActiveLayerTracker::IsTransformMaybeAnimated(Frame()); 6855 6856 wr::StackingContextParams params; 6857 params.mBoundTransform = &newTransformMatrix; 6858 params.animation = animationsId ? &prop : nullptr; 6859 6860 if (mWrapsBackdropFilter) { 6861 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER; 6862 } 6863 if (mForceIsolation) { 6864 params.flags |= wr::StackingContextFlags::FORCED_ISOLATION; 6865 } 6866 6867 wr::WrTransformInfo transform_info; 6868 if (transformForSC) { 6869 transform_info.transform = wr::ToLayoutTransform(newTransformMatrix); 6870 transform_info.key = key; 6871 params.mTransformPtr = &transform_info; 6872 } else { 6873 params.mTransformPtr = nullptr; 6874 } 6875 6876 params.prim_flags = !BackfaceIsHidden() 6877 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE 6878 : wr::PrimitiveFlags{0}; 6879 params.paired_with_perspective = mHasAssociatedPerspective; 6880 params.mDeferredTransformItem = deferredTransformItem; 6881 params.mAnimated = animated; 6882 // Determine if we would have to rasterize any items in local raster space 6883 // (i.e. disable subpixel AA). We don't always need to rasterize locally even 6884 // if the stacking context is possibly animated (at the cost of potentially 6885 // some false negatives with respect to will-change handling), so we pass in 6886 // this determination separately to accurately match with when FLB would 6887 // normally disable subpixel AA. 6888 params.mRasterizeLocally = animated && Frame()->HasAnimationOfTransform(); 6889 params.SetPreserve3D(mFrame->Extend3DContext() && !mIsTransformSeparator); 6890 params.clip = 6891 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId()); 6892 6893 LayoutDeviceSize boundsSize = LayoutDeviceSize::FromAppUnits( 6894 mChildBounds.Size(), mFrame->PresContext()->AppUnitsPerDevPixel()); 6895 6896 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, 6897 params, LayoutDeviceRect(position, boundsSize)); 6898 6899 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList( 6900 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources); 6901 return true; 6902 } 6903 6904 bool nsDisplayTransform::UpdateScrollData( 6905 WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) { 6906 if (ShouldDeferTransform()) { 6907 // This case is handled in CreateWebRenderCommands by stashing the transform 6908 // on the stacking context. 6909 return false; 6910 } 6911 if (aLayerData) { 6912 aLayerData->SetTransform(GetTransform().GetMatrix()); 6913 aLayerData->SetTransformIsPerspective(mFrame->ChildrenHavePerspective()); 6914 } 6915 return true; 6916 } 6917 6918 bool nsDisplayTransform::ShouldSkipTransform( 6919 nsDisplayListBuilder* aBuilder) const { 6920 return (aBuilder->RootReferenceFrame() == mFrame) && 6921 aBuilder->IsForGenerateGlyphMask(); 6922 } 6923 6924 void nsDisplayTransform::Collect3DTransformLeaves( 6925 nsDisplayListBuilder* aBuilder, nsTArray<nsDisplayTransform*>& aLeaves) { 6926 if (!IsParticipating3DContext() || IsLeafOf3DContext()) { 6927 aLeaves.AppendElement(this); 6928 return; 6929 } 6930 6931 FlattenedDisplayListIterator iter(aBuilder, &mChildren); 6932 while (iter.HasNext()) { 6933 nsDisplayItem* item = iter.GetNextItem(); 6934 if (item->GetType() == DisplayItemType::TYPE_PERSPECTIVE) { 6935 auto* perspective = static_cast<nsDisplayPerspective*>(item); 6936 if (!perspective->GetChildren()->GetTop()) { 6937 continue; 6938 } 6939 item = perspective->GetChildren()->GetTop(); 6940 } 6941 if (item->GetType() != DisplayItemType::TYPE_TRANSFORM) { 6942 gfxCriticalError() << "Invalid child item within 3D transform of type: " 6943 << item->Name(); 6944 continue; 6945 } 6946 static_cast<nsDisplayTransform*>(item)->Collect3DTransformLeaves(aBuilder, 6947 aLeaves); 6948 } 6949 } 6950 6951 static RefPtr<gfx::Path> BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT, 6952 const gfx::Polygon& aPolygon) { 6953 MOZ_ASSERT(!aPolygon.IsEmpty()); 6954 6955 RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder(); 6956 const nsTArray<Point4D>& points = aPolygon.GetPoints(); 6957 6958 pathBuilder->MoveTo(points[0].As2DPoint()); 6959 6960 for (size_t i = 1; i < points.Length(); ++i) { 6961 pathBuilder->LineTo(points[i].As2DPoint()); 6962 } 6963 6964 pathBuilder->Close(); 6965 return pathBuilder->Finish(); 6966 } 6967 6968 void nsDisplayTransform::CollectSorted3DTransformLeaves( 6969 nsDisplayListBuilder* aBuilder, nsTArray<TransformPolygon>& aLeaves) { 6970 std::list<TransformPolygon> inputLayers; 6971 6972 nsTArray<nsDisplayTransform*> leaves; 6973 Collect3DTransformLeaves(aBuilder, leaves); 6974 for (nsDisplayTransform* item : leaves) { 6975 auto bounds = LayoutDeviceRect::FromAppUnits( 6976 item->mChildBounds, item->mFrame->PresContext()->AppUnitsPerDevPixel()); 6977 Matrix4x4 transform = item->GetAccumulatedPreserved3DTransform(aBuilder); 6978 6979 if (!IsFrameVisible(item->mFrame, transform)) { 6980 continue; 6981 } 6982 gfx::Polygon polygon = 6983 gfx::Polygon::FromRect(gfx::Rect(bounds.ToUnknownRect())); 6984 6985 polygon.TransformToScreenSpace(transform); 6986 6987 if (polygon.GetPoints().Length() >= 3) { 6988 inputLayers.push_back(TransformPolygon(item, std::move(polygon))); 6989 } 6990 } 6991 6992 if (inputLayers.empty()) { 6993 return; 6994 } 6995 6996 BSPTree<nsDisplayTransform> tree(inputLayers); 6997 nsTArray<TransformPolygon> orderedLayers(tree.GetDrawOrder()); 6998 6999 for (TransformPolygon& polygon : orderedLayers) { 7000 Matrix4x4 inverse = 7001 polygon.data->GetAccumulatedPreserved3DTransform(aBuilder).Inverse(); 7002 7003 MOZ_ASSERT(polygon.geometry); 7004 polygon.geometry->TransformToLayerSpace(inverse); 7005 } 7006 7007 aLeaves = std::move(orderedLayers); 7008 } 7009 7010 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder, 7011 gfxContext* aCtx) { 7012 Paint(aBuilder, aCtx, Nothing()); 7013 } 7014 7015 void nsDisplayTransform::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx, 7016 const Maybe<gfx::Polygon>& aPolygon) { 7017 if (IsParticipating3DContext() && !IsLeafOf3DContext()) { 7018 MOZ_ASSERT(!aPolygon); 7019 nsTArray<TransformPolygon> leaves; 7020 CollectSorted3DTransformLeaves(aBuilder, leaves); 7021 for (TransformPolygon& item : leaves) { 7022 item.data->Paint(aBuilder, aCtx, item.geometry); 7023 } 7024 return; 7025 } 7026 7027 gfxContextMatrixAutoSaveRestore saveMatrix(aCtx); 7028 Matrix4x4 trans = ShouldSkipTransform(aBuilder) 7029 ? Matrix4x4() 7030 : GetAccumulatedPreserved3DTransform(aBuilder); 7031 if (!IsFrameVisible(mFrame, trans)) { 7032 return; 7033 } 7034 7035 Matrix trans2d; 7036 if (trans.CanDraw2D(&trans2d)) { 7037 aCtx->Multiply(ThebesMatrix(trans2d)); 7038 7039 if (aPolygon) { 7040 RefPtr<gfx::Path> path = 7041 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon); 7042 aCtx->GetDrawTarget()->PushClip(path); 7043 } 7044 7045 GetChildren()->Paint(aBuilder, aCtx, 7046 mFrame->PresContext()->AppUnitsPerDevPixel()); 7047 7048 if (aPolygon) { 7049 aCtx->GetDrawTarget()->PopClip(); 7050 } 7051 return; 7052 } 7053 7054 // TODO: Implement 3d transform handling, including plane splitting and 7055 // sorting. See BasicCompositor. 7056 auto pixelBounds = LayoutDeviceRect::FromAppUnitsToOutside( 7057 mChildBounds, mFrame->PresContext()->AppUnitsPerDevPixel()); 7058 RefPtr<DrawTarget> untransformedDT = 7059 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( 7060 IntSize(pixelBounds.Width(), pixelBounds.Height()), 7061 SurfaceFormat::B8G8R8A8, true); 7062 if (!untransformedDT || !untransformedDT->IsValid()) { 7063 return; 7064 } 7065 untransformedDT->SetTransform( 7066 Matrix::Translation(-Point(pixelBounds.X(), pixelBounds.Y()))); 7067 7068 gfxContext groupTarget(untransformedDT, /* aPreserveTransform */ true); 7069 7070 if (aPolygon) { 7071 RefPtr<gfx::Path> path = 7072 BuildPathFromPolygon(aCtx->GetDrawTarget(), *aPolygon); 7073 aCtx->GetDrawTarget()->PushClip(path); 7074 } 7075 7076 GetChildren()->Paint(aBuilder, &groupTarget, 7077 mFrame->PresContext()->AppUnitsPerDevPixel()); 7078 7079 if (aPolygon) { 7080 aCtx->GetDrawTarget()->PopClip(); 7081 } 7082 7083 RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot(); 7084 7085 trans.PreTranslate(pixelBounds.X(), pixelBounds.Y(), 0); 7086 aCtx->GetDrawTarget()->Draw3DTransformedSurface(untransformedSurf, trans); 7087 } 7088 7089 bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const { 7090 // If EffectCompositor::HasAnimationsForCompositor() is true then we can 7091 // completely bypass the main thread for this animation, so it is always 7092 // worthwhile. 7093 // For ActiveLayerTracker::IsTransformAnimated() cases the main thread is 7094 // already involved so there is less to be gained. 7095 // Therefore we check that the *post-transform* bounds of this item are 7096 // big enough to justify an active layer. 7097 return EffectCompositor::HasAnimationsForCompositor( 7098 mFrame, DisplayItemType::TYPE_TRANSFORM) || 7099 (ActiveLayerTracker::IsTransformAnimated(aBuilder, mFrame)); 7100 } 7101 7102 nsRect nsDisplayTransform::TransformUntransformedBounds( 7103 nsDisplayListBuilder* aBuilder, const Matrix4x4Flagged& aMatrix) const { 7104 const nsRect untransformedBounds = GetUntransformedBounds(aBuilder); 7105 // GetTransform always operates in dev pixels. 7106 const float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); 7107 return nsLayoutUtils::MatrixTransformRect(untransformedBounds, aMatrix, 7108 factor); 7109 } 7110 7111 /** 7112 * Returns the bounds for this transform. The bounds are calculated during 7113 * display list building and merging, see |nsDisplayTransform::UpdateBounds()|. 7114 */ 7115 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder, 7116 bool* aSnap) const { 7117 *aSnap = false; 7118 return mBounds; 7119 } 7120 7121 void nsDisplayTransform::ComputeBounds(nsDisplayListBuilder* aBuilder) { 7122 MOZ_ASSERT(mFrame->Extend3DContext() || IsLeafOf3DContext()); 7123 7124 /* Some transforms can get empty bounds in 2D, but might get transformed again 7125 * and get non-empty bounds. A simple example of this would be a 180 degree 7126 * rotation getting applied twice. 7127 * We should not depend on transforming bounds level by level. 7128 * 7129 * This function collects the bounds of this transform and stores it in 7130 * nsDisplayListBuilder. If this is not a leaf of a 3D context, we recurse 7131 * down and include the bounds of the child transforms. 7132 * The bounds are transformed with the accumulated transformation matrix up to 7133 * the 3D context root coordinate space. 7134 */ 7135 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder); 7136 accTransform.Accumulate(GetTransform().GetMatrix()); 7137 7138 // Do not dive into another 3D context. 7139 if (!IsLeafOf3DContext()) { 7140 for (nsDisplayItem* i : *GetChildren()) { 7141 i->DoUpdateBoundsPreserves3D(aBuilder); 7142 } 7143 } 7144 7145 /* The child transforms that extend 3D context further will have empty bounds, 7146 * so the untransformed bounds here is the bounds of all the non-preserve-3d 7147 * content under this transform. 7148 */ 7149 const nsRect rect = TransformUntransformedBounds( 7150 aBuilder, accTransform.GetCurrentTransform()); 7151 aBuilder->AccumulateRect(rect); 7152 } 7153 7154 void nsDisplayTransform::DoUpdateBoundsPreserves3D( 7155 nsDisplayListBuilder* aBuilder) { 7156 MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() || 7157 IsTransformSeparator()); 7158 // Updating is not going through to child 3D context. 7159 ComputeBounds(aBuilder); 7160 } 7161 7162 void nsDisplayTransform::UpdateBounds(nsDisplayListBuilder* aBuilder) { 7163 UpdateUntransformedBounds(aBuilder); 7164 7165 if (IsTransformSeparator()) { 7166 MOZ_ASSERT(GetTransform().IsIdentity()); 7167 mBounds = mChildBounds; 7168 return; 7169 } 7170 7171 if (mFrame->Extend3DContext()) { 7172 if (!Combines3DTransformWithAncestors()) { 7173 // The transform establishes a 3D context. |UpdateBoundsFor3D()| will 7174 // collect the bounds from the child transforms. 7175 UpdateBoundsFor3D(aBuilder); 7176 } else { 7177 // With nested 3D transforms, the 2D bounds might not be useful. 7178 mBounds = nsRect(); 7179 } 7180 7181 return; 7182 } 7183 7184 MOZ_ASSERT(!mFrame->Extend3DContext()); 7185 7186 // We would like to avoid calculating 2D bounds here for nested 3D transforms, 7187 // but mix-blend-mode relies on having bounds set. See bug 1556956. 7188 7189 // A stand-alone transform. 7190 mBounds = TransformUntransformedBounds(aBuilder, GetTransform()); 7191 } 7192 7193 void nsDisplayTransform::UpdateBoundsFor3D(nsDisplayListBuilder* aBuilder) { 7194 MOZ_ASSERT(mFrame->Extend3DContext() && 7195 !mFrame->Combines3DTransformWithAncestors() && 7196 !IsTransformSeparator()); 7197 7198 // Always start updating from an establisher of a 3D rendering context. 7199 nsDisplayListBuilder::AutoAccumulateRect accRect(aBuilder); 7200 nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder); 7201 accTransform.StartRoot(); 7202 ComputeBounds(aBuilder); 7203 mBounds = aBuilder->GetAccumulatedRect(); 7204 } 7205 7206 void nsDisplayTransform::UpdateUntransformedBounds( 7207 nsDisplayListBuilder* aBuilder) { 7208 mChildBounds = GetChildren()->GetClippedBoundsWithRespectToASR( 7209 aBuilder, mActiveScrolledRoot); 7210 } 7211 7212 #ifdef DEBUG_HIT 7213 # include <time.h> 7214 #endif 7215 7216 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */ 7217 void nsDisplayTransform::HitTest(nsDisplayListBuilder* aBuilder, 7218 const nsRect& aRect, HitTestState* aState, 7219 nsTArray<nsIFrame*>* aOutFrames) { 7220 if (aState->mGatheringPreserves3DLeaves) { 7221 GetChildren()->HitTest(aBuilder, aRect, aState, aOutFrames); 7222 return; 7223 } 7224 7225 /* Here's how this works: 7226 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit 7227 * anything). 7228 * 2. Invert the matrix. 7229 * 3. Use it to transform the rect into the correct space. 7230 * 4. Pass that rect down through to the list's version of HitTest. 7231 */ 7232 // GetTransform always operates in dev pixels. 7233 float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); 7234 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder); 7235 7236 if (!IsFrameVisible(mFrame, matrix)) { 7237 return; 7238 } 7239 7240 const bool oldHitOccludingItem = aState->mHitOccludingItem; 7241 7242 /* We want to go from transformed-space to regular space. 7243 * Thus we have to invert the matrix, which normally does 7244 * the reverse operation (e.g. regular->transformed) 7245 */ 7246 7247 /* Now, apply the transform and pass it down the channel. */ 7248 matrix.Invert(); 7249 nsRect resultingRect; 7250 // Magic width/height indicating we're hit testing a point, not a rect 7251 const bool testingPoint = aRect.width == 1 && aRect.height == 1; 7252 if (testingPoint) { 7253 Point4D point = 7254 matrix.ProjectPoint(Point(NSAppUnitsToFloatPixels(aRect.x, factor), 7255 NSAppUnitsToFloatPixels(aRect.y, factor))); 7256 if (!point.HasPositiveWCoord()) { 7257 return; 7258 } 7259 7260 Point point2d = point.As2DPoint(); 7261 7262 resultingRect = 7263 nsRect(NSFloatPixelsToAppUnits(float(point2d.x), factor), 7264 NSFloatPixelsToAppUnits(float(point2d.y), factor), 1, 1); 7265 7266 } else { 7267 Rect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor), 7268 NSAppUnitsToFloatPixels(aRect.y, factor), 7269 NSAppUnitsToFloatPixels(aRect.width, factor), 7270 NSAppUnitsToFloatPixels(aRect.height, factor)); 7271 7272 Rect childGfxBounds(NSAppUnitsToFloatPixels(mChildBounds.x, factor), 7273 NSAppUnitsToFloatPixels(mChildBounds.y, factor), 7274 NSAppUnitsToFloatPixels(mChildBounds.width, factor), 7275 NSAppUnitsToFloatPixels(mChildBounds.height, factor)); 7276 7277 Rect rect = matrix.ProjectRectBounds(originalRect, childGfxBounds); 7278 7279 resultingRect = 7280 nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor), 7281 NSFloatPixelsToAppUnits(float(rect.Y()), factor), 7282 NSFloatPixelsToAppUnits(float(rect.Width()), factor), 7283 NSFloatPixelsToAppUnits(float(rect.Height()), factor)); 7284 } 7285 7286 if (resultingRect.IsEmpty()) { 7287 return; 7288 } 7289 7290 #ifdef DEBUG_HIT 7291 printf("Frame: %p\n", dynamic_cast<void*>(mFrame)); 7292 printf(" Untransformed point: (%f, %f)\n", resultingRect.X(), 7293 resultingRect.Y()); 7294 uint32_t originalFrameCount = aOutFrames.Length(); 7295 #endif 7296 7297 const bool savedTransformHasBackfaceVisible = 7298 aState->mTransformHasBackfaceVisible; 7299 if (IsLeafOf3DContext()) { 7300 aState->mTransformHasBackfaceVisible = matrix.IsBackfaceVisible(); 7301 } 7302 GetChildren()->HitTest(aBuilder, resultingRect, aState, aOutFrames); 7303 if (IsLeafOf3DContext()) { 7304 aState->mTransformHasBackfaceVisible = savedTransformHasBackfaceVisible; 7305 } 7306 7307 if (aState->mHitOccludingItem && !testingPoint && !mBounds.Contains(aRect)) { 7308 MOZ_ASSERT(aBuilder->HitTestIsForVisibility()); 7309 // We're hit-testing a rect that's bigger than our child bounds, but 7310 // resultingRect is clipped by our bounds (in ProjectRectBounds above), so 7311 // we can't stop hit-testing altogether. 7312 // 7313 // FIXME(emilio): I think this means that theoretically we might include 7314 // some frames fully behind other transformed-but-opaque frames? Then again 7315 // that's our pre-existing behavior for other untransformed content that 7316 // doesn't fill the whole rect. To be fully correct I think we'd need proper 7317 // "known occluded region" tracking, but that might be overkill for our 7318 // purposes here. 7319 aState->mHitOccludingItem = oldHitOccludingItem; 7320 } 7321 7322 #ifdef DEBUG_HIT 7323 if (originalFrameCount != aOutFrames.Length()) 7324 printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()), 7325 dynamic_cast<void*>(aOutFrames.ElementAt(0))); 7326 printf("=== end of hit test ===\n"); 7327 #endif 7328 } 7329 7330 float nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, 7331 const nsPoint& aPoint) { 7332 // GetTransform always operates in dev pixels. 7333 float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); 7334 Matrix4x4 matrix = GetAccumulatedPreserved3DTransform(aBuilder); 7335 7336 NS_ASSERTION(IsFrameVisible(mFrame, matrix), 7337 "We can't have hit a frame that isn't visible!"); 7338 7339 Matrix4x4 inverse = matrix; 7340 inverse.Invert(); 7341 7342 // Compute the value z so that (aPoint.x, aPoint.y, z, 1) gets transformed by 7343 // inverse to the z=0 plane. This is the same thing that 7344 // Matrix4x4Typed::ProjectPoint does, but we are only interested in the z 7345 // value, not the projected point, thus we extract the formula here, look 7346 // there for how this equation is determined. 7347 // https://searchfox.org/mozilla-central/rev/bd4d1cd1ca3037e6dc8d4081a4303824880b1b45/gfx/2d/Matrix.h#724 7348 return -(NSAppUnitsToFloatPixels(aPoint.x, factor) * inverse._13 + 7349 NSAppUnitsToFloatPixels(aPoint.y, factor) * inverse._23 + 7350 inverse._43) / 7351 inverse._33; 7352 } 7353 7354 /* The transform is opaque iff the transform consists solely of scales and 7355 * translations and if the underlying content is opaque. Thus if the transform 7356 * is of the form 7357 * 7358 * |a c e| 7359 * |b d f| 7360 * |0 0 1| 7361 * 7362 * We need b and c to be zero. 7363 * 7364 * We also need to check whether the underlying opaque content completely fills 7365 * our visible rect. We use UntransformRect which expands to the axis-aligned 7366 * bounding rect, but that's OK since if 7367 * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it 7368 * certainly contains the actual (non-axis-aligned) untransformed rect. 7369 */ 7370 nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, 7371 bool* aSnap) const { 7372 *aSnap = false; 7373 7374 nsRect untransformedVisible; 7375 if (!UntransformBuildingRect(aBuilder, &untransformedVisible)) { 7376 return nsRegion(); 7377 } 7378 7379 const Matrix4x4Flagged& matrix = GetTransform(); 7380 Matrix matrix2d; 7381 if (!matrix.Is2D(&matrix2d) || !matrix2d.PreservesAxisAlignedRectangles()) { 7382 return nsRegion(); 7383 } 7384 7385 nsRegion result; 7386 7387 const nsRect bounds = GetUntransformedBounds(aBuilder); 7388 const nsRegion opaque = 7389 ::mozilla::GetOpaqueRegion(aBuilder, GetChildren(), bounds); 7390 7391 if (opaque.Contains(untransformedVisible)) { 7392 bool tmpSnap; 7393 result = GetBuildingRect().Intersect(GetBounds(aBuilder, &tmpSnap)); 7394 } 7395 return result; 7396 } 7397 7398 nsRect nsDisplayTransform::GetComponentAlphaBounds( 7399 nsDisplayListBuilder* aBuilder) const { 7400 if (GetChildren()->GetComponentAlphaBounds(aBuilder).IsEmpty()) { 7401 return nsRect(); 7402 } 7403 7404 bool snap; 7405 return GetBounds(aBuilder, &snap); 7406 } 7407 7408 /* TransformRect takes in as parameters a rectangle (in app space) and returns 7409 * the smallest rectangle (in app space) containing the transformed image of 7410 * that rectangle. That is, it takes the four corners of the rectangle, 7411 * transforms them according to the matrix associated with the specified frame, 7412 * then returns the smallest rectangle containing the four transformed points. 7413 * 7414 * @param aUntransformedBounds The rectangle (in app units) to transform. 7415 * @param aFrame The frame whose transformation should be applied. 7416 * @param aOrigin The delta from the frame origin to the coordinate space origin 7417 * @return The smallest rectangle containing the image of the transformed 7418 * rectangle. 7419 */ 7420 nsRect nsDisplayTransform::TransformRect(const nsRect& aUntransformedBounds, 7421 const nsIFrame* aFrame, 7422 TransformReferenceBox& aRefBox) { 7423 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!"); 7424 7425 float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); 7426 7427 FrameTransformProperties props(aFrame, aRefBox, factor); 7428 return nsLayoutUtils::MatrixTransformRect( 7429 aUntransformedBounds, 7430 GetResultingTransformMatrixInternal( 7431 props, aRefBox, nsPoint(), factor, 7432 kTransformRectFlags & ~OFFSET_BY_ORIGIN), 7433 factor); 7434 } 7435 7436 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds, 7437 const nsRect& aChildBounds, 7438 const nsIFrame* aFrame, 7439 nsRect* aOutRect) { 7440 MOZ_ASSERT(aFrame, "Can't take the transform based on a null frame!"); 7441 7442 float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); 7443 Matrix4x4 transform = GetResultingTransformMatrix( 7444 aFrame, nsPoint(), factor, kTransformRectFlags & ~OFFSET_BY_ORIGIN); 7445 return UntransformRect(aTransformedBounds, aChildBounds, transform, factor, 7446 aOutRect); 7447 } 7448 7449 bool nsDisplayTransform::UntransformRect(const nsRect& aTransformedBounds, 7450 const nsRect& aChildBounds, 7451 const Matrix4x4& aMatrix, 7452 float aAppUnitsPerPixel, 7453 nsRect* aOutRect) { 7454 Maybe<Matrix4x4> inverse = aMatrix.MaybeInverse(); 7455 if (inverse.isNothing()) { 7456 return false; 7457 } 7458 7459 RectDouble result( 7460 NSAppUnitsToFloatPixels(aTransformedBounds.x, aAppUnitsPerPixel), 7461 NSAppUnitsToFloatPixels(aTransformedBounds.y, aAppUnitsPerPixel), 7462 NSAppUnitsToFloatPixels(aTransformedBounds.width, aAppUnitsPerPixel), 7463 NSAppUnitsToFloatPixels(aTransformedBounds.height, aAppUnitsPerPixel)); 7464 7465 RectDouble childGfxBounds( 7466 NSAppUnitsToFloatPixels(aChildBounds.x, aAppUnitsPerPixel), 7467 NSAppUnitsToFloatPixels(aChildBounds.y, aAppUnitsPerPixel), 7468 NSAppUnitsToFloatPixels(aChildBounds.width, aAppUnitsPerPixel), 7469 NSAppUnitsToFloatPixels(aChildBounds.height, aAppUnitsPerPixel)); 7470 7471 result = inverse->ProjectRectBounds(result, childGfxBounds); 7472 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, aAppUnitsPerPixel); 7473 return true; 7474 } 7475 7476 bool nsDisplayTransform::UntransformRect(nsDisplayListBuilder* aBuilder, 7477 const nsRect& aRect, 7478 nsRect* aOutRect) const { 7479 if (GetTransform().IsSingular()) { 7480 return false; 7481 } 7482 7483 // GetTransform always operates in dev pixels. 7484 float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); 7485 RectDouble result(NSAppUnitsToFloatPixels(aRect.x, factor), 7486 NSAppUnitsToFloatPixels(aRect.y, factor), 7487 NSAppUnitsToFloatPixels(aRect.width, factor), 7488 NSAppUnitsToFloatPixels(aRect.height, factor)); 7489 7490 nsRect childBounds = GetUntransformedBounds(aBuilder); 7491 RectDouble childGfxBounds( 7492 NSAppUnitsToFloatPixels(childBounds.x, factor), 7493 NSAppUnitsToFloatPixels(childBounds.y, factor), 7494 NSAppUnitsToFloatPixels(childBounds.width, factor), 7495 NSAppUnitsToFloatPixels(childBounds.height, factor)); 7496 7497 /* We want to untransform the matrix, so invert the transformation first! */ 7498 result = GetInverseTransform().ProjectRectBounds(result, childGfxBounds); 7499 7500 *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor); 7501 7502 return true; 7503 } 7504 7505 void nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream) { 7506 aStream << GetTransform().GetMatrix(); 7507 if (IsTransformSeparator()) { 7508 aStream << " transform-separator"; 7509 } 7510 if (IsLeafOf3DContext()) { 7511 aStream << " 3d-context-leaf"; 7512 } 7513 if (mFrame->Extend3DContext()) { 7514 aStream << " extends-3d-context"; 7515 } 7516 if (mFrame->Combines3DTransformWithAncestors()) { 7517 aStream << " combines-3d-with-ancestors"; 7518 } 7519 7520 aStream << " prerender("; 7521 switch (mPrerenderDecision) { 7522 case PrerenderDecision::No: 7523 aStream << "no"; 7524 break; 7525 case PrerenderDecision::Partial: 7526 aStream << "partial"; 7527 break; 7528 case PrerenderDecision::Full: 7529 aStream << "full"; 7530 break; 7531 } 7532 aStream << ")"; 7533 aStream << " childrenBuildingRect" << mChildrenBuildingRect; 7534 } 7535 7536 nsDisplayPerspective::nsDisplayPerspective(nsDisplayListBuilder* aBuilder, 7537 nsIFrame* aFrame, 7538 nsDisplayList* aList) 7539 : nsPaintedDisplayItem(aBuilder, aFrame), mList(aBuilder) { 7540 mList.AppendToTop(aList); 7541 MOZ_ASSERT(mList.Length() == 1); 7542 MOZ_ASSERT(mList.GetTop()->GetType() == DisplayItemType::TYPE_TRANSFORM); 7543 } 7544 7545 void nsDisplayPerspective::Paint(nsDisplayListBuilder* aBuilder, 7546 gfxContext* aCtx) { 7547 // Just directly recurse into children, since we'll include the persepctive 7548 // value in any nsDisplayTransform children. 7549 GetChildren()->Paint(aBuilder, aCtx, 7550 mFrame->PresContext()->AppUnitsPerDevPixel()); 7551 } 7552 7553 nsRegion nsDisplayPerspective::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, 7554 bool* aSnap) const { 7555 if (!GetChildren()->GetTop()) { 7556 *aSnap = false; 7557 return nsRegion(); 7558 } 7559 7560 return GetChildren()->GetTop()->GetOpaqueRegion(aBuilder, aSnap); 7561 } 7562 7563 bool nsDisplayPerspective::CreateWebRenderCommands( 7564 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 7565 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 7566 nsDisplayListBuilder* aDisplayListBuilder) { 7567 float appUnitsPerPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); 7568 Matrix4x4 perspectiveMatrix; 7569 DebugOnly<bool> hasPerspective = nsDisplayTransform::ComputePerspectiveMatrix( 7570 mFrame, appUnitsPerPixel, perspectiveMatrix); 7571 MOZ_ASSERT(hasPerspective, "Why did we create nsDisplayPerspective?"); 7572 7573 /* 7574 * ClipListToRange can remove our child after we were created. 7575 */ 7576 if (!GetChildren()->GetTop()) { 7577 return false; 7578 } 7579 7580 /* 7581 * The resulting matrix is still in the coordinate space of the transformed 7582 * frame. Append a translation to the reference frame coordinates. 7583 */ 7584 nsDisplayTransform* transform = 7585 static_cast<nsDisplayTransform*>(GetChildren()->GetTop()); 7586 7587 Point3D newOrigin = 7588 Point3D(NSAppUnitsToFloatPixels(transform->ToReferenceFrame().x, 7589 appUnitsPerPixel), 7590 NSAppUnitsToFloatPixels(transform->ToReferenceFrame().y, 7591 appUnitsPerPixel), 7592 0.0f); 7593 Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0); 7594 7595 perspectiveMatrix.PostTranslate(roundedOrigin); 7596 7597 nsIFrame* perspectiveFrame = 7598 mFrame->GetClosestFlattenedTreeAncestorPrimaryFrame(); 7599 7600 // Passing true here is always correct, since perspective always combines 7601 // transforms with the descendants. However that'd make WR do a lot of work 7602 // that it doesn't really need to do if there aren't other transforms forming 7603 // part of the 3D context. 7604 // 7605 // WR knows how to treat perspective in that case, so the only thing we need 7606 // to do is to ensure we pass true when we're involved in a 3d context in any 7607 // other way via the transform-style property on either the transformed frame 7608 // or the perspective frame in order to not confuse WR's preserve-3d code in 7609 // very awful ways. 7610 bool preserve3D = 7611 mFrame->Extend3DContext() || perspectiveFrame->Extend3DContext(); 7612 7613 wr::StackingContextParams params; 7614 7615 wr::WrTransformInfo transform_info; 7616 transform_info.transform = wr::ToLayoutTransform(perspectiveMatrix); 7617 transform_info.key = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(), 7618 wr::SpatialKeyKind::Perspective); 7619 params.mTransformPtr = &transform_info; 7620 7621 params.reference_frame_kind = wr::WrReferenceFrameKind::Perspective; 7622 params.prim_flags = !BackfaceIsHidden() 7623 ? wr::PrimitiveFlags::IS_BACKFACE_VISIBLE 7624 : wr::PrimitiveFlags{0}; 7625 params.SetPreserve3D(preserve3D); 7626 params.clip = 7627 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId()); 7628 7629 Maybe<uint64_t> scrollingRelativeTo; 7630 for (const auto* asr = GetActiveScrolledRoot(); asr; asr = asr->mParent) { 7631 // In OOP documents, the root scrollable frame of the in-process root 7632 // document is always active, so using IsAncestorFrameCrossDocInProcess 7633 // should be fine here. 7634 if (ScrollContainerFrame* scrollFrame = asr->ScrollFrameOrNull()) { 7635 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess( 7636 scrollFrame->GetScrolledFrame(), perspectiveFrame)) { 7637 scrollingRelativeTo.emplace(asr->GetViewId()); 7638 break; 7639 } 7640 } 7641 } 7642 7643 // We put the perspective reference frame wrapping the transformed frame, 7644 // even though there may be arbitrarily nested scroll frames in between. 7645 // 7646 // We need to know how many ancestor scroll-frames are we nested in, in order 7647 // for the async scrolling code in WebRender to calculate the right 7648 // transformation for the perspective contents. 7649 params.scrolling_relative_to = scrollingRelativeTo.ptrOr(nullptr); 7650 7651 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, 7652 params); 7653 7654 aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList( 7655 GetChildren(), this, aDisplayListBuilder, sc, aBuilder, aResources); 7656 7657 return true; 7658 } 7659 7660 nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder, 7661 nsTextFrame* aFrame) 7662 : nsPaintedDisplayItem(aBuilder, aFrame), 7663 mVisIStartEdge(0), 7664 mVisIEndEdge(0) { 7665 MOZ_COUNT_CTOR(nsDisplayText); 7666 mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame(); 7667 // Bug 748228 7668 mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel()); 7669 mVisibleRect = aBuilder->GetVisibleRect() + 7670 aBuilder->GetCurrentFrameOffsetToReferenceFrame(); 7671 } 7672 7673 bool nsDisplayText::CanApplyOpacity(WebRenderLayerManager* aManager, 7674 nsDisplayListBuilder* aBuilder) const { 7675 auto* f = static_cast<nsTextFrame*>(mFrame); 7676 7677 if (f->IsSelected()) { 7678 return false; 7679 } 7680 7681 const nsStyleText* textStyle = f->StyleText(); 7682 if (textStyle->HasTextShadow()) { 7683 return false; 7684 } 7685 7686 nsTextFrame::TextDecorations decorations; 7687 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors, 7688 decorations); 7689 return !decorations.HasDecorationLines(); 7690 } 7691 7692 void nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) { 7693 AUTO_PROFILER_LABEL("nsDisplayText::Paint", GRAPHICS); 7694 // We don't pass mVisibleRect here, since this can be called from within 7695 // the WebRender fallback painting path, and we don't want to issue 7696 // recorded commands that are dependent on the visible/building rect. 7697 RenderToContext(aCtx, aBuilder, GetPaintRect(aBuilder, aCtx)); 7698 7699 auto* textFrame = static_cast<nsTextFrame*>(mFrame); 7700 LCPTextFrameHelper::MaybeUnionTextFrame(textFrame, 7701 mBounds - ToReferenceFrame()); 7702 } 7703 7704 bool nsDisplayText::CreateWebRenderCommands( 7705 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 7706 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 7707 nsDisplayListBuilder* aDisplayListBuilder) { 7708 auto* f = static_cast<nsTextFrame*>(mFrame); 7709 auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel(); 7710 7711 nsRect bounds = f->WebRenderBounds() + ToReferenceFrame(); 7712 // Bug 748228 7713 bounds.Inflate(appUnitsPerDevPixel); 7714 7715 if (bounds.IsEmpty()) { 7716 return true; 7717 } 7718 7719 // For large font sizes, punt to a blob image, to avoid the blurry rendering 7720 // that results from WR clamping the glyph size used for rasterization. 7721 // 7722 // (See FONT_SIZE_LIMIT in webrender/src/glyph_rasterizer/mod.rs.) 7723 // 7724 // This is not strictly accurate, as final used font sizes might not be the 7725 // same as claimed by the fontGroup's style.size (eg: due to font-size-adjust 7726 // altering the used size of the font actually used). 7727 // It also fails to consider how transforms might affect the device-font-size 7728 // that webrender uses (and clamps). 7729 // But it should be near enough for practical purposes; the limitations just 7730 // mean we might sometimes end up with webrender still applying some bitmap 7731 // scaling, or bail out when we didn't really need to. 7732 constexpr float kWebRenderFontSizeLimit = 320.0; 7733 f->EnsureTextRun(nsTextFrame::eInflated); 7734 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated); 7735 if (textRun && 7736 textRun->GetFontGroup()->GetStyle()->size > kWebRenderFontSizeLimit) { 7737 return false; 7738 } 7739 7740 gfx::Point deviceOffset = 7741 LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel) 7742 .ToUnknownPoint(); 7743 7744 // Clipping the bounds to the PaintRect (factoring in what's covered by parent 7745 // frames) lets us early reject a bunch of things. 7746 nsRect visible = mVisibleRect; 7747 7748 // Add the "source rect" area from which the given shadows could intersect 7749 // with mVisibleRect, and which therefore needs to included in the paint 7750 // operation, to the `visible` rect that we will use to limit the bounds of 7751 // what we send to the renderer. 7752 auto addShadowSourceToVisible = [&](Span<const StyleSimpleShadow> aShadows) { 7753 for (const auto& shadow : aShadows) { 7754 nsRect sourceRect = mVisibleRect; 7755 // Negate the offsets, because we're looking for the "source" rect that 7756 // could cast a shadow into the visible rect, rather than a "target" area 7757 // onto which the visible rect would cast a shadow. 7758 sourceRect.MoveBy(-shadow.horizontal.ToAppUnits(), 7759 -shadow.vertical.ToAppUnits()); 7760 // Inflate to account for the shadow blur. 7761 sourceRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin( 7762 shadow.blur.ToAppUnits(), appUnitsPerDevPixel)); 7763 visible.OrWith(sourceRect); 7764 } 7765 }; 7766 7767 // Shadows can translate things back into view, so we enlarge the notional 7768 // "visible" rect to ensure we don't skip painting relevant parts that might 7769 // cast a shadow within the visible area. 7770 addShadowSourceToVisible(f->StyleText()->mTextShadow.AsSpan()); 7771 7772 // Similarly for shadows that may be cast by ::selection. 7773 if (f->IsSelected()) { 7774 nsTextPaintStyle textPaint(f); 7775 Span<const StyleSimpleShadow> shadows; 7776 f->GetSelectionTextShadow(SelectionType::eNormal, textPaint, &shadows); 7777 addShadowSourceToVisible(shadows); 7778 } 7779 7780 // Inflate a little extra to allow for potential antialiasing "blur". 7781 visible.Inflate(3 * appUnitsPerDevPixel); 7782 bounds = bounds.Intersect(visible); 7783 7784 gfxContext* textDrawer = aBuilder.GetTextContext(aResources, aSc, aManager, 7785 this, bounds, deviceOffset); 7786 7787 LCPTextFrameHelper::MaybeUnionTextFrame(f, bounds - ToReferenceFrame()); 7788 7789 aBuilder.StartGroup(this); 7790 7791 RenderToContext(textDrawer, aDisplayListBuilder, mVisibleRect, 7792 aBuilder.GetInheritedOpacity(), true); 7793 const bool result = textDrawer->GetTextDrawer()->Finish(); 7794 7795 if (result) { 7796 aBuilder.FinishGroup(); 7797 } else { 7798 aBuilder.CancelGroup(true); 7799 } 7800 7801 return result; 7802 } 7803 7804 void nsDisplayText::RenderToContext(gfxContext* aCtx, 7805 nsDisplayListBuilder* aBuilder, 7806 const nsRect& aVisibleRect, float aOpacity, 7807 bool aIsRecording) { 7808 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame); 7809 7810 // Add 1 pixel of dirty area around mVisibleRect to allow us to paint 7811 // antialiased pixels beyond the measured text extents. 7812 // This is temporary until we do this in the actual calculation of text 7813 // extents. 7814 auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel(); 7815 LayoutDeviceRect extraVisible = 7816 LayoutDeviceRect::FromAppUnits(aVisibleRect, A2D); 7817 extraVisible.Inflate(1); 7818 7819 gfxRect pixelVisible(extraVisible.x, extraVisible.y, extraVisible.width, 7820 extraVisible.height); 7821 pixelVisible.Inflate(2); 7822 pixelVisible.RoundOut(); 7823 7824 gfxClipAutoSaveRestore autoSaveClip(aCtx); 7825 if (!aBuilder->IsForGenerateGlyphMask() && !aIsRecording) { 7826 autoSaveClip.Clip(pixelVisible); 7827 } 7828 7829 NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge"); 7830 NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge"); 7831 7832 gfxContextMatrixAutoSaveRestore matrixSR; 7833 7834 nsPoint framePt = ToReferenceFrame(); 7835 if (f->Style()->IsTextCombined()) { 7836 auto [offset, scale] = f->GetTextCombineOffsetAndScale(); 7837 gfxTextRun* textRun = f->GetTextRun(nsTextFrame::eInflated); 7838 bool rtl = textRun && textRun->IsRightToLeft(); 7839 if (rtl) { 7840 framePt.x -= offset; 7841 } else { 7842 framePt.x += offset; 7843 } 7844 if (scale != 1.0f) { 7845 if (auto* textDrawer = aCtx->GetTextDrawer()) { 7846 // WebRender doesn't support scaling text like this yet 7847 textDrawer->FoundUnsupportedFeature(); 7848 return; 7849 } 7850 matrixSR.SetContext(aCtx); 7851 // Setup matrix to compress text for text-combine-upright if 7852 // necessary. This is done here because we want selection be 7853 // compressed at the same time as text. 7854 gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D); 7855 if (rtl) { 7856 pt.x += gfxFloat(f->GetSize().width) / A2D; 7857 } 7858 gfxMatrix mat = aCtx->CurrentMatrixDouble() 7859 .PreTranslate(pt) 7860 .PreScale(scale, 1.0) 7861 .PreTranslate(-pt); 7862 aCtx->SetMatrixDouble(mat); 7863 } 7864 } 7865 nsTextFrame::PaintTextParams params(aCtx); 7866 params.framePt = gfx::Point(framePt.x, framePt.y); 7867 params.dirtyRect = extraVisible; 7868 7869 if (aBuilder->IsForGenerateGlyphMask()) { 7870 params.state = nsTextFrame::PaintTextParams::GenerateTextMask; 7871 } else { 7872 params.state = nsTextFrame::PaintTextParams::PaintText; 7873 } 7874 7875 f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(), 7876 f->IsSelected(), aOpacity); 7877 } 7878 7879 // This could go to nsDisplayListInvalidation.h, but 7880 // |nsTextFrame::TextDecorations| requires including of nsTextFrame.h which 7881 // would produce circular dependencies. 7882 class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry { 7883 public: 7884 nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder) 7885 : nsDisplayItemGenericGeometry(aItem, aBuilder), 7886 mVisIStartEdge(aItem->VisIStartEdge()), 7887 mVisIEndEdge(aItem->VisIEndEdge()) { 7888 nsTextFrame* f = static_cast<nsTextFrame*>(aItem->Frame()); 7889 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors, 7890 mDecorations); 7891 } 7892 7893 /** 7894 * We store the computed text decorations here since they are 7895 * computed using style data from parent frames. Any changes to these 7896 * styles will only invalidate the parent frame and not this frame. 7897 */ 7898 nsTextFrame::TextDecorations mDecorations; 7899 nscoord mVisIStartEdge; 7900 nscoord mVisIEndEdge; 7901 }; 7902 7903 nsDisplayItemGeometry* nsDisplayText::AllocateGeometry( 7904 nsDisplayListBuilder* aBuilder) { 7905 return new nsDisplayTextGeometry(this, aBuilder); 7906 } 7907 7908 void nsDisplayText::ComputeInvalidationRegion( 7909 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, 7910 nsRegion* aInvalidRegion) const { 7911 const nsDisplayTextGeometry* geometry = 7912 static_cast<const nsDisplayTextGeometry*>(aGeometry); 7913 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame); 7914 7915 nsTextFrame::TextDecorations decorations; 7916 f->GetTextDecorations(f->PresContext(), nsTextFrame::eResolvedColors, 7917 decorations); 7918 7919 bool snap; 7920 const nsRect& newRect = geometry->mBounds; 7921 nsRect oldRect = GetBounds(aBuilder, &snap); 7922 if (decorations != geometry->mDecorations || 7923 mVisIStartEdge != geometry->mVisIStartEdge || 7924 mVisIEndEdge != geometry->mVisIEndEdge || 7925 !oldRect.IsEqualInterior(newRect) || 7926 !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) { 7927 aInvalidRegion->Or(oldRect, newRect); 7928 } 7929 } 7930 7931 void nsDisplayText::WriteDebugInfo(std::stringstream& aStream) { 7932 #ifdef DEBUG 7933 aStream << " (\""; 7934 7935 nsTextFrame* f = static_cast<nsTextFrame*>(mFrame); 7936 nsCString buf; 7937 f->ToCString(buf); 7938 7939 aStream << buf.get() << "\")"; 7940 #endif 7941 } 7942 7943 nsDisplayEffectsBase::nsDisplayEffectsBase( 7944 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 7945 const ActiveScrolledRoot* aActiveScrolledRoot, 7946 ContainerASRType aContainerASRType, bool aClearClipChain) 7947 : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, 7948 aContainerASRType, aClearClipChain) { 7949 MOZ_COUNT_CTOR(nsDisplayEffectsBase); 7950 } 7951 7952 nsDisplayEffectsBase::nsDisplayEffectsBase(nsDisplayListBuilder* aBuilder, 7953 nsIFrame* aFrame, 7954 nsDisplayList* aList) 7955 : nsDisplayWrapList(aBuilder, aFrame, aList) { 7956 MOZ_COUNT_CTOR(nsDisplayEffectsBase); 7957 } 7958 7959 nsRegion nsDisplayEffectsBase::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, 7960 bool* aSnap) const { 7961 *aSnap = false; 7962 return nsRegion(); 7963 } 7964 7965 void nsDisplayEffectsBase::HitTest(nsDisplayListBuilder* aBuilder, 7966 const nsRect& aRect, HitTestState* aState, 7967 nsTArray<nsIFrame*>* aOutFrames) { 7968 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2); 7969 if (SVGIntegrationUtils::HitTestFrameForEffects( 7970 mFrame, rectCenter - ToReferenceFrame())) { 7971 mList.HitTest(aBuilder, aRect, aState, aOutFrames); 7972 } 7973 } 7974 7975 gfxRect nsDisplayEffectsBase::BBoxInUserSpace() const { 7976 return SVGUtils::GetBBox(mFrame); 7977 } 7978 7979 gfxPoint nsDisplayEffectsBase::UserSpaceOffset() const { 7980 return SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame); 7981 } 7982 7983 void nsDisplayEffectsBase::ComputeInvalidationRegion( 7984 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, 7985 nsRegion* aInvalidRegion) const { 7986 const auto* geometry = 7987 static_cast<const nsDisplaySVGEffectGeometry*>(aGeometry); 7988 bool snap; 7989 nsRect bounds = GetBounds(aBuilder, &snap); 7990 if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() || 7991 geometry->mUserSpaceOffset != UserSpaceOffset() || 7992 !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) { 7993 // Filter and mask output can depend on the location of the frame's user 7994 // space and on the frame's BBox. We need to invalidate if either of these 7995 // change relative to the reference frame. 7996 // Invalidations from our inactive layer manager are not enough to catch 7997 // some of these cases because filters can produce output even if there's 7998 // nothing in the filter input. 7999 aInvalidRegion->Or(bounds, geometry->mBounds); 8000 } 8001 } 8002 8003 bool nsDisplayEffectsBase::ValidateSVGFrame() { 8004 if (mFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) { 8005 ISVGDisplayableFrame* svgFrame = do_QueryFrame(mFrame); 8006 if (!svgFrame) { 8007 return false; 8008 } 8009 if (auto* svgElement = SVGElement::FromNode(mFrame->GetContent())) { 8010 // The SVG spec says only to draw filters if the element 8011 // has valid dimensions. 8012 return svgElement->HasValidDimensions(); 8013 } 8014 return false; 8015 } 8016 8017 return true; 8018 } 8019 8020 using PaintFramesParams = SVGIntegrationUtils::PaintFramesParams; 8021 8022 static void ComputeMaskGeometry(PaintFramesParams& aParams) { 8023 // Properties are added lazily and may have been removed by a restyle, so 8024 // make sure all applicable ones are set again. 8025 nsIFrame* firstFrame = 8026 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame); 8027 8028 const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset(); 8029 8030 nsTArray<SVGMaskFrame*> maskFrames; 8031 // XXX check return value? 8032 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames); 8033 8034 if (maskFrames.Length() == 0) { 8035 return; 8036 } 8037 8038 gfxContext& ctx = aParams.ctx; 8039 nsIFrame* frame = aParams.frame; 8040 8041 nsPoint offsetToUserSpace = 8042 nsLayoutUtils::ComputeOffsetToUserSpace(aParams.builder, aParams.frame); 8043 8044 auto cssToDevScale = frame->PresContext()->CSSToDevPixelScale(); 8045 int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel(); 8046 8047 gfxPoint devPixelOffsetToUserSpace = 8048 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, appUnitsPerDevPixel); 8049 8050 gfxContextMatrixAutoSaveRestore matSR(&ctx); 8051 ctx.SetMatrixDouble( 8052 ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace)); 8053 8054 // Convert boaderArea and dirtyRect to user space. 8055 nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace; 8056 nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace; 8057 8058 // Union all mask layer rectangles in user space. 8059 LayoutDeviceRect maskInUserSpace; 8060 for (size_t i = 0; i < maskFrames.Length(); i++) { 8061 SVGMaskFrame* maskFrame = maskFrames[i]; 8062 LayoutDeviceRect currentMaskSurfaceRect; 8063 8064 if (maskFrame) { 8065 auto rect = maskFrame->GetMaskArea(aParams.frame); 8066 currentMaskSurfaceRect = 8067 CSSRect::FromUnknownRect(ToRect(rect)) * cssToDevScale; 8068 } else { 8069 nsCSSRendering::ImageLayerClipState clipState; 8070 nsCSSRendering::GetImageLayerClip( 8071 svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(), 8072 userSpaceBorderArea, userSpaceDirtyRect, 8073 /* aWillPaintBorder = */ false, appUnitsPerDevPixel, &clipState); 8074 currentMaskSurfaceRect = LayoutDeviceRect::FromUnknownRect( 8075 ToRect(clipState.mDirtyRectInDevPx)); 8076 } 8077 8078 maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect); 8079 } 8080 8081 if (!maskInUserSpace.IsEmpty()) { 8082 aParams.maskRect = Some(maskInUserSpace); 8083 } else { 8084 aParams.maskRect = Nothing(); 8085 } 8086 } 8087 8088 nsDisplayMasksAndClipPaths::nsDisplayMasksAndClipPaths( 8089 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, 8090 const ActiveScrolledRoot* aActiveScrolledRoot, 8091 ContainerASRType aContainerASRType, bool aWrapsBackdropFilter, 8092 bool aForceIsolation) 8093 : nsDisplayEffectsBase(aBuilder, aFrame, aList, aActiveScrolledRoot, 8094 aContainerASRType, /* aClearClipChain = */ true), 8095 mWrapsBackdropFilter(aWrapsBackdropFilter), 8096 mForceIsolation(aForceIsolation) { 8097 MOZ_COUNT_CTOR(nsDisplayMasksAndClipPaths); 8098 8099 nsPresContext* presContext = mFrame->PresContext(); 8100 uint32_t flags = 8101 aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE; 8102 const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset(); 8103 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, svgReset->mMask) { 8104 const auto& layer = svgReset->mMask.mLayers[i]; 8105 if (!layer.mImage.IsResolved()) { 8106 continue; 8107 } 8108 const nsRect& borderArea = mFrame->GetRectRelativeToSelf(); 8109 // NOTE(emilio): We only care about the dest rect so we don't bother 8110 // computing a clip. 8111 bool isTransformedFixed = false; 8112 nsBackgroundLayerState state = nsCSSRendering::PrepareImageLayer( 8113 presContext, aFrame, flags, borderArea, borderArea, layer, 8114 &isTransformedFixed); 8115 mDestRects.AppendElement(state.mDestArea); 8116 } 8117 } 8118 8119 static bool CanMergeDisplayMaskFrame(nsIFrame* aFrame) { 8120 // Do not merge items for box-decoration-break:clone elements, 8121 // since each box should have its own mask in that case. 8122 if (aFrame->StyleBorder()->mBoxDecorationBreak == 8123 StyleBoxDecorationBreak::Clone) { 8124 return false; 8125 } 8126 8127 // Do not merge if either frame has a mask. Continuation frames should apply 8128 // the mask independently (just like nsDisplayBackgroundImage). 8129 if (aFrame->StyleSVGReset()->HasMask()) { 8130 return false; 8131 } 8132 8133 return true; 8134 } 8135 8136 bool nsDisplayMasksAndClipPaths::CanMerge(const nsDisplayItem* aItem) const { 8137 // Items for the same content element should be merged into a single 8138 // compositing group. 8139 if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) || 8140 !HasSameContent(aItem)) { 8141 return false; 8142 } 8143 8144 return CanMergeDisplayMaskFrame(mFrame) && 8145 CanMergeDisplayMaskFrame(aItem->Frame()); 8146 } 8147 8148 bool nsDisplayMasksAndClipPaths::IsValidMask() { 8149 if (!ValidateSVGFrame()) { 8150 return false; 8151 } 8152 8153 return SVGUtils::DetermineMaskUsage(mFrame, false).UsingMaskOrClipPath(); 8154 } 8155 8156 bool nsDisplayMasksAndClipPaths::PaintMask(nsDisplayListBuilder* aBuilder, 8157 gfxContext* aMaskContext, 8158 bool aHandleOpacity, 8159 bool* aMaskPainted) { 8160 MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8); 8161 8162 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags()); 8163 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); 8164 PaintFramesParams params(*aMaskContext, mFrame, mBounds, borderArea, aBuilder, 8165 aHandleOpacity, imgParams); 8166 ComputeMaskGeometry(params); 8167 bool maskIsComplete = false; 8168 bool painted = SVGIntegrationUtils::PaintMask(params, maskIsComplete); 8169 if (aMaskPainted) { 8170 *aMaskPainted = painted; 8171 } 8172 8173 return maskIsComplete && 8174 (imgParams.result == ImgDrawResult::SUCCESS || 8175 imgParams.result == ImgDrawResult::SUCCESS_NOT_COMPLETE || 8176 imgParams.result == ImgDrawResult::WRONG_SIZE); 8177 } 8178 8179 void nsDisplayMasksAndClipPaths::ComputeInvalidationRegion( 8180 nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, 8181 nsRegion* aInvalidRegion) const { 8182 nsDisplayEffectsBase::ComputeInvalidationRegion(aBuilder, aGeometry, 8183 aInvalidRegion); 8184 8185 const auto* geometry = 8186 static_cast<const nsDisplayMasksAndClipPathsGeometry*>(aGeometry); 8187 bool snap; 8188 nsRect bounds = GetBounds(aBuilder, &snap); 8189 8190 if (mDestRects.Length() != geometry->mDestRects.Length()) { 8191 aInvalidRegion->Or(bounds, geometry->mBounds); 8192 } else { 8193 for (size_t i = 0; i < mDestRects.Length(); i++) { 8194 if (!mDestRects[i].IsEqualInterior(geometry->mDestRects[i])) { 8195 aInvalidRegion->Or(bounds, geometry->mBounds); 8196 break; 8197 } 8198 } 8199 } 8200 } 8201 8202 void nsDisplayMasksAndClipPaths::PaintWithContentsPaintCallback( 8203 nsDisplayListBuilder* aBuilder, gfxContext* aCtx, 8204 const std::function<void()>& aPaintChildren) { 8205 // Clip the drawing target by mVisibleRect, which contains the visible 8206 // region of the target frame and its out-of-flow and inflow descendants. 8207 Rect bounds = NSRectToRect(GetPaintRect(aBuilder, aCtx), 8208 mFrame->PresContext()->AppUnitsPerDevPixel()); 8209 bounds.RoundOut(); 8210 gfxClipAutoSaveRestore autoSaveClip(aCtx); 8211 autoSaveClip.Clip(bounds); 8212 8213 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags()); 8214 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); 8215 PaintFramesParams params(*aCtx, mFrame, GetPaintRect(aBuilder, aCtx), 8216 borderArea, aBuilder, false, imgParams); 8217 8218 ComputeMaskGeometry(params); 8219 8220 SVGIntegrationUtils::PaintMaskAndClipPath(params, aPaintChildren); 8221 } 8222 8223 void nsDisplayMasksAndClipPaths::Paint(nsDisplayListBuilder* aBuilder, 8224 gfxContext* aCtx) { 8225 if (!IsValidMask()) { 8226 return; 8227 } 8228 PaintWithContentsPaintCallback(aBuilder, aCtx, [&] { 8229 GetChildren()->Paint(aBuilder, aCtx, 8230 mFrame->PresContext()->AppUnitsPerDevPixel()); 8231 }); 8232 } 8233 8234 static Maybe<wr::WrClipChainId> CreateSimpleClipRegion( 8235 const nsDisplayMasksAndClipPaths& aDisplayItem, 8236 wr::DisplayListBuilder& aBuilder) { 8237 nsIFrame* frame = aDisplayItem.Frame(); 8238 const auto* style = frame->StyleSVGReset(); 8239 MOZ_ASSERT(style->HasClipPath() || style->HasMask()); 8240 if (!SVGUtils::DetermineMaskUsage(frame, false).IsSimpleClipShape()) { 8241 return Nothing(); 8242 } 8243 8244 const auto& clipPath = style->mClipPath; 8245 const auto& shape = *clipPath.AsShape()._0; 8246 8247 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel(); 8248 const nsRect refBox = 8249 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1); 8250 8251 wr::WrClipId clipId{}; 8252 8253 switch (shape.tag) { 8254 case StyleBasicShape::Tag::Rect: { 8255 const nsRect rect = 8256 ShapeUtils::ComputeInsetRect(shape.AsRect().rect, refBox) + 8257 aDisplayItem.ToReferenceFrame(); 8258 8259 nsRectCornerRadii radii; 8260 if (ShapeUtils::ComputeRectRadii(shape.AsRect().round, refBox, rect, 8261 radii)) { 8262 clipId = aBuilder.DefineRoundedRectClip( 8263 Nothing(), 8264 wr::ToComplexClipRegion(rect, radii, appUnitsPerDevPixel)); 8265 } else { 8266 clipId = aBuilder.DefineRectClip( 8267 Nothing(), wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits( 8268 rect, appUnitsPerDevPixel))); 8269 } 8270 8271 break; 8272 } 8273 case StyleBasicShape::Tag::Ellipse: 8274 case StyleBasicShape::Tag::Circle: { 8275 nsPoint center = ShapeUtils::ComputeCircleOrEllipseCenter(shape, refBox); 8276 8277 nsSize radii; 8278 if (shape.IsEllipse()) { 8279 radii = ShapeUtils::ComputeEllipseRadii(shape, center, refBox); 8280 } else { 8281 nscoord radius = ShapeUtils::ComputeCircleRadius(shape, center, refBox); 8282 radii = {radius, radius}; 8283 } 8284 8285 nsRect ellipseRect(aDisplayItem.ToReferenceFrame() + center - 8286 nsPoint(radii.width, radii.height), 8287 radii * 2); 8288 8289 nsRectCornerRadii ellipseRadii; 8290 for (const auto corner : AllPhysicalHalfCorners()) { 8291 ellipseRadii[corner] = 8292 HalfCornerIsX(corner) ? radii.width : radii.height; 8293 } 8294 8295 clipId = aBuilder.DefineRoundedRectClip( 8296 Nothing(), wr::ToComplexClipRegion(ellipseRect, ellipseRadii, 8297 appUnitsPerDevPixel)); 8298 8299 break; 8300 } 8301 default: 8302 // Please don't add more exceptions, try to find a way to define the clip 8303 // without using a mask image. 8304 // 8305 // And if you _really really_ need to add an exception, add it to 8306 // SVGUtils::DetermineMaskUsage 8307 MOZ_ASSERT_UNREACHABLE("Unhandled shape id?"); 8308 return Nothing(); 8309 } 8310 8311 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain( 8312 {&clipId, 1}, aBuilder.CurrentClipChainIdIfNotRoot()); 8313 8314 return Some(clipChainId); 8315 } 8316 8317 static void FillPolygonDataForDisplayItem( 8318 const nsDisplayMasksAndClipPaths& aDisplayItem, 8319 nsTArray<wr::LayoutPoint>& aPoints, wr::FillRule& aFillRule) { 8320 nsIFrame* frame = aDisplayItem.Frame(); 8321 const auto* style = frame->StyleSVGReset(); 8322 bool isPolygon = style->HasClipPath() && style->mClipPath.IsShape() && 8323 style->mClipPath.AsShape()._0->IsPolygon(); 8324 if (!isPolygon) { 8325 return; 8326 } 8327 8328 const auto& clipPath = style->mClipPath; 8329 const auto& shape = *clipPath.AsShape()._0; 8330 const nsRect refBox = 8331 nsLayoutUtils::ComputeClipPathGeometryBox(frame, clipPath.AsShape()._1); 8332 8333 // We only fill polygon data for polygons that are below a complexity 8334 // limit. 8335 nsTArray<nsPoint> vertices = 8336 ShapeUtils::ComputePolygonVertices(shape, refBox); 8337 if (vertices.Length() > wr::POLYGON_CLIP_VERTEX_MAX) { 8338 return; 8339 } 8340 8341 auto appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel(); 8342 8343 for (size_t i = 0; i < vertices.Length(); ++i) { 8344 wr::LayoutPoint point = wr::ToLayoutPoint( 8345 LayoutDevicePoint::FromAppUnits(vertices[i], appUnitsPerDevPixel)); 8346 aPoints.AppendElement(point); 8347 } 8348 8349 aFillRule = (shape.AsPolygon().fill == StyleFillRule::Nonzero) 8350 ? wr::FillRule::Nonzero 8351 : wr::FillRule::Evenodd; 8352 } 8353 8354 static Maybe<wr::WrClipChainId> CreateWRClipPathAndMasks( 8355 nsDisplayMasksAndClipPaths* aDisplayItem, const LayoutDeviceRect& aBounds, 8356 wr::IpcResourceUpdateQueue& aResources, wr::DisplayListBuilder& aBuilder, 8357 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 8358 nsDisplayListBuilder* aDisplayListBuilder) { 8359 if (auto clip = CreateSimpleClipRegion(*aDisplayItem, aBuilder)) { 8360 return clip; 8361 } 8362 8363 Maybe<wr::ImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage( 8364 aDisplayItem, aBuilder, aResources, aSc, aDisplayListBuilder, aBounds); 8365 if (!mask) { 8366 return Nothing(); 8367 } 8368 8369 // We couldn't create a simple clip region, but before we create an image 8370 // mask clip, see if we can get a polygon clip to add to it. 8371 nsTArray<wr::LayoutPoint> points; 8372 wr::FillRule fillRule = wr::FillRule::Nonzero; 8373 FillPolygonDataForDisplayItem(*aDisplayItem, points, fillRule); 8374 8375 wr::WrClipId clipId = 8376 aBuilder.DefineImageMaskClip(mask.ref(), points, fillRule); 8377 8378 wr::WrClipChainId clipChainId = aBuilder.DefineClipChain( 8379 {&clipId, 1}, aBuilder.CurrentClipChainIdIfNotRoot()); 8380 8381 return Some(clipChainId); 8382 } 8383 8384 bool nsDisplayMasksAndClipPaths::CreateWebRenderCommands( 8385 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 8386 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 8387 nsDisplayListBuilder* aDisplayListBuilder) { 8388 bool snap; 8389 auto appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); 8390 nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap); 8391 LayoutDeviceRect bounds = 8392 LayoutDeviceRect::FromAppUnits(displayBounds, appUnitsPerDevPixel); 8393 8394 Maybe<wr::WrClipChainId> clip = CreateWRClipPathAndMasks( 8395 this, bounds, aResources, aBuilder, aSc, aManager, aDisplayListBuilder); 8396 8397 float oldOpacity = aBuilder.GetInheritedOpacity(); 8398 8399 Maybe<StackingContextHelper> layer; 8400 const StackingContextHelper* sc = &aSc; 8401 if (clip) { 8402 // Create a new stacking context to attach the mask to, ensuring the mask is 8403 // applied to the aggregate, and not the individual elements. 8404 8405 // The stacking context shouldn't have any offset. 8406 bounds.MoveTo(0, 0); 8407 8408 Maybe<float> opacity = 8409 (SVGUtils::DetermineMaskUsage(mFrame, false).IsSimpleClipShape() && 8410 aBuilder.GetInheritedOpacity() != 1.0f) 8411 ? Some(aBuilder.GetInheritedOpacity()) 8412 : Nothing(); 8413 8414 wr::StackingContextParams params; 8415 params.clip = wr::WrStackingContextClip::ClipChain(clip->id); 8416 params.opacity = opacity.ptrOr(nullptr); 8417 if (mForceIsolation) { 8418 params.flags |= wr::StackingContextFlags::FORCED_ISOLATION; 8419 } 8420 if (mWrapsBackdropFilter) { 8421 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER; 8422 } 8423 layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, params, 8424 bounds); 8425 sc = layer.ptr(); 8426 } 8427 8428 aBuilder.SetInheritedOpacity(1.0f); 8429 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain(); 8430 aBuilder.SetInheritedClipChain(nullptr); 8431 CreateWebRenderCommandsNewClipListOption(aBuilder, aResources, *sc, aManager, 8432 aDisplayListBuilder, layer.isSome()); 8433 aBuilder.SetInheritedOpacity(oldOpacity); 8434 aBuilder.SetInheritedClipChain(oldClipChain); 8435 8436 return true; 8437 } 8438 8439 Maybe<nsRect> nsDisplayMasksAndClipPaths::GetClipWithRespectToASR( 8440 nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR) const { 8441 if (const DisplayItemClip* clip = 8442 DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) { 8443 return Some(clip->GetClipRect()); 8444 } 8445 // This item does not have a clip with respect to |aASR|. However, we 8446 // might still have finite bounds with respect to |aASR|. Check our 8447 // children. 8448 nsDisplayList* childList = GetSameCoordinateSystemChildren(); 8449 if (childList) { 8450 return Some(childList->GetClippedBoundsWithRespectToASR(aBuilder, aASR)); 8451 } 8452 #ifdef DEBUG 8453 NS_ASSERTION(false, "item should have finite clip with respect to aASR"); 8454 #endif 8455 return Nothing(); 8456 } 8457 8458 #ifdef MOZ_DUMP_PAINTING 8459 void nsDisplayMasksAndClipPaths::PrintEffects(nsACString& aTo) { 8460 nsIFrame* firstFrame = 8461 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); 8462 bool first = true; 8463 aTo += " effects=("; 8464 SVGClipPathFrame* clipPathFrame; 8465 // XXX Check return value? 8466 SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame); 8467 if (clipPathFrame) { 8468 if (!first) { 8469 aTo += ", "; 8470 } 8471 aTo += nsPrintfCString( 8472 "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial"); 8473 first = false; 8474 } else if (mFrame->StyleSVGReset()->HasClipPath()) { 8475 if (!first) { 8476 aTo += ", "; 8477 } 8478 aTo += "clip(basic-shape)"; 8479 first = false; 8480 } 8481 8482 nsTArray<SVGMaskFrame*> masks; 8483 // XXX check return value? 8484 SVGObserverUtils::GetAndObserveMasks(firstFrame, &masks); 8485 if (!masks.IsEmpty() && masks[0]) { 8486 if (!first) { 8487 aTo += ", "; 8488 } 8489 aTo += "mask"; 8490 } 8491 aTo += ")"; 8492 } 8493 #endif 8494 8495 bool nsDisplayBackdropFilters::CreateWebRenderCommands( 8496 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 8497 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 8498 nsDisplayListBuilder* aDisplayListBuilder) { 8499 WrFiltersHolder wrFilters; 8500 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style(); 8501 auto filterChain = style.StyleEffects()->mBackdropFilters.AsSpan(); 8502 // Try building a CSS filter chain 8503 WrFiltersStatus status = SVGIntegrationUtils::CreateWebRenderCSSFilters( 8504 filterChain, mFrame, wrFilters); 8505 if (status == WrFiltersStatus::BLOB_FALLBACK) { 8506 // If the filters are too complex for CSS filters, try SVG filters 8507 auto offsetForSVGFilters = 8508 nsLayoutUtils::ComputeOffsetToUserSpace(aDisplayListBuilder, mFrame); 8509 status = SVGIntegrationUtils::BuildWebRenderFilters( 8510 mFrame, filterChain, StyleFilterType::BackdropFilter, wrFilters, 8511 offsetForSVGFilters); 8512 } 8513 8514 if (status == WrFiltersStatus::BLOB_FALLBACK) { 8515 // TODO: If painting backdrop-filters on the content side is implemented, 8516 // consider returning false to fall back to that. 8517 wrFilters = {}; 8518 } 8519 8520 if (status == WrFiltersStatus::UNSUPPORTED) { 8521 wrFilters = {}; 8522 } 8523 8524 nsCSSRendering::ImageLayerClipState clip; 8525 nsCSSRendering::GetImageLayerClip( 8526 style.StyleBackground()->BottomLayer(), mFrame, *style.StyleBorder(), 8527 mBackdropRect, mBackdropRect, false, 8528 mFrame->PresContext()->AppUnitsPerDevPixel(), &clip); 8529 8530 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits( 8531 mBackdropRect, mFrame->PresContext()->AppUnitsPerDevPixel()); 8532 8533 wr::ComplexClipRegion region = 8534 wr::ToComplexClipRegion(clip.mBGClipArea, clip.mRadii, 8535 mFrame->PresContext()->AppUnitsPerDevPixel()); 8536 8537 aBuilder.PushBackdropFilter(wr::ToLayoutRect(bounds), region, 8538 wrFilters.filters, wrFilters.filter_datas, 8539 !BackfaceIsHidden()); 8540 8541 wr::StackingContextParams params; 8542 params.clip = 8543 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId()); 8544 params.flags = wr::StackingContextFlags::FORCED_ISOLATION; 8545 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, 8546 params); 8547 8548 nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager, 8549 aDisplayListBuilder); 8550 return true; 8551 } 8552 8553 void nsDisplayBackdropFilters::Paint(nsDisplayListBuilder* aBuilder, 8554 gfxContext* aCtx) { 8555 // TODO: Implement backdrop filters 8556 GetChildren()->Paint(aBuilder, aCtx, 8557 mFrame->PresContext()->AppUnitsPerDevPixel()); 8558 } 8559 8560 nsRect nsDisplayBackdropFilters::GetBounds(nsDisplayListBuilder* aBuilder, 8561 bool* aSnap) const { 8562 nsRect childBounds = nsDisplayWrapList::GetBounds(aBuilder, aSnap); 8563 8564 *aSnap = false; 8565 8566 return mBackdropRect.Union(childBounds); 8567 } 8568 8569 /* static */ 8570 nsDisplayFilters::nsDisplayFilters(nsDisplayListBuilder* aBuilder, 8571 nsIFrame* aFrame, nsDisplayList* aList, 8572 nsIFrame* aStyleFrame, 8573 bool aWrapsBackdropFilter) 8574 : nsDisplayEffectsBase(aBuilder, aFrame, aList), 8575 mStyle(aFrame == aStyleFrame ? nullptr : aStyleFrame->Style()), 8576 mEffectsBounds(aFrame->InkOverflowRectRelativeToSelf()), 8577 mWrapsBackdropFilter(aWrapsBackdropFilter) { 8578 MOZ_COUNT_CTOR(nsDisplayFilters); 8579 mVisibleRect = aBuilder->GetVisibleRect() + 8580 aBuilder->GetCurrentFrameOffsetToReferenceFrame(); 8581 } 8582 8583 void nsDisplayFilters::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) { 8584 PaintWithContentsPaintCallback(aBuilder, aCtx, [&](gfxContext* aContext) { 8585 GetChildren()->Paint(aBuilder, aContext, 8586 mFrame->PresContext()->AppUnitsPerDevPixel()); 8587 }); 8588 } 8589 8590 void nsDisplayFilters::PaintWithContentsPaintCallback( 8591 nsDisplayListBuilder* aBuilder, gfxContext* aCtx, 8592 const std::function<void(gfxContext* aContext)>& aPaintChildren) { 8593 imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags()); 8594 nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); 8595 PaintFramesParams params(*aCtx, mFrame, mVisibleRect, borderArea, aBuilder, 8596 false, imgParams); 8597 8598 gfxPoint userSpaceToFrameSpaceOffset = 8599 SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(mFrame, params); 8600 8601 auto filterChain = mStyle ? mStyle->StyleEffects()->mFilters.AsSpan() 8602 : mFrame->StyleEffects()->mFilters.AsSpan(); 8603 SVGIntegrationUtils::PaintFilter( 8604 params, filterChain, 8605 [&](gfxContext& aContext, imgDrawingParams&, const gfxMatrix*, 8606 const nsIntRect*) { 8607 gfxContextMatrixAutoSaveRestore autoSR(&aContext); 8608 aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate( 8609 -userSpaceToFrameSpaceOffset)); 8610 aPaintChildren(&aContext); 8611 }); 8612 } 8613 8614 bool nsDisplayFilters::CanCreateWebRenderCommands() const { 8615 return SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(mFrame); 8616 } 8617 8618 bool nsDisplayFilters::CreateWebRenderCommands( 8619 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 8620 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 8621 nsDisplayListBuilder* aDisplayListBuilder) { 8622 WrFiltersHolder wrFilters; 8623 const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style(); 8624 auto filterChain = style.StyleEffects()->mFilters.AsSpan(); 8625 // Try building a CSS filter chain 8626 WrFiltersStatus status = SVGIntegrationUtils::CreateWebRenderCSSFilters( 8627 filterChain, mFrame, wrFilters); 8628 if (status == WrFiltersStatus::BLOB_FALLBACK) { 8629 // Try building an SVG filter graph 8630 auto offsetForSVGFilters = 8631 nsLayoutUtils::ComputeOffsetToUserSpace(aDisplayListBuilder, mFrame); 8632 status = SVGIntegrationUtils::BuildWebRenderFilters( 8633 mFrame, filterChain, StyleFilterType::Filter, wrFilters, 8634 offsetForSVGFilters); 8635 if (status == WrFiltersStatus::BLOB_FALLBACK && mStyle) { 8636 // TODO(bug 1769223): Support fallback filters in the root code-path, 8637 // perhaps. For now treat it the same way as invalid filters. 8638 status = WrFiltersStatus::UNSUPPORTED; 8639 } 8640 } 8641 8642 switch (status) { 8643 case WrFiltersStatus::BLOB_FALLBACK: 8644 // Draw using fallback. 8645 return false; 8646 case WrFiltersStatus::UNSUPPORTED: 8647 // https://drafts.fxtf.org/filter-effects/#typedef-filter-url: 8648 // 8649 // If the filter references a non-existent object or the referenced 8650 // object is not a filter element, then the whole filter chain is 8651 // ignored. No filter is applied to the object. 8652 // 8653 // Note that other engines have a weird discrepancy between SVG and HTML 8654 // content here, but the spec is clear. 8655 wrFilters = {}; 8656 break; 8657 case WrFiltersStatus::DISABLED_FOR_PERFORMANCE: 8658 // SVG spec allows us to drop the entire filter graph if it contains too 8659 // many filters to render or other performance considerations. 8660 wrFilters = {}; 8661 break; 8662 case WrFiltersStatus::CHAIN: 8663 case WrFiltersStatus::SVGFE: 8664 // Filter the image using the wrFilters produced above. 8665 break; 8666 } 8667 8668 uint64_t clipChainId; 8669 if (wrFilters.post_filters_clip) { 8670 auto devPxRect = LayoutDeviceRect::FromAppUnits( 8671 wrFilters.post_filters_clip.value() + ToReferenceFrame(), 8672 mFrame->PresContext()->AppUnitsPerDevPixel()); 8673 auto clipId = 8674 aBuilder.DefineRectClip(Nothing(), wr::ToLayoutRect(devPxRect)); 8675 clipChainId = aBuilder 8676 .DefineClipChain({&clipId, 1}, 8677 aBuilder.CurrentClipChainIdIfNotRoot()) 8678 .id; 8679 } else { 8680 clipChainId = aBuilder.CurrentClipChainId(); 8681 } 8682 wr::WrStackingContextClip clip = 8683 wr::WrStackingContextClip::ClipChain(clipChainId); 8684 8685 float opacity = aBuilder.GetInheritedOpacity(); 8686 aBuilder.SetInheritedOpacity(1.0f); 8687 const DisplayItemClipChain* oldClipChain = aBuilder.GetInheritedClipChain(); 8688 aBuilder.SetInheritedClipChain(nullptr); 8689 wr::StackingContextParams params; 8690 params.mFilters = std::move(wrFilters.filters); 8691 params.mFilterDatas = std::move(wrFilters.filter_datas); 8692 params.opacity = opacity != 1.0f ? &opacity : nullptr; 8693 params.clip = clip; 8694 if (mWrapsBackdropFilter) { 8695 params.flags |= wr::StackingContextFlags::WRAPS_BACKDROP_FILTER; 8696 } 8697 StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, 8698 params); 8699 8700 nsDisplayEffectsBase::CreateWebRenderCommands(aBuilder, aResources, sc, 8701 aManager, aDisplayListBuilder); 8702 aBuilder.SetInheritedOpacity(opacity); 8703 aBuilder.SetInheritedClipChain(oldClipChain); 8704 8705 return true; 8706 } 8707 8708 #ifdef MOZ_DUMP_PAINTING 8709 void nsDisplayFilters::PrintEffects(nsACString& aTo) { 8710 nsIFrame* firstFrame = 8711 nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); 8712 bool first = true; 8713 aTo += " effects=("; 8714 // We may exist for a mix of CSS filter functions and/or references to SVG 8715 // filters. If we have invalid references to SVG filters then we paint 8716 // nothing, but otherwise we will apply one or more filters. 8717 if (SVGObserverUtils::GetAndObserveFilters(firstFrame, nullptr) != 8718 SVGObserverUtils::eHasRefsSomeInvalid) { 8719 if (!first) { 8720 aTo += ", "; 8721 } 8722 aTo += "filter"; 8723 } 8724 aTo += ")"; 8725 } 8726 #endif 8727 8728 nsDisplaySVGWrapper::nsDisplaySVGWrapper(nsDisplayListBuilder* aBuilder, 8729 nsIFrame* aFrame, nsDisplayList* aList) 8730 : nsDisplayWrapList(aBuilder, aFrame, aList) { 8731 MOZ_COUNT_CTOR(nsDisplaySVGWrapper); 8732 } 8733 8734 bool nsDisplaySVGWrapper::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) { 8735 return !aBuilder->GetWidgetLayerManager(); 8736 } 8737 8738 bool nsDisplaySVGWrapper::CreateWebRenderCommands( 8739 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 8740 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 8741 nsDisplayListBuilder* aDisplayListBuilder) { 8742 return CreateWebRenderCommandsNewClipListOption( 8743 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false); 8744 } 8745 8746 nsDisplayForeignObject::nsDisplayForeignObject(nsDisplayListBuilder* aBuilder, 8747 nsIFrame* aFrame, 8748 nsDisplayList* aList) 8749 : nsDisplayWrapList(aBuilder, aFrame, aList) { 8750 MOZ_COUNT_CTOR(nsDisplayForeignObject); 8751 } 8752 8753 bool nsDisplayForeignObject::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) { 8754 return !aBuilder->GetWidgetLayerManager(); 8755 } 8756 8757 bool nsDisplayForeignObject::CreateWebRenderCommands( 8758 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 8759 const StackingContextHelper& aSc, RenderRootStateManager* aManager, 8760 nsDisplayListBuilder* aDisplayListBuilder) { 8761 AutoRestore<bool> restoreDoGrouping(aManager->CommandBuilder().mDoGrouping); 8762 aManager->CommandBuilder().mDoGrouping = false; 8763 return CreateWebRenderCommandsNewClipListOption( 8764 aBuilder, aResources, aSc, aManager, aDisplayListBuilder, false); 8765 } 8766 8767 void nsDisplayLink::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) { 8768 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel(); 8769 aCtx->GetDrawTarget()->Link( 8770 mLinkURI.get(), mLinkDest.get(), 8771 NSRectToRect(GetPaintRect(aBuilder, aCtx), appPerDev)); 8772 } 8773 8774 void nsDisplayDestination::Paint(nsDisplayListBuilder* aBuilder, 8775 gfxContext* aCtx) { 8776 auto appPerDev = mFrame->PresContext()->AppUnitsPerDevPixel(); 8777 aCtx->GetDrawTarget()->Destination( 8778 mDestinationName.get(), 8779 NSPointToPoint(GetPaintRect(aBuilder, aCtx).TopLeft(), appPerDev)); 8780 } 8781 8782 void nsDisplayListCollection::SerializeWithCorrectZOrder( 8783 nsDisplayList* aOutResultList, nsIContent* aContent) { 8784 // Sort PositionedDescendants() in CSS 'z-order' order. The list is already 8785 // in content document order and SortByZOrder is a stable sort which 8786 // guarantees that boxes produced by the same element are placed together 8787 // in the sort. Consider a position:relative inline element that breaks 8788 // across lines and has absolutely positioned children; all the abs-pos 8789 // children should be z-ordered after all the boxes for the position:relative 8790 // element itself. 8791 PositionedDescendants()->SortByZOrder(); 8792 8793 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html 8794 // 1,2: backgrounds and borders 8795 aOutResultList->AppendToTop(BorderBackground()); 8796 // 3: negative z-index children. 8797 for (auto* item : PositionedDescendants()->TakeItems()) { 8798 if (item->ZIndex() < 0) { 8799 aOutResultList->AppendToTop(item); 8800 } else { 8801 PositionedDescendants()->AppendToTop(item); 8802 } 8803 } 8804 8805 // 4: block backgrounds 8806 aOutResultList->AppendToTop(BlockBorderBackgrounds()); 8807 // 5: floats 8808 aOutResultList->AppendToTop(Floats()); 8809 // 7: general content 8810 aOutResultList->AppendToTop(Content()); 8811 // 7.5: outlines, in content tree order. We need to sort by content order 8812 // because an element with outline that breaks and has children with outline 8813 // might have placed child outline items between its own outline items. 8814 // The element's outline items need to all come before any child outline 8815 // items. 8816 if (aContent) { 8817 Outlines()->SortByContentOrder(aContent); 8818 } 8819 aOutResultList->AppendToTop(Outlines()); 8820 // 8, 9: non-negative z-index children 8821 aOutResultList->AppendToTop(PositionedDescendants()); 8822 } 8823 8824 uint32_t PaintTelemetry::sPaintLevel = 0; 8825 8826 PaintTelemetry::AutoRecordPaint::AutoRecordPaint() { 8827 // Don't record nested paints. 8828 if (sPaintLevel++ > 0) { 8829 return; 8830 } 8831 8832 mStart = TimeStamp::Now(); 8833 } 8834 8835 PaintTelemetry::AutoRecordPaint::~AutoRecordPaint() { 8836 MOZ_ASSERT(sPaintLevel != 0); 8837 if (--sPaintLevel > 0) { 8838 return; 8839 } 8840 8841 // If we're in multi-process mode, don't include paint times for the parent 8842 // process. 8843 if (gfxVars::BrowserTabsRemoteAutostart() && XRE_IsParentProcess()) { 8844 return; 8845 } 8846 8847 // Record the total time. 8848 mozilla::glean::gfx_content::paint_time.AccumulateRawDuration( 8849 TimeStamp::Now() - mStart); 8850 } 8851 8852 static nsIFrame* GetSelfOrPlaceholderFor(nsIFrame* aFrame) { 8853 if (aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW)) { 8854 return aFrame; 8855 } 8856 8857 if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && 8858 !aFrame->GetPrevInFlow()) { 8859 return aFrame->GetPlaceholderFrame(); 8860 } 8861 8862 return aFrame; 8863 } 8864 8865 static nsIFrame* GetAncestorFor(nsIFrame* aFrame) { 8866 nsIFrame* f = GetSelfOrPlaceholderFor(aFrame); 8867 MOZ_ASSERT(f); 8868 return nsLayoutUtils::GetCrossDocParentFrameInProcess(f); 8869 } 8870 8871 nsDisplayListBuilder::AutoBuildingDisplayList::AutoBuildingDisplayList( 8872 nsDisplayListBuilder* aBuilder, nsIFrame* aForChild, 8873 const nsRect& aVisibleRect, const nsRect& aDirtyRect, 8874 const bool aIsTransformed) 8875 : mBuilder(aBuilder), 8876 mPrevFrame(aBuilder->mCurrentFrame), 8877 mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame), 8878 mPrevVisibleRect(aBuilder->mVisibleRect), 8879 mPrevDirtyRect(aBuilder->mDirtyRect), 8880 mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame), 8881 mPrevAdditionalOffset(aBuilder->mAdditionalOffset), 8882 mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo), 8883 mPrevAncestorHasApzAwareEventHandler( 8884 aBuilder->mAncestorHasApzAwareEventHandler), 8885 mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems), 8886 mPrevInInvalidSubtree(aBuilder->mInInvalidSubtree) { 8887 // If the last AutoBuildingDisplayList on the stack that we created was for 8888 // this same frame then we are already up to date and can skip this work (this 8889 // happens eg when BuildDisplayListForChild calls 8890 // BuildDisplayListForStackingContext). 8891 if (aForChild != mPrevFrame) { 8892 if (aIsTransformed) { 8893 aBuilder->mCurrentOffsetToReferenceFrame = 8894 aBuilder->AdditionalOffset().refOr(nsPoint()); 8895 aBuilder->mCurrentReferenceFrame = aForChild; 8896 } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) { 8897 aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition(); 8898 } else { 8899 aBuilder->mCurrentReferenceFrame = aBuilder->FindReferenceFrameFor( 8900 aForChild, &aBuilder->mCurrentOffsetToReferenceFrame); 8901 } 8902 } 8903 8904 // If aForChild is being visited from a frame other than it's ancestor frame, 8905 // mInInvalidSubtree will need to be recalculated the slow way. 8906 if (aForChild == mPrevFrame || GetAncestorFor(aForChild) == mPrevFrame) { 8907 aBuilder->mInInvalidSubtree = 8908 aBuilder->mInInvalidSubtree || aForChild->IsFrameModified(); 8909 } else { 8910 aBuilder->mInInvalidSubtree = AnyContentAncestorModified(aForChild); 8911 } 8912 8913 aBuilder->mCurrentFrame = aForChild; 8914 aBuilder->mVisibleRect = aVisibleRect; 8915 aBuilder->mDirtyRect = 8916 aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect; 8917 } 8918 8919 } // namespace mozilla