Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
*/
/*
* structures that represent things to be painted (ordered in z-order),
* used during painting and hit testing
*/
#include "nsDisplayList.h"
#include <stdint.h>
#include <algorithm>
#include <limits>
#include "gfxContext.h"
#include "gfxUtils.h"
#include "mozilla/DisplayPortUtils.h"
#include "mozilla/Likely.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/RemoteBrowser.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/ServiceWorkerRegistrar.h"
#include "mozilla/dom/ServiceWorkerRegistration.h"
#include "mozilla/dom/SVGElement.h"
#include "mozilla/dom/TouchEvent.h"
#include "mozilla/dom/PerformanceMainThread.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/PresShell.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/ShapeUtils.h"
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_layers.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_print.h"
#include "mozilla/SVGIntegrationUtils.h"
#include "mozilla/SVGUtils.h"
#include "mozilla/ViewportUtils.h"
#include "nsCSSRendering.h"
#include "nsCSSRenderingGradients.h"
#include "nsCaseTreatment.h"
#include "nsRefreshDriver.h"
#include "nsRegion.h"
#include "nsStyleStructInlines.h"
#include "nsStyleTransformMatrix.h"
#include "nsTransitionManager.h"
#include "gfxMatrix.h"
#include "nsLayoutUtils.h"
#include "nsIFrameInlines.h"
#include "nsStyleConsts.h"
#include "BorderConsts.h"
#include "mozilla/MathAlgorithms.h"
#include "imgIContainer.h"
#include "nsImageFrame.h"
#include "nsSubDocumentFrame.h"
#include "nsViewManager.h"
#include "ImageContainer.h"
#include "nsCanvasFrame.h"
#include "nsSubDocumentFrame.h"
#include "StickyScrollContainer.h"
#include "mozilla/AnimationPerformanceWarning.h"
#include "mozilla/AnimationUtils.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h"
#include "mozilla/glean/GleanMetrics.h"
#include "mozilla/HashTable.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/OperatorNewExtensions.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/SVGClipPathFrame.h"
#include "mozilla/SVGMaskFrame.h"
#include "mozilla/SVGObserverUtils.h"
#include "mozilla/Telemetry.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "mozilla/ViewportFrame.h"
#include "mozilla/gfx/gfxVars.h"
#include "ActiveLayerTracker.h"
#include "nsEscape.h"
#include "nsPrintfCString.h"
#include "UnitTransforms.h"
#include "LayerAnimationInfo.h"
#include "mozilla/EventStateManager.h"
#include "nsCaret.h"
#include "nsDOMTokenList.h"
#include "nsCSSProps.h"
#include "nsTableCellFrame.h"
#include "nsTableColFrame.h"
#include "nsTextFrame.h"
#include "nsTextPaintStyle.h"
#include "nsSliderFrame.h"
#include "nsFocusManager.h"
#include "TextDrawTarget.h"
#include "mozilla/layers/AnimationHelper.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/InputAPZContext.h"
#include "mozilla/layers/RenderRootStateManager.h"
#include "mozilla/layers/StackingContextHelper.h"
#include "mozilla/layers/TreeTraversal.h"
#include "mozilla/layers/WebRenderBridgeChild.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/layers/WebRenderMessages.h"
#include "mozilla/layers/WebRenderScrollData.h"
namespace mozilla {
using namespace dom;
using namespace gfx;
using namespace layout;
using namespace layers;
using namespace image;
LazyLogModule sContentDisplayListLog("dl.content");
LazyLogModule sParentDisplayListLog("dl.parent");
LazyLogModule& GetLoggerByProcess() {
return XRE_IsContentProcess() ? sContentDisplayListLog
: sParentDisplayListLog;
}
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
void AssertUniqueItem(nsDisplayItem* aItem) {
for (nsDisplayItem* i : aItem->Frame()->DisplayItems()) {
if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() &&
i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
if (i->IsPreProcessedItem() || i->IsPreProcessed()) {
continue;
}
MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
}
}
}
#endif
bool ShouldBuildItemForEvents(const DisplayItemType aType) {
return aType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO ||
(GetDisplayItemFlagsForType(aType) & TYPE_IS_CONTAINER);
}
static bool ItemTypeSupportsHitTesting(const DisplayItemType aType) {
switch (aType) {
case DisplayItemType::TYPE_BACKGROUND:
case DisplayItemType::TYPE_BACKGROUND_COLOR:
case DisplayItemType::TYPE_THEMED_BACKGROUND:
return true;
default:
return false;
}
}
void InitializeHitTestInfo(nsDisplayListBuilder* aBuilder,
nsPaintedDisplayItem* aItem,
const DisplayItemType aType) {
if (ItemTypeSupportsHitTesting(aType)) {
aItem->InitializeHitTestInfo(aBuilder);
}
}
/* static */
already_AddRefed<ActiveScrolledRoot> ActiveScrolledRoot::CreateASRForFrame(
const ActiveScrolledRoot* aParent,
ScrollContainerFrame* aScrollContainerFrame, bool aIsRetained) {
RefPtr<ActiveScrolledRoot> asr;
if (aIsRetained) {
asr = aScrollContainerFrame->GetProperty(ActiveScrolledRootCache());
}
if (!asr) {
asr = new ActiveScrolledRoot();
if (aIsRetained) {
RefPtr<ActiveScrolledRoot> ref = asr;
aScrollContainerFrame->SetProperty(ActiveScrolledRootCache(),
ref.forget().take());
}
}
asr->mParent = aParent;
asr->mScrollContainerFrame = aScrollContainerFrame;
asr->mDepth = aParent ? aParent->mDepth + 1 : 1;
asr->mRetained = aIsRetained;
return asr.forget();
}
/* static */
bool ActiveScrolledRoot::IsAncestor(const ActiveScrolledRoot* aAncestor,
const ActiveScrolledRoot* aDescendant) {
if (!aAncestor) {
// nullptr is the root
return true;
}
if (Depth(aAncestor) > Depth(aDescendant)) {
return false;
}
const ActiveScrolledRoot* asr = aDescendant;
while (asr) {
if (asr == aAncestor) {
return true;
}
asr = asr->mParent;
}
return false;
}
/* static */
bool ActiveScrolledRoot::IsProperAncestor(
const ActiveScrolledRoot* aAncestor,
const ActiveScrolledRoot* aDescendant) {
return aAncestor != aDescendant && IsAncestor(aAncestor, aDescendant);
}
/* static */
nsCString ActiveScrolledRoot::ToString(
const ActiveScrolledRoot* aActiveScrolledRoot) {
nsAutoCString str;
for (const auto* asr = aActiveScrolledRoot; asr; asr = asr->mParent) {
str.AppendPrintf("<0x%p>", asr->mScrollContainerFrame);
if (asr->mParent) {
str.AppendLiteral(", ");
}
}
return std::move(str);
}
ScrollableLayerGuid::ViewID ActiveScrolledRoot::ComputeViewId() const {
nsIContent* content = mScrollContainerFrame->GetScrolledFrame()->GetContent();
return nsLayoutUtils::FindOrCreateIDFor(content);
}
ActiveScrolledRoot::~ActiveScrolledRoot() {
if (mScrollContainerFrame && mRetained) {
mScrollContainerFrame->RemoveProperty(ActiveScrolledRootCache());
}
}
static uint64_t AddAnimationsForWebRender(
nsDisplayItem* aItem, RenderRootStateManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder,
const Maybe<LayoutDevicePoint>& aPosition = Nothing()) {
auto* effects = EffectSet::GetForFrame(aItem->Frame(), aItem->GetType());
if (!effects || effects->IsEmpty()) {
// If there is no animation on the nsIFrame, that means
// 1) we've never created any animations on this frame or
// 2) the frame was reconstruced or
// 3) all animations on the frame have finished
// in such cases we don't need do anything here.
//
// Even if there is a WebRenderAnimationData for the display item type on
// this frame, it's going to be discarded since it's not marked as being
// used.
return 0;
}
RefPtr<WebRenderAnimationData> animationData =
aManager->CommandBuilder()
.CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(aItem);
AnimationInfo& animationInfo = animationData->GetAnimationInfo();
nsIFrame* frame = aItem->Frame();
animationInfo.AddAnimationsForDisplayItem(
frame, aDisplayListBuilder, aItem, aItem->GetType(),
aManager->LayerManager(), aPosition);
// Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
// are no active animations.
uint64_t animationsId = animationInfo.GetCompositorAnimationsId();
if (!animationInfo.GetAnimations().IsEmpty()) {
OpAddCompositorAnimations anim(
CompositorAnimations(animationInfo.GetAnimations(), animationsId));
aManager->WrBridge()->AddWebRenderParentCommand(anim);
aManager->AddActiveCompositorAnimationId(animationsId);
} else if (animationsId) {
aManager->AddCompositorAnimationsIdForDiscard(animationsId);
animationsId = 0;
}
return animationsId;
}
static bool GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext,
const nsRect& aFillRect,
nsDisplayListBuilder* aBuilder) {
if (aBuilder->IsForGenerateGlyphMask()) {
return false;
}
SVGObserverUtils::GetAndObserveBackgroundClip(aFrame);
// The main function of enabling background-clip:text property value.
// When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
// this function to
// 1. Generate a mask by all descendant text frames
// 2. Push the generated mask into aContext.
gfxContext* sourceCtx = aContext;
LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
aFillRect, aFrame->PresContext()->AppUnitsPerDevPixel());
// Create a mask surface.
RefPtr<DrawTarget> sourceTarget = sourceCtx->GetDrawTarget();
RefPtr<DrawTarget> maskDT = sourceTarget->CreateClippedDrawTarget(
bounds.ToUnknownRect(), SurfaceFormat::A8);
if (!maskDT || !maskDT->IsValid()) {
return false;
}
gfxContext maskCtx(maskDT, /* aPreserveTransform */ true);
maskCtx.Multiply(Matrix::Translation(bounds.TopLeft().ToUnknownPoint()));
// Shade text shape into mask A8 surface.
nsLayoutUtils::PaintFrame(
&maskCtx, aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()),
NS_RGB(255, 255, 255), nsDisplayListBuilderMode::GenerateGlyph);
// Push the generated mask into aContext, so that the caller can pop and
// blend with it.
Matrix currentMatrix = sourceCtx->CurrentMatrix();
Matrix invCurrentMatrix = currentMatrix;
invCurrentMatrix.Invert();
RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0,
maskSurface, invCurrentMatrix);
return true;
}
nsDisplayWrapper* nsDisplayWrapList::CreateShallowCopy(
nsDisplayListBuilder* aBuilder) {
const nsDisplayWrapList* wrappedItem = AsDisplayWrapList();
MOZ_ASSERT(wrappedItem);
// Create a new nsDisplayWrapList using a copy-constructor. This is done
// to preserve the information about bounds.
nsDisplayWrapper* wrapper =
new (aBuilder) nsDisplayWrapper(aBuilder, *wrappedItem);
wrapper->SetType(nsDisplayWrapper::ItemType());
MOZ_ASSERT(wrapper);
// Set the display list pointer of the new wrapper item to the display list
// of the wrapped item.
wrapper->mListPtr = wrappedItem->mListPtr;
return wrapper;
}
nsDisplayWrapList* nsDisplayListBuilder::MergeItems(
nsTArray<nsDisplayItem*>& aItems) {
// For merging, we create a temporary item by cloning the last item of the
// mergeable items list. This ensures that the temporary item will have the
// correct frame and bounds.
nsDisplayWrapList* last = aItems.PopLastElement()->AsDisplayWrapList();
MOZ_ASSERT(last);
nsDisplayWrapList* merged = last->Clone(this);
MOZ_ASSERT(merged);
AddTemporaryItem(merged);
// Create nsDisplayWrappers that point to the internal display lists of the
// items we are merging. These nsDisplayWrappers are added to the display list
// of the temporary item.
for (nsDisplayItem* item : aItems) {
MOZ_ASSERT(item);
MOZ_ASSERT(merged->CanMerge(item));
merged->Merge(item);
MOZ_ASSERT(item->AsDisplayWrapList());
merged->GetChildren()->AppendToTop(
static_cast<nsDisplayWrapList*>(item)->CreateShallowCopy(this));
}
merged->GetChildren()->AppendToTop(last->CreateShallowCopy(this));
return merged;
}
// FIXME(emilio): This whole business should ideally not be needed at all, but
// there are a variety of hard-to-deal-with caret invalidation issues, like
// probably isn't worth chasing all them down.
void nsDisplayListBuilder::InvalidateCaretFramesIfNeeded() {
if (mPaintedCarets.IsEmpty()) {
return;
}
size_t i = mPaintedCarets.Length();
while (i--) {
nsCaret* caret = mPaintedCarets[i];
nsIFrame* oldCaret = caret->GetLastPaintedFrame();
nsRect caretRect;
nsIFrame* currentCaret = caret->GetPaintGeometry(&caretRect);
if (oldCaret == currentCaret) {
// Keep tracking this caret, it hasn't changed.
continue;
}
if (oldCaret) {
oldCaret->MarkNeedsDisplayItemRebuild();
}
if (currentCaret) {
currentCaret->MarkNeedsDisplayItemRebuild();
}
// If / when we paint this caret, we'll track it again.
caret->SetLastPaintedFrame(nullptr);
mPaintedCarets.RemoveElementAt(i);
}
}
void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
SetCurrentActiveScrolledRoot(
const ActiveScrolledRoot* aActiveScrolledRoot) {
MOZ_ASSERT(!mUsed);
// Set the builder's mCurrentActiveScrolledRoot.
mBuilder->mCurrentActiveScrolledRoot = aActiveScrolledRoot;
// We also need to adjust the builder's mCurrentContainerASR.
// mCurrentContainerASR needs to be an ASR that all the container's
// contents have finite bounds with respect to. If aActiveScrolledRoot
// is an ancestor ASR of mCurrentContainerASR, that means we need to
// set mCurrentContainerASR to aActiveScrolledRoot, because otherwise
// the items that will be created with aActiveScrolledRoot wouldn't
// have finite bounds with respect to mCurrentContainerASR. There's one
// exception, in the case where there's a content clip on the builder
// that is scrolled by a descendant ASR of aActiveScrolledRoot. This
// content clip will clip all items that are created while this
// AutoCurrentActiveScrolledRootSetter exists. This means that the items
// created during our lifetime will have finite bounds with respect to
// the content clip's ASR, even if the items' actual ASR is an ancestor
// of that. And it also means that mCurrentContainerASR only needs to be
// set to the content clip's ASR and not all the way to aActiveScrolledRoot.
// This case is tested by fixed-pos-scrolled-clip-opacity-layerize.html
// and fixed-pos-scrolled-clip-opacity-inside-layerize.html.
// finiteBoundsASR is the leafmost ASR that all items created during
// object's lifetime have finite bounds with respect to.
const ActiveScrolledRoot* finiteBoundsASR =
ActiveScrolledRoot::PickDescendant(mContentClipASR, aActiveScrolledRoot);
// mCurrentContainerASR is adjusted so that it's still an ancestor of
// finiteBoundsASR.
mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
mBuilder->mCurrentContainerASR, finiteBoundsASR);
// If we are entering out-of-flow content inside a CSS filter, mark
// scroll frames wrt. which the content is fixed as containing such content.
if (mBuilder->mFilterASR && ActiveScrolledRoot::IsAncestor(
aActiveScrolledRoot, mBuilder->mFilterASR)) {
for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
asr && asr != aActiveScrolledRoot; asr = asr->mParent) {
asr->mScrollContainerFrame->SetHasOutOfFlowContentInsideFilter();
}
}
mUsed = true;
}
void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
InsertScrollFrame(ScrollContainerFrame* aScrollContainerFrame) {
MOZ_ASSERT(!mUsed);
size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
const ActiveScrolledRoot* parentASR = mBuilder->mCurrentActiveScrolledRoot;
const ActiveScrolledRoot* asr =
mBuilder->AllocateActiveScrolledRoot(parentASR, aScrollContainerFrame);
mBuilder->mCurrentActiveScrolledRoot = asr;
// All child ASRs of parentASR that were created while this
// AutoCurrentActiveScrolledRootSetter object was on the stack belong to us
// now. Reparent them to asr.
for (size_t i = mDescendantsStartIndex; i < descendantsEndIndex; i++) {
ActiveScrolledRoot* descendantASR = mBuilder->mActiveScrolledRoots[i];
if (ActiveScrolledRoot::IsAncestor(parentASR, descendantASR)) {
descendantASR->IncrementDepth();
if (descendantASR->mParent == parentASR) {
descendantASR->mParent = asr;
}
}
}
mUsed = true;
}
nsDisplayListBuilder::AutoContainerASRTracker::AutoContainerASRTracker(
nsDisplayListBuilder* aBuilder)
: mBuilder(aBuilder), mSavedContainerASR(aBuilder->mCurrentContainerASR) {
mBuilder->mCurrentContainerASR = mBuilder->mCurrentActiveScrolledRoot;
}
nsPresContext* nsDisplayListBuilder::CurrentPresContext() {
return CurrentPresShellState()->mPresShell->GetPresContext();
}
/* static */
nsRect nsDisplayListBuilder::OutOfFlowDisplayData::ComputeVisibleRectForFrame(
nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
const nsRect& aVisibleRect, const nsRect& aDirtyRect,
nsRect* aOutDirtyRect) {
nsRect visible = aVisibleRect;
nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect;
bool inPartialUpdate =
aBuilder->IsRetainingDisplayList() && aBuilder->IsPartialUpdate();
if (StaticPrefs::apz_allow_zooming() &&
DisplayPortUtils::IsFixedPosFrameInDisplayPort(aFrame) &&
aBuilder->IsPaintingToWindow() && !inPartialUpdate) {
dirtyRectRelativeToDirtyFrame =
nsRect(nsPoint(0, 0), aFrame->GetParent()->GetSize());
// If there's a visual viewport size set, restrict the amount of the
// fixed-position element we paint to the visual viewport. (In general
// the fixed-position element can be as large as the layout viewport,
// which at a high zoom level can cause us to paint too large of an
// area.)
PresShell* presShell = aFrame->PresShell();
if (presShell->IsVisualViewportSizeSet()) {
dirtyRectRelativeToDirtyFrame =
nsRect(presShell->GetVisualViewportOffsetRelativeToLayoutViewport(),
presShell->GetVisualViewportSize());
// But if we have a displayport, expand it to the displayport, so
// that async-scrolling the visual viewport within the layout viewport
// will not checkerboard.
if (nsIFrame* rootScrollContainerFrame =
presShell->GetRootScrollContainerFrame()) {
nsRect displayport;
// Note that the displayport here is already in the right coordinate
// space: it's relative to the scroll port (= layout viewport), but
// covers the visual viewport with some margins around it, which is
// exactly what we want.
if (DisplayPortUtils::GetDisplayPort(
rootScrollContainerFrame->GetContent(), &displayport,
DisplayPortOptions().With(ContentGeometryType::Fixed))) {
dirtyRectRelativeToDirtyFrame = displayport;
}
}
}
visible = dirtyRectRelativeToDirtyFrame;
if (StaticPrefs::apz_test_logging_enabled() &&
presShell->GetDocument()->IsContentDocument()) {
nsLayoutUtils::LogAdditionalTestData(
aBuilder, "fixedPosDisplayport",
ToString(CSSSize::FromAppUnits(visible)));
}
}
*aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition();
visible -= aFrame->GetPosition();
nsRect overflowRect = aFrame->InkOverflowRect();
if (aFrame->IsTransformed() && EffectCompositor::HasAnimationsForCompositor(
aFrame, DisplayItemType::TYPE_TRANSFORM)) {
/**
* Add a fuzz factor to the overflow rectangle so that elements only
* just out of view are pulled into the display list, so they can be
* prerendered if necessary.
*/
overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32));
}
visible.IntersectRect(visible, overflowRect);
aOutDirtyRect->IntersectRect(*aOutDirtyRect, overflowRect);
return visible;
}
nsDisplayListBuilder::Linkifier::Linkifier(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame,
nsDisplayList* aList)
: mList(aList) {
// Find the element that we need to check for link-ness, bailing out if
// we can't find one.
Element* elem = Element::FromNodeOrNull(aFrame->GetContent());
if (!elem) {
return;
}
// If the element has an id and/or name attribute, generate a destination
// for possible internal linking.
auto maybeGenerateDest = [&](const nsAtom* aAttr) {
nsAutoString attrValue;
elem->GetAttr(aAttr, attrValue);
if (!attrValue.IsEmpty()) {
NS_ConvertUTF16toUTF8 dest(attrValue);
// Ensure that we only emit a given destination once, although there may
// be multiple frames associated with a given element; we'll simply use
// the first of them as the target of any links to it.
// XXX(jfkthame) This prevents emitting duplicate destinations *on the
// same page*, but does not prevent duplicates on subsequent pages, as
// each new page is handled by a new temporary DisplayListBuilder. This
// seems to be harmless in practice, though a bit wasteful of space. To
// fix, we need to maintain the set of already-seen destinations globally
// for the print job, rather than attached to the (per-page) builder.
if (aBuilder->mDestinations.EnsureInserted(dest)) {
auto* destination = MakeDisplayItem<nsDisplayDestination>(
aBuilder, aFrame, dest.get(), aFrame->GetRect().TopLeft());
mList->AppendToTop(destination);
}
}
};
if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled()) {
if (elem->HasID()) {
maybeGenerateDest(nsGkAtoms::id);
}
if (elem->HasName()) {
maybeGenerateDest(nsGkAtoms::name);
}
}
// Links don't nest, so if the builder already has a destination, no need to
// check for a link element here.
if (!aBuilder->mLinkURI.IsEmpty() || !aBuilder->mLinkDest.IsEmpty()) {
return;
}
// Check if we have actually found a link.
if (!elem->IsLink()) {
return;
}
nsCOMPtr<nsIURI> uri = elem->GetHrefURI();
if (!uri) {
return;
}
// Is it potentially a local (in-document) destination?
bool hasRef, eqExRef;
nsIURI* docURI;
if (StaticPrefs::print_save_as_pdf_internal_destinations_enabled() &&
NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef &&
(docURI = aFrame->PresContext()->Document()->GetDocumentURI()) &&
NS_SUCCEEDED(uri->EqualsExceptRef(docURI, &eqExRef)) && eqExRef) {
// Try to get a local destination name. If this fails, we'll leave the
// mLinkDest string empty, but still try to set mLinkURI below.
if (NS_FAILED(uri->GetRef(aBuilder->mLinkDest))) {
aBuilder->mLinkDest.Truncate();
}
// The destination name is simply a string; we don't want URL-escaping
// applied to it.
if (!aBuilder->mLinkDest.IsEmpty()) {
NS_UnescapeURL(aBuilder->mLinkDest);
}
}
if (NS_FAILED(uri->GetSpec(aBuilder->mLinkURI))) {
aBuilder->mLinkURI.Truncate();
}
// If we didn't get either kind of destination, we won't try to linkify at
// this level.
if (aBuilder->mLinkDest.IsEmpty() && aBuilder->mLinkURI.IsEmpty()) {
return;
}
// Record that we need to reset the builder's state on destruction.
mBuilderToReset = aBuilder;
}
void nsDisplayListBuilder::Linkifier::MaybeAppendLink(
nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
// Note that we may generate a link here even if the constructor bailed out
// without updating aBuilder->mLinkURI/Dest, because it may have been set by
// an ancestor that was associated with a link element.
if (!aBuilder->mLinkURI.IsEmpty() || !aBuilder->mLinkDest.IsEmpty()) {
auto* link = MakeDisplayItem<nsDisplayLink>(
aBuilder, aFrame, aBuilder->mLinkDest.get(), aBuilder->mLinkURI.get(),
aFrame->GetRect());
mList->AppendToTop(link);
}
}
uint32_t nsDisplayListBuilder::sPaintSequenceNumber(1);
nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
nsDisplayListBuilderMode aMode,
bool aBuildCaret,
bool aRetainingDisplayList)
: mReferenceFrame(aReferenceFrame),
mIgnoreScrollFrame(nullptr),
mCurrentActiveScrolledRoot(nullptr),
mCurrentContainerASR(nullptr),
mCurrentFrame(aReferenceFrame),
mCurrentReferenceFrame(aReferenceFrame),
mScrollInfoItemsForHoisting(nullptr),
mFirstClipChainToDestroy(nullptr),
mTableBackgroundSet(nullptr),
mCurrentScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
mCurrentScrollbarTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
mFilterASR(nullptr),
mDirtyRect(-1, -1, -1, -1),
mBuildingExtraPagesForPageNum(0),
mMode(aMode),
mContainsBlendMode(false),
mIsBuildingScrollbar(false),
mCurrentScrollbarWillHaveLayer(false),
mBuildCaret(aBuildCaret),
mRetainingDisplayList(aRetainingDisplayList),
mPartialUpdate(false),
mIgnoreSuppression(false),
mIncludeAllOutOfFlows(false),
mDescendIntoSubdocuments(true),
mSelectedFramesOnly(false),
mAllowMergingAndFlattening(true),
mInTransform(false),
mInEventsOnly(false),
mInFilter(false),
mInPageSequence(false),
mIsInChromePresContext(false),
mSyncDecodeImages(false),
mIsPaintingToWindow(false),
mUseHighQualityScaling(false),
mIsPaintingForWebRender(false),
mAncestorHasApzAwareEventHandler(false),
mHaveScrollableDisplayPort(false),
mWindowDraggingAllowed(false),
mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
mForceLayerForScrollParent(false),
mContainsNonMinimalDisplayPort(false),
mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame)),
mBuildingInvisibleItems(false),
mIsBuilding(false),
mInInvalidSubtree(false),
mDisablePartialUpdates(false),
mPartialBuildFailed(false),
mIsInActiveDocShell(false),
mBuildAsyncZoomContainer(false),
mIsRelativeToLayoutViewport(false),
mUseOverlayScrollbars(false),
mAlwaysLayerizeScrollbars(false) {
MOZ_COUNT_CTOR(nsDisplayListBuilder);
mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting();
ShouldRebuildDisplayListDueToPrefChange();
mUseOverlayScrollbars =
!!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
mAlwaysLayerizeScrollbars =
StaticPrefs::layout_scrollbars_always_layerize_track();
static_assert(
static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
"Check TYPE_MAX should not overflow");
mIsReusingStackingContextItems =
mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
}
void nsDisplayListBuilder::BeginFrame() {
nsCSSRendering::BeginFrameTreesLocked();
mIsPaintingToWindow = false;
mUseHighQualityScaling = false;
mIgnoreSuppression = false;
mInTransform = false;
mInFilter = false;
mSyncDecodeImages = false;
}
void nsDisplayListBuilder::EndFrame() {
NS_ASSERTION(!mInInvalidSubtree,
"Someone forgot to cleanup mInInvalidSubtree!");
mCurrentContainerASR = nullptr;
mActiveScrolledRoots.Clear();
FreeClipChains();
FreeTemporaryItems();
nsCSSRendering::EndFrameTreesLocked();
}
void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
const nsIFrame* aStopAtFrame) {
mFramesMarkedForDisplay.AppendElement(aFrame);
for (nsIFrame* f = aFrame; f;
f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
if (f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
return;
}
f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
if (f == aStopAtFrame) {
// we've reached a frame that we know will be painted, so we can stop.
break;
}
}
}
void nsDisplayListBuilder::AddFrameMarkedForDisplayIfVisible(nsIFrame* aFrame) {
mFramesMarkedForDisplayIfVisible.AppendElement(aFrame);
}
static void MarkFrameForDisplayIfVisibleInternal(nsIFrame* aFrame,
const nsIFrame* aStopAtFrame) {
for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
if (f->ForceDescendIntoIfVisible()) {
return;
}
f->SetForceDescendIntoIfVisible(true);
// This condition must match the condition in
// nsLayoutUtils::GetParentOrPlaceholderFor which is used by
// nsLayoutUtils::GetDisplayListParent
if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
nsIFrame* parent = f->GetParent();
if (parent && !parent->ForceDescendIntoIfVisible()) {
// If the GetDisplayListParent call is going to walk to a placeholder,
// in rare cases the placeholder might be contained in a different
// continuation from the oof. So we have to make sure to mark the oofs
// parent. In the common case this doesn't make us do any extra work,
// just changes the order in which we visit the frames since walking
// through placeholders will walk through the parent, and we stop when
// we find a ForceDescendIntoIfVisible bit set.
MarkFrameForDisplayIfVisibleInternal(parent, aStopAtFrame);
}
}
if (f == aStopAtFrame) {
// we've reached a frame that we know will be painted, so we can stop.
break;
}
}
}
void nsDisplayListBuilder::MarkFrameForDisplayIfVisible(
nsIFrame* aFrame, const nsIFrame* aStopAtFrame) {
AddFrameMarkedForDisplayIfVisible(aFrame);
MarkFrameForDisplayIfVisibleInternal(aFrame, aStopAtFrame);
}
void nsDisplayListBuilder::SetIsRelativeToLayoutViewport() {
mIsRelativeToLayoutViewport = true;
UpdateShouldBuildAsyncZoomContainer();
}
void nsDisplayListBuilder::ForceLayerForScrollParent() {
mForceLayerForScrollParent = true;
mNumActiveScrollframesEncountered++;
}
void nsDisplayListBuilder::UpdateShouldBuildAsyncZoomContainer() {
const Document* document = mReferenceFrame->PresContext()->Document();
mBuildAsyncZoomContainer = !mIsRelativeToLayoutViewport &&
!document->Fullscreen() &&
nsLayoutUtils::AllowZoomingForDocument(document);
// If mIsRelativeToLayoutViewport == false, hit-testing on this
// display list will take into account the pres shell resolution.
// If we're not building an async zoom container (meaning, the
// resolution will not take effect visually), the resolution better
// be 1.0, otherwise rendering and hit-testing are out of sync.
#ifdef DEBUG
if (!mIsRelativeToLayoutViewport && !mBuildAsyncZoomContainer) {
MOZ_ASSERT(document->GetPresShell()->GetResolution() == 1.0f);
}
#endif
}
// Certain prefs may cause display list items to be added or removed when they
// are toggled. In those cases, we need to fully rebuild the display list.
bool nsDisplayListBuilder::ShouldRebuildDisplayListDueToPrefChange() {
// If we transition between wrapping the RCD-RSF contents into an async
// zoom container vs. not, we need to rebuild the display list. This only
// happens when the zooming or container scrolling prefs are toggled
// (manually by the user, or during test setup).
bool didBuildAsyncZoomContainer = mBuildAsyncZoomContainer;
UpdateShouldBuildAsyncZoomContainer();
bool hadOverlayScrollbarsLastTime = mUseOverlayScrollbars;
mUseOverlayScrollbars =
!!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
bool alwaysLayerizedScrollbarsLastTime = mAlwaysLayerizeScrollbars;
mAlwaysLayerizeScrollbars =
StaticPrefs::layout_scrollbars_always_layerize_track();
if (didBuildAsyncZoomContainer != mBuildAsyncZoomContainer) {
return true;
}
if (hadOverlayScrollbarsLastTime != mUseOverlayScrollbars) {
return true;
}
if (alwaysLayerizedScrollbarsLastTime != mAlwaysLayerizeScrollbars) {
return true;
}
return false;
}
void nsDisplayListBuilder::AddScrollContainerFrameToNotify(
ScrollContainerFrame* aScrollContainerFrame) {
mScrollContainerFramesToNotify.insert(aScrollContainerFrame);
}
void nsDisplayListBuilder::NotifyAndClearScrollContainerFrames() {
for (const auto& it : mScrollContainerFramesToNotify) {
it->NotifyApzTransaction();
}
mScrollContainerFramesToNotify.clear();
}
bool nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(
nsIFrame* aDirtyFrame, nsIFrame* aFrame, const nsRect& aVisibleRect,
const nsRect& aDirtyRect) {
MOZ_ASSERT(aFrame->GetParent() == aDirtyFrame);
nsRect dirty;
nsRect visible = OutOfFlowDisplayData::ComputeVisibleRectForFrame(
this, aFrame, aVisibleRect, aDirtyRect, &dirty);
if (!aFrame->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
visible.IsEmpty()) {
return false;
}
// Only MarkFrameForDisplay if we're dirty. If this is a nested out-of-flow
// frame, then it will also mark any outer frames to ensure that building
// reaches the dirty feame.
if (!dirty.IsEmpty() || aFrame->ForceDescendIntoIfVisible()) {
MarkFrameForDisplay(aFrame, aDirtyFrame);
}
return true;
}
static void UnmarkFrameForDisplay(nsIFrame* aFrame,
const nsIFrame* aStopAtFrame) {
for (nsIFrame* f = aFrame; f;
f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
if (!f->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
return;
}
f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
if (f == aStopAtFrame) {
// we've reached a frame that we know will be painted, so we can stop.
break;
}
}
}
static void UnmarkFrameForDisplayIfVisible(nsIFrame* aFrame) {
for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetDisplayListParent(f)) {
if (!f->ForceDescendIntoIfVisible()) {
return;
}
f->SetForceDescendIntoIfVisible(false);
// This condition must match the condition in
// nsLayoutUtils::GetParentOrPlaceholderFor which is used by
// nsLayoutUtils::GetDisplayListParent
if (f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && !f->GetPrevInFlow()) {
nsIFrame* parent = f->GetParent();
if (parent && parent->ForceDescendIntoIfVisible()) {
// If the GetDisplayListParent call is going to walk to a placeholder,
// in rare cases the placeholder might be contained in a different
// continuation from the oof. So we have to make sure to mark the oofs
// parent. In the common case this doesn't make us do any extra work,
// just changes the order in which we visit the frames since walking
// through placeholders will walk through the parent, and we stop when
// we find a ForceDescendIntoIfVisible bit set.
UnmarkFrameForDisplayIfVisible(f);
}
}
}
}
nsDisplayListBuilder::~nsDisplayListBuilder() {
NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0,
"All frames should have been unmarked");
NS_ASSERTION(mFramesWithOOFData.Length() == 0,
"All OOF data should have been removed");
NS_ASSERTION(mPresShellStates.Length() == 0,
"All presshells should have been exited");
DisplayItemClipChain* c = mFirstClipChainToDestroy;
while (c) {
DisplayItemClipChain* next = c->mNextClipChainToDestroy;
c->DisplayItemClipChain::~DisplayItemClipChain();
c = next;
}
MOZ_COUNT_DTOR(nsDisplayListBuilder);
}
uint32_t nsDisplayListBuilder::GetBackgroundPaintFlags() {
uint32_t flags = 0;
if (mSyncDecodeImages) {
flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES;
}
if (mIsPaintingToWindow) {
flags |= nsCSSRendering::PAINTBG_TO_WINDOW;
}
if (mUseHighQualityScaling) {
flags |= nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING;
}
return flags;
}
// TODO(emilio): Maybe unify BackgroundPaintFlags and IamgeRendererFlags.
uint32_t nsDisplayListBuilder::GetImageRendererFlags() const {
uint32_t flags = 0;
if (mSyncDecodeImages) {
flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
}
if (mIsPaintingToWindow) {
flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
}
if (mUseHighQualityScaling) {
flags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING;
}
return flags;
}
uint32_t nsDisplayListBuilder::GetImageDecodeFlags() const {
uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
if (mSyncDecodeImages) {
flags |= imgIContainer::FLAG_SYNC_DECODE;
} else {
flags |= imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
}
if (mIsPaintingToWindow || mUseHighQualityScaling) {
flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
}
return flags;
}
nsCaret* nsDisplayListBuilder::GetCaret() {
RefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
return caret;
}
void nsDisplayListBuilder::IncrementPresShellPaintCount(PresShell* aPresShell) {
if (mIsPaintingToWindow) {
aPresShell->IncrementPaintCount();
}
}
void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame,
bool aPointerEventsNoneDoc) {
PresShellState* state = mPresShellStates.AppendElement();
state->mPresShell = aReferenceFrame->PresShell();
state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
ScrollContainerFrame* sf = state->mPresShell->GetRootScrollContainerFrame();
if (sf && IsInSubdocument()) {
// We are forcing a rebuild of nsDisplayCanvasBackgroundColor to make sure
// that the canvas background color will be set correctly, and that only one
// unscrollable item will be created.
// This is done to avoid, for example, a case where only scrollbar frames
// are invalidated - we would skip creating nsDisplayCanvasBackgroundColor
// and possibly end up with an extra nsDisplaySolidColor item.
// We skip this for the root document, since we don't want to use
// MarkFrameForDisplayIfVisible before ComputeRebuildRegion. We'll
// do it manually there.
nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
if (canvasFrame) {
MarkFrameForDisplayIfVisible(canvasFrame, aReferenceFrame);
}
}
#ifdef DEBUG
state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(),
nsLayoutPhase::DisplayListBuilding);
#endif
state->mPresShell->UpdateCanvasBackground();
bool buildCaret = mBuildCaret;
if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) {
state->mIsBackgroundOnly = false;
} else {
state->mIsBackgroundOnly = true;
buildCaret = false;
}
bool pointerEventsNone = aPointerEventsNoneDoc;
if (IsInSubdocument()) {
pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2]
.mInsidePointerEventsNoneDoc;
}
state->mInsidePointerEventsNoneDoc = pointerEventsNone;
state->mPresShellIgnoreScrollFrame =
state->mPresShell->IgnoringViewportScrolling()
? state->mPresShell->GetRootScrollContainerFrame()
: nullptr;
nsPresContext* pc = aReferenceFrame->PresContext();
mIsInChromePresContext = pc->IsChrome();
nsIDocShell* docShell = pc->GetDocShell();
if (docShell) {
docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
}
state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell);
if (!buildCaret) {
return;
}
state->mCaretFrame = [&]() -> nsIFrame* {
RefPtr<nsCaret> caret = state->mPresShell->GetCaret();
nsIFrame* currentCaret = caret->GetPaintGeometry(&mCaretRect);
if (!currentCaret) {
return nullptr;
}
// Check if the display root for the caret matches the display root that
// we're painting, and only use it if it matches. Likely we only need this
// for carets inside popups.
if (nsLayoutUtils::GetDisplayRootFrame(currentCaret) !=
nsLayoutUtils::GetDisplayRootFrame(aReferenceFrame)) {
return nullptr;
}
// Caret frames add visual area to their frame, but we don't update the
// overflow area. Use flags to make sure we build display items for that
// frame instead.
MOZ_ASSERT(currentCaret->PresShell() == state->mPresShell);
MarkFrameForDisplay(currentCaret, aReferenceFrame);
caret->SetLastPaintedFrame(currentCaret);
if (!mPaintedCarets.Contains(caret)) {
mPaintedCarets.AppendElement(std::move(caret));
}
return currentCaret;
}();
}
// A non-blank paint is a paint that does not just contain the canvas
// background.
static bool DisplayListIsNonBlank(nsDisplayList* aList) {
for (nsDisplayItem* i : *aList) {
switch (i->GetType()) {
case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO:
case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR:
case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE:
continue;
case DisplayItemType::TYPE_SOLID_COLOR:
case DisplayItemType::TYPE_BACKGROUND:
case DisplayItemType::TYPE_BACKGROUND_COLOR:
if (i->Frame()->IsCanvasFrame()) {
continue;
}
return true;
default:
return true;
}
}
return false;
}
// A contentful paint is a paint that does contains DOM content (text,
// images, non-blank canvases, SVG): "First Contentful Paint entry
// contains a DOMHighResTimeStamp reporting the time when the browser
// first rendered any text, image (including background images),
// non-white canvas or SVG. This excludes any content of iframes, but
// includes text with pending webfonts. This is the first time users
// could start consuming page content."
static bool DisplayListIsContentful(nsDisplayListBuilder* aBuilder,
nsDisplayList* aList) {
for (nsDisplayItem* i : *aList) {
DisplayItemType type = i->GetType();
nsDisplayList* children = i->GetChildren();
switch (type) {
case DisplayItemType::TYPE_SUBDOCUMENT: // iframes are ignored
break;
// CANVASes check if they may have been modified (as a stand-in
// actually tracking all modifications)
default:
if (i->IsContentful()) {
bool dummy;
nsRect bound = i->GetBounds(aBuilder, &dummy);
if (!bound.IsEmpty()) {
return true;
}
}
if (children) {
if (DisplayListIsContentful(aBuilder, children)) {
return true;
}
}
break;
}
}
return false;
}
void nsDisplayListBuilder::LeavePresShell(const nsIFrame* aReferenceFrame,
nsDisplayList* aPaintedContents) {
NS_ASSERTION(
CurrentPresShellState()->mPresShell == aReferenceFrame->PresShell(),
"Presshell mismatch");
if (mIsPaintingToWindow && aPaintedContents) {
nsPresContext* pc = aReferenceFrame->PresContext();
if (!pc->HadNonBlankPaint()) {
if (!CurrentPresShellState()->mIsBackgroundOnly &&
DisplayListIsNonBlank(aPaintedContents)) {
pc->NotifyNonBlankPaint();
}
}
nsRootPresContext* rootPresContext = pc->GetRootPresContext();
if (!pc->HasStoppedGeneratingLCP() && rootPresContext) {
if (!CurrentPresShellState()->mIsBackgroundOnly) {
if (pc->HasEverBuiltInvisibleText() ||
DisplayListIsContentful(this, aPaintedContents)) {
pc->NotifyContentfulPaint();
}
}
}
}
ResetMarkedFramesForDisplayList(aReferenceFrame);
mPresShellStates.RemoveLastElement();
if (!mPresShellStates.IsEmpty()) {
nsPresContext* pc = CurrentPresContext();
nsIDocShell* docShell = pc->GetDocShell();
if (docShell) {
docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed);
}
mIsInChromePresContext = pc->IsChrome();
} else {
for (uint32_t i = 0; i < mFramesMarkedForDisplayIfVisible.Length(); ++i) {
UnmarkFrameForDisplayIfVisible(mFramesMarkedForDisplayIfVisible[i]);
}
mFramesMarkedForDisplayIfVisible.SetLength(0);
}
}
void nsDisplayListBuilder::FreeClipChains() {
// Iterate the clip chains from newest to oldest (forward
// iteration), so that we destroy descendants first which
// will drop the ref count on their ancestors.
DisplayItemClipChain** indirect = &mFirstClipChainToDestroy;
while (*indirect) {
if (!(*indirect)->mRefCount) {
DisplayItemClipChain* next = (*indirect)->mNextClipChainToDestroy;
mClipDeduplicator.erase(*indirect);
(*indirect)->DisplayItemClipChain::~DisplayItemClipChain();
Destroy(DisplayListArenaObjectId::CLIPCHAIN, *indirect);
*indirect = next;
} else {
indirect = &(*indirect)->mNextClipChainToDestroy;
}
}
}
void nsDisplayListBuilder::FreeTemporaryItems() {
for (nsDisplayItem* i : mTemporaryItems) {
// Temporary display items are not added to the frames.
MOZ_ASSERT(i->Frame());
i->RemoveFrame(i->Frame());
i->Destroy(this);
}
mTemporaryItems.Clear();
}
void nsDisplayListBuilder::ResetMarkedFramesForDisplayList(
const nsIFrame* aReferenceFrame) {
// Unmark and pop off the frames marked for display in this pres shell.
uint32_t firstFrameForShell =
CurrentPresShellState()->mFirstFrameMarkedForDisplay;
for (uint32_t i = firstFrameForShell; i < mFramesMarkedForDisplay.Length();
++i) {
UnmarkFrameForDisplay(mFramesMarkedForDisplay[i], aReferenceFrame);
}
mFramesMarkedForDisplay.SetLength(firstFrameForShell);
firstFrameForShell = CurrentPresShellState()->mFirstFrameWithOOFData;
for (uint32_t i = firstFrameForShell; i < mFramesWithOOFData.Length(); ++i) {
mFramesWithOOFData[i]->RemoveProperty(OutOfFlowDisplayDataProperty());
}
mFramesWithOOFData.SetLength(firstFrameForShell);
}
void nsDisplayListBuilder::ClearFixedBackgroundDisplayData() {
CurrentPresShellState()->mFixedBackgroundDisplayData = Nothing();
}
void nsDisplayListBuilder::MarkFramesForDisplayList(
nsIFrame* aDirtyFrame, const nsFrameList& aFrames) {
nsRect visibleRect = GetVisibleRect();
nsRect dirtyRect = GetDirtyRect();
// If we are entering content that is fixed to the RCD-RSF, we are
// crossing the async zoom container boundary, and need to convert from
// visual to layout coordinates.
if (ViewportFrame* viewportFrame = do_QueryFrame(aDirtyFrame)) {
if (IsForEventDelivery() && ShouldBuildAsyncZoomContainer() &&
viewportFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
if (viewportFrame->PresShell()->GetRootScrollContainerFrame()) {
#ifdef DEBUG
for (nsIFrame* f : aFrames) {
MOZ_ASSERT(ViewportUtils::IsZoomedContentRoot(f));
}
#endif
visibleRect = ViewportUtils::VisualToLayout(visibleRect,
viewportFrame->PresShell());
dirtyRect = ViewportUtils::VisualToLayout(dirtyRect,
viewportFrame->PresShell());
}
#ifdef DEBUG
else {
// This is an edge case that should only happen if we are in a
// document with a XUL root element so that it does not have a root
// scroll frame but it has fixed pos content and all of the frames in
// aFrames are that fixed pos content.
for (nsIFrame* f : aFrames) {
MOZ_ASSERT(!ViewportUtils::IsZoomedContentRoot(f) &&
f->GetParent() == aDirtyFrame &&
f->StyleDisplay()->mPosition ==
StylePositionProperty::Fixed);
}
// There's no root scroll frame so there can't be any zooming or async
// panning so we don't need to adjust the visible and dirty rects.
}
#endif
}
}
bool markedFrames = false;
for (nsIFrame* e : aFrames) {
// Skip the AccessibleCaret frame when building no caret.
if (!IsBuildingCaret()) {
nsIContent* content = e->GetContent();
if (content && content->IsInNativeAnonymousSubtree() &&
content->IsElement()) {
const nsAttrValue* classes = content->AsElement()->GetClasses();
if (classes &&
classes->Contains(nsGkAtoms::mozAccessiblecaret, eCaseMatters)) {
continue;
}
}
}
if (MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, visibleRect, dirtyRect)) {
markedFrames = true;
}
}
if (markedFrames) {
// mClipState.GetClipChainForContainingBlockDescendants can return pointers
// to objects on the stack, so we need to clone the chain.
const DisplayItemClipChain* clipChain =
CopyWholeChain(mClipState.GetClipChainForContainingBlockDescendants());
const DisplayItemClipChain* combinedClipChain =
mClipState.GetCurrentCombinedClipChain(this);
const ActiveScrolledRoot* asr = mCurrentActiveScrolledRoot;
OutOfFlowDisplayData* data = new OutOfFlowDisplayData(
clipChain, combinedClipChain, asr, this->mCurrentScrollParentId,
visibleRect, dirtyRect);
aDirtyFrame->SetProperty(
nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
mFramesWithOOFData.AppendElement(aDirtyFrame);
}