/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* * structures that represent things to be painted (ordered in z-order), * used during painting and hit testing
*/
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;
}
// 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);
} elseif (animationsId) {
aManager->AddCompositorAnimationsIdForDiscard(animationsId);
animationsId = 0;
}
// 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.
// 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));
}
// 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 // bug 1888583, and caret changes are relatively uncommon, enough that it // 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();
nsIFrame* currentCaret = caret->GetPaintGeometry(); 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);
}
}
// 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();
}
}
// 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;
}
}
}
// 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)));
}
}
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));
}
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);
}
}
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;
}
}
}
staticvoid 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;
}
}
}
// 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();
// 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);
}
returntrue;
}
staticvoid 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;
}
}
}
staticvoid 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;
}
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);
}
}
// 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. staticbool 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;
} returntrue; default: returntrue;
}
} returnfalse;
}
// 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." staticbool 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()) { returntrue;
}
} if (children) { if (DisplayListIsContentful(aBuilder, children)) { returntrue;
}
} break;
}
} returnfalse;
}
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;
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);
}
// 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);
}
if (!aDirtyFrame->GetParent()) { // This is the viewport frame of aDirtyFrame's presshell. // Store the current display data so that it can be used for fixed // background images.
NS_ASSERTION(
CurrentPresShellState()->mPresShell == aDirtyFrame->PresShell(), "Presshell mismatch");
MOZ_ASSERT(!CurrentPresShellState()->mFixedBackgroundDisplayData, "already traversed this presshell's root frame?");
/** * Mark all preserve-3d children with * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure * nsIFrame::BuildDisplayListForChild() would visit them. Also compute * dirty rect for preserve-3d children. * * @param aDirtyFrame is the frame to mark children extending context.
*/ void nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(
nsIFrame* aDirtyFrame) { for (constauto& childList : aDirtyFrame->ChildLists()) { for (nsIFrame* child : childList.mList) { if (child->Combines3DTransformWithAncestors()) {
MarkFrameForDisplay(child, aDirtyFrame);
}
if (child->IsBlockWrapper()) { // Mark preserve-3d frames inside the block wrapper.
MarkPreserve3DFramesForDisplayList(child);
}
}
}
}
const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain( const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR, const DisplayItemClipChain* aParent) {
MOZ_DIAGNOSTIC_ASSERT(!(aParent && aParent->mOnStack)); void* p = Allocate(sizeof(DisplayItemClipChain),
DisplayListArenaObjectId::CLIPCHAIN);
DisplayItemClipChain* c = new (KnownNotNull, p)
DisplayItemClipChain(aClip, aASR, aParent, mFirstClipChainToDestroy); #ifdefined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
c->mOnStack = false; #endif auto result = mClipDeduplicator.insert(c); if (!result.second) { // An equivalent clip chain item was already created, so let's return that // instead. Destroy the one we just created. // Note that this can cause clip chains from different coordinate systems to // collapse into the same clip chain object, because clip chains do not keep // track of the reference frame that they were created in.
c->DisplayItemClipChain::~DisplayItemClipChain();
Destroy(DisplayListArenaObjectId::CLIPCHAIN, c); return *(result.first);
}
mFirstClipChainToDestroy = c; return c;
}
// Build up the intersection from the leaf to the root and put it into // intersectedClips. The loop below will convert intersectedClips into an // actual DisplayItemClipChain. // (We need to do this in two passes because we need the parent clip in order // to create the DisplayItemClipChain object, but the parent clip has not // been created at that point.) while (!aAncestor || asr != aAncestor->mASR) { if (clip1 && clip1->mASR == asr) { if (clip2 && clip2->mASR == asr) {
DisplayItemClip intersection = clip1->mClip;
intersection.IntersectWith(clip2->mClip);
intersectedClips.AppendElement(ClipChainItem{intersection, asr});
clip2 = clip2->mParent;
} else {
intersectedClips.AppendElement(ClipChainItem{clip1->mClip, asr});
}
clip1 = clip1->mParent;
} elseif (clip2 && clip2->mASR == asr) {
intersectedClips.AppendElement(ClipChainItem{clip2->mClip, asr});
clip2 = clip2->mParent;
} if (!asr) {
MOZ_ASSERT(!aAncestor, "We should have exited this loop earlier"); break;
}
asr = asr->mParent;
}
// Convert intersectedClips into a DisplayItemClipChain. const DisplayItemClipChain* parentSC = aAncestor; for (auto& sc : Reversed(intersectedClips)) {
parentSC = AllocateDisplayItemClipChain(sc.clip, sc.asr, parentSC);
} return parentSC;
}
const DisplayItemClipChain* nsDisplayListBuilder::CreateClipChainIntersection( const DisplayItemClipChain* aLeafClip1, const DisplayItemClipChain* aLeafClip2) { // aLeafClip2 might be a reference to a clip on the stack. We need to make // sure that CreateClipChainIntersection will allocate the actual intersected // clip in the builder's arena, so for the aLeafClip1 == nullptr case, // we supply nullptr as the common ancestor so that // CreateClipChainIntersection clones the whole chain. const DisplayItemClipChain* ancestorClip =
aLeafClip1 ? FindCommonAncestorClipForIntersection(aLeafClip1, aLeafClip2)
: nullptr;
if (aFrame == mCurrentFrame) { if (aOffset) {
*aOffset = mCurrentOffsetToReferenceFrame;
} return mCurrentReferenceFrame;
}
for (const nsIFrame* f = aFrame; f;
f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) { if (f == mReferenceFrame || f->IsTransformed()) { if (aOffset) {
*aOffset = aFrame->GetOffsetToCrossDoc(f);
MaybeApplyAdditionalOffset();
} return f;
}
}
if (aOffset) {
*aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
MaybeApplyAdditionalOffset();
}
return mReferenceFrame;
}
// Sticky frames are active if their nearest scrollable frame is also active. staticbool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame) {
MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
StylePositionProperty::Sticky);
const nsStyleUIReset* styleUI = aFrame->StyleUIReset(); if (styleUI->mWindowDragging == StyleWindowDragging::Default) { // This frame has the default value and doesn't influence the window // dragging region. return;
}
// The const_cast is for nsLayoutUtils::GetTransformToAncestor.
nsIFrame* referenceFrame = const_cast<nsIFrame*>(FindReferenceFrameFor(aFrame));
if (IsInTransform()) { // Only support 2d rectilinear transforms. Transform support is needed for // the horizontal flip transform that's applied to the urlbar textbox in // RTL mode - it should be able to exclude itself from the draggable region.
referenceFrameToRootReferenceFrame =
ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(
nsLayoutUtils::GetTransformToAncestor(RelativeTo{referenceFrame},
RelativeTo{mReferenceFrame})
.GetMatrix());
Matrix referenceFrameToRootReferenceFrame2d; if (!referenceFrameToRootReferenceFrame.Is2D(
&referenceFrameToRootReferenceFrame2d) ||
!referenceFrameToRootReferenceFrame2d.IsRectilinear()) { return;
}
} else {
MOZ_ASSERT(referenceFrame == mReferenceFrame, "referenceFrameToRootReferenceFrame needs to be adjusted");
}
// We do some basic visibility checking on the frame's border box here. // We intersect it both with the current dirty rect and with the current // clip. Either one is just a conservative approximation on its own, but // their intersection luckily works well enough for our purposes, so that // we don't have to do full-blown visibility computations. // The most important case we need to handle is the scrolled-off tab: // If the tab bar overflows, tab parts that are clipped by the scrollbox // should not be allowed to interfere with the window dragging region. Using // just the current DisplayItemClip is not enough to cover this case // completely because clips are reset while building stacking context // contents, so for example we'd fail to clip frames that have a clip path // applied to them. But the current dirty rect doesn't get reset in that // case, so we use it to make this case work.
nsRect borderBox = aFrame->GetRectRelativeToSelf().Intersect(mVisibleRect);
borderBox += ToReferenceFrame(aFrame); const DisplayItemClipChain* clip =
ClipState().GetCurrentCombinedClipChain(this);
borderBox = ApplyAllClipNonRoundedIntersection(clip, borderBox); if (borderBox.IsEmpty()) { return;
}
size_t n = 0;
MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
n += mDocumentWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
n += mFrameWillChangeBudgets.ShallowSizeOfExcludingThis(mallocSizeOf);
n += mRetainedWindowDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
n += mRetainedWindowNoDraggingRegion.SizeOfExcludingThis(mallocSizeOf);
n += mRetainedWindowOpaqueRegion.SizeOfExcludingThis(mallocSizeOf); // XXX can't measure mClipDeduplicator since it uses std::unordered_set.
aSizes.mLayoutRetainedDisplayListSize += n;
}
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.53 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.