Bug 1697979 - Part 1: Reuse previously built stacking context display items without merging r=mstange
Differential Revision: https://phabricator.services.mozilla.com/D128413
This commit is contained in:
@@ -586,6 +586,12 @@ void DisplayPortUtils::InvalidateForDisplayPortChange(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StaticPrefs::layout_display_list_retain_sc()) {
|
||||||
|
// DisplayListBuildingDisplayPortRect property is not used when retain sc
|
||||||
|
// mode is enabled.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool found;
|
bool found;
|
||||||
nsRect* rect = frame->GetProperty(
|
nsRect* rect = frame->GetProperty(
|
||||||
nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), &found);
|
nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), &found);
|
||||||
|
|||||||
@@ -116,6 +116,24 @@ static void PrintDisplayItemTo(nsDisplayListBuilder* aBuilder,
|
|||||||
area.width, area.height);
|
area.width, area.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto ReuseStateToString = [](nsDisplayItem::ReuseState aState) {
|
||||||
|
switch (aState) {
|
||||||
|
case nsDisplayItem::ReuseState::None:
|
||||||
|
return "None";
|
||||||
|
case nsDisplayItem::ReuseState::Reusable:
|
||||||
|
return "Reusable";
|
||||||
|
case nsDisplayItem::ReuseState::PreProcessed:
|
||||||
|
return "PreProcessed";
|
||||||
|
case nsDisplayItem::ReuseState::Reused:
|
||||||
|
return "Reused";
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT_UNREACHABLE();
|
||||||
|
};
|
||||||
|
|
||||||
|
aStream << nsPrintfCString(" reuse-state(%s)",
|
||||||
|
ReuseStateToString(aItem->GetReuseState()));
|
||||||
|
|
||||||
// Display item specific debug info
|
// Display item specific debug info
|
||||||
aItem->WriteDebugInfo(aStream);
|
aItem->WriteDebugInfo(aStream);
|
||||||
|
|
||||||
|
|||||||
@@ -3130,15 +3130,6 @@ void nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
|
|||||||
|
|
||||||
MOZ_ASSERT(builder && list && metrics);
|
MOZ_ASSERT(builder && list && metrics);
|
||||||
|
|
||||||
// Retained builder exists, but display list retaining is disabled.
|
|
||||||
if (!useRetainedBuilder && retainedBuilder) {
|
|
||||||
// Clear the modified frames lists and frame properties.
|
|
||||||
retainedBuilder->ClearFramesWithProps();
|
|
||||||
|
|
||||||
// Clear the retained display list.
|
|
||||||
retainedBuilder->List()->DeleteAll(retainedBuilder->Builder());
|
|
||||||
}
|
|
||||||
|
|
||||||
metrics->Reset();
|
metrics->Reset();
|
||||||
metrics->StartBuild();
|
metrics->StartBuild();
|
||||||
|
|
||||||
@@ -3320,15 +3311,11 @@ void nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
|
|||||||
// If a pref is toggled that adds or removes display list items,
|
// If a pref is toggled that adds or removes display list items,
|
||||||
// we need to rebuild the display list. The pref may be toggled
|
// we need to rebuild the display list. The pref may be toggled
|
||||||
// manually by the user, or during test setup.
|
// manually by the user, or during test setup.
|
||||||
bool shouldAttemptPartialUpdate = useRetainedBuilder;
|
if (useRetainedBuilder &&
|
||||||
if (builder->ShouldRebuildDisplayListDueToPrefChange()) {
|
!builder->ShouldRebuildDisplayListDueToPrefChange()) {
|
||||||
shouldAttemptPartialUpdate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to do a partial build and merge into the existing list.
|
// Attempt to do a partial build and merge into the existing list.
|
||||||
// This calls BuildDisplayListForStacking context on a subset of the
|
// This calls BuildDisplayListForStacking context on a subset of the
|
||||||
// viewport.
|
// viewport.
|
||||||
if (shouldAttemptPartialUpdate) {
|
|
||||||
updateState = retainedBuilder->AttemptPartialUpdate(aBackstop);
|
updateState = retainedBuilder->AttemptPartialUpdate(aBackstop);
|
||||||
metrics->EndPartialBuild(updateState);
|
metrics->EndPartialBuild(updateState);
|
||||||
} else {
|
} else {
|
||||||
@@ -3349,16 +3336,25 @@ void nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (doFullRebuild) {
|
if (doFullRebuild) {
|
||||||
DL_LOGI("Starting full display list build, root frame: %p",
|
|
||||||
builder->RootReferenceFrame());
|
|
||||||
list->DeleteAll(builder);
|
list->DeleteAll(builder);
|
||||||
list->RestoreState();
|
list->RestoreState();
|
||||||
|
|
||||||
|
if (useRetainedBuilder) {
|
||||||
|
retainedBuilder->ClearFramesWithProps();
|
||||||
|
mozilla::RDLUtils::AssertFrameSubtreeUnmodified(
|
||||||
|
builder->RootReferenceFrame());
|
||||||
|
MOZ_ASSERT(retainedBuilder->List()->IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
builder->ClearRetainedWindowRegions();
|
builder->ClearRetainedWindowRegions();
|
||||||
builder->ClearWillChangeBudgets();
|
builder->ClearWillChangeBudgets();
|
||||||
|
|
||||||
builder->EnterPresShell(aFrame);
|
builder->EnterPresShell(aFrame);
|
||||||
builder->SetDirtyRect(visibleRect);
|
builder->SetDirtyRect(visibleRect);
|
||||||
|
|
||||||
|
DL_LOGI("Starting full display list build, root frame: %p",
|
||||||
|
builder->RootReferenceFrame());
|
||||||
|
|
||||||
aFrame->BuildDisplayListForStackingContext(builder, list);
|
aFrame->BuildDisplayListForStackingContext(builder, list);
|
||||||
AddExtraBackgroundItems(builder, list, aFrame, canvasArea,
|
AddExtraBackgroundItems(builder, list, aFrame, canvasArea,
|
||||||
visibleRegion, aBackstop);
|
visibleRegion, aBackstop);
|
||||||
|
|||||||
@@ -4488,23 +4488,25 @@ bool ScrollFrameHelper::DecideScrollableLayer(
|
|||||||
content, &displayPort,
|
content, &displayPort,
|
||||||
DisplayPortOptions().With(DisplayportRelativeTo::ScrollFrame));
|
DisplayPortOptions().With(DisplayportRelativeTo::ScrollFrame));
|
||||||
|
|
||||||
if (usingDisplayPort) {
|
auto OverrideDirtyRect = [&](const nsRect& aRect) {
|
||||||
// Override the dirty rectangle if the displayport has been set.
|
*aDirtyRect = aRect;
|
||||||
*aVisibleRect = displayPort;
|
|
||||||
if (!aBuilder->IsPartialUpdate() || aBuilder->InInvalidSubtree() ||
|
|
||||||
mOuter->IsFrameModified()) {
|
|
||||||
*aDirtyRect = displayPort;
|
|
||||||
if (aDirtyRectHasBeenOverriden) {
|
if (aDirtyRectHasBeenOverriden) {
|
||||||
*aDirtyRectHasBeenOverriden = true;
|
*aDirtyRectHasBeenOverriden = true;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (usingDisplayPort) {
|
||||||
|
// Override the dirty rectangle if the displayport has been set.
|
||||||
|
*aVisibleRect = displayPort;
|
||||||
|
if (aBuilder->IsReusingStackingContextItems() ||
|
||||||
|
!aBuilder->IsPartialUpdate() || aBuilder->InInvalidSubtree() ||
|
||||||
|
mOuter->IsFrameModified()) {
|
||||||
|
OverrideDirtyRect(displayPort);
|
||||||
} else if (mOuter->HasOverrideDirtyRegion()) {
|
} else if (mOuter->HasOverrideDirtyRegion()) {
|
||||||
nsRect* rect = mOuter->GetProperty(
|
nsRect* rect = mOuter->GetProperty(
|
||||||
nsDisplayListBuilder::DisplayListBuildingDisplayPortRect());
|
nsDisplayListBuilder::DisplayListBuildingDisplayPortRect());
|
||||||
if (rect) {
|
if (rect) {
|
||||||
*aDirtyRect = *rect;
|
OverrideDirtyRect(*rect);
|
||||||
if (aDirtyRectHasBeenOverriden) {
|
|
||||||
*aDirtyRectHasBeenOverriden = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (mIsRoot) {
|
} else if (mIsRoot) {
|
||||||
|
|||||||
@@ -1043,7 +1043,7 @@ void nsIFrame::RemoveDisplayItemDataForDeletion() {
|
|||||||
GetFrameName(name);
|
GetFrameName(name);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
DL_LOGD("Removing display item data for frame %p (%s)", this,
|
DL_LOGV("Removing display item data for frame %p (%s)", this,
|
||||||
NS_ConvertUTF16toUTF8(name).get());
|
NS_ConvertUTF16toUTF8(name).get());
|
||||||
|
|
||||||
// Destroying a WebRenderUserDataTable can cause destruction of other objects
|
// Destroying a WebRenderUserDataTable can cause destruction of other objects
|
||||||
@@ -1126,15 +1126,6 @@ void nsIFrame::MarkNeedsDisplayItemRebuild() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAutoString name;
|
|
||||||
#ifdef DEBUG_FRAME_DUMP
|
|
||||||
if (DL_LOG_TEST(LogLevel::Debug)) {
|
|
||||||
GetFrameName(name);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
DL_LOGD("RDL - Rebuilding display items for frame %p (%s)", this,
|
|
||||||
NS_ConvertUTF16toUTF8(name).get());
|
|
||||||
|
|
||||||
nsIFrame* rootFrame = PresShell()->GetRootFrame();
|
nsIFrame* rootFrame = PresShell()->GetRootFrame();
|
||||||
MOZ_ASSERT(rootFrame);
|
MOZ_ASSERT(rootFrame);
|
||||||
|
|
||||||
@@ -1142,8 +1133,17 @@ void nsIFrame::MarkNeedsDisplayItemRebuild() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
|
nsAutoString name;
|
||||||
|
#ifdef DEBUG_FRAME_DUMP
|
||||||
|
if (DL_LOG_TEST(LogLevel::Debug)) {
|
||||||
|
GetFrameName(name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DL_LOGV("RDL - Rebuilding display items for frame %p (%s)", this,
|
||||||
|
NS_ConvertUTF16toUTF8(name).get());
|
||||||
|
|
||||||
|
RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
|
||||||
if (data->ModifiedFramesCount() >
|
if (data->ModifiedFramesCount() >
|
||||||
StaticPrefs::layout_display_list_rebuild_frame_limit()) {
|
StaticPrefs::layout_display_list_rebuild_frame_limit()) {
|
||||||
// If the modified frames count is above the rebuild limit, mark the root
|
// If the modified frames count is above the rebuild limit, mark the root
|
||||||
@@ -3089,6 +3089,41 @@ struct ContainerTracker {
|
|||||||
bool mCreatedContainer = false;
|
bool mCreatedContainer = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to reuse a top-level stacking context item from the previous paint.
|
||||||
|
* Returns true if an item was reused, otherwise false.
|
||||||
|
*/
|
||||||
|
bool TryToReuseStackingContextItem(nsDisplayListBuilder* aBuilder,
|
||||||
|
nsDisplayList* aList, nsIFrame* aFrame) {
|
||||||
|
if (!aBuilder->IsForPainting() || !aBuilder->IsPartialUpdate() ||
|
||||||
|
aBuilder->InInvalidSubtree()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aFrame->IsFrameModified() || aFrame->HasModifiedDescendants()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& items = aFrame->DisplayItems();
|
||||||
|
auto* res = std::find_if(
|
||||||
|
items.begin(), items.end(),
|
||||||
|
[](nsDisplayItem* aItem) { return aItem->IsPreProcessed(); });
|
||||||
|
|
||||||
|
if (res == items.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsDisplayItem* container = *res;
|
||||||
|
MOZ_ASSERT(!container->GetAbove());
|
||||||
|
MOZ_ASSERT(container->Frame() == aFrame);
|
||||||
|
DL_LOGD("RDL - Found SC item %p (%s) (frame: %p)", container,
|
||||||
|
container->Name(), container->Frame());
|
||||||
|
|
||||||
|
aList->AppendToTop(container);
|
||||||
|
aBuilder->ReuseDisplayItem(container);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void nsIFrame::BuildDisplayListForStackingContext(
|
void nsIFrame::BuildDisplayListForStackingContext(
|
||||||
nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
|
nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
|
||||||
bool* aCreatedContainerItem) {
|
bool* aCreatedContainerItem) {
|
||||||
@@ -3103,7 +3138,18 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||||||
});
|
});
|
||||||
|
|
||||||
AutoCheckBuilder check(aBuilder);
|
AutoCheckBuilder check(aBuilder);
|
||||||
if (HasAnyStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE)) return;
|
|
||||||
|
if (aBuilder->IsReusingStackingContextItems() &&
|
||||||
|
TryToReuseStackingContextItem(aBuilder, aList, this)) {
|
||||||
|
if (aCreatedContainerItem) {
|
||||||
|
*aCreatedContainerItem = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasAnyStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const nsStyleDisplay* disp = StyleDisplay();
|
const nsStyleDisplay* disp = StyleDisplay();
|
||||||
const nsStyleEffects* effects = StyleEffects();
|
const nsStyleEffects* effects = StyleEffects();
|
||||||
@@ -3258,7 +3304,8 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||||||
//
|
//
|
||||||
// These conditions should match |CanStoreDisplayListBuildingRect()| in
|
// These conditions should match |CanStoreDisplayListBuildingRect()| in
|
||||||
// RetainedDisplayListBuilder.cpp
|
// RetainedDisplayListBuilder.cpp
|
||||||
if (aBuilder->IsPartialUpdate() && !aBuilder->InInvalidSubtree() &&
|
if (!aBuilder->IsReusingStackingContextItems() &&
|
||||||
|
aBuilder->IsPartialUpdate() && !aBuilder->InInvalidSubtree() &&
|
||||||
!IsFrameModified() && IsFixedPosContainingBlock() &&
|
!IsFrameModified() && IsFixedPosContainingBlock() &&
|
||||||
!GetPrevContinuation() && !GetNextContinuation()) {
|
!GetPrevContinuation() && !GetNextContinuation()) {
|
||||||
dirtyRect = nsRect();
|
dirtyRect = nsRect();
|
||||||
@@ -3439,6 +3486,7 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||||||
MarkAbsoluteFramesForDisplayList(aBuilder);
|
MarkAbsoluteFramesForDisplayList(aBuilder);
|
||||||
aBuilder->Check();
|
aBuilder->Check();
|
||||||
BuildDisplayList(aBuilder, set);
|
BuildDisplayList(aBuilder, set);
|
||||||
|
SetBuiltDisplayList(true);
|
||||||
aBuilder->Check();
|
aBuilder->Check();
|
||||||
aBuilder->DisplayCaret(this, set.Outlines());
|
aBuilder->DisplayCaret(this, set.Outlines());
|
||||||
|
|
||||||
@@ -3804,15 +3852,39 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||||||
CreateOwnLayerIfNeeded(aBuilder, &resultList,
|
CreateOwnLayerIfNeeded(aBuilder, &resultList,
|
||||||
nsDisplayOwnLayer::OwnLayerForStackingContext,
|
nsDisplayOwnLayer::OwnLayerForStackingContext,
|
||||||
&createdOwnLayer);
|
&createdOwnLayer);
|
||||||
|
|
||||||
if (createdOwnLayer) {
|
if (createdOwnLayer) {
|
||||||
ct.TrackContainer(resultList.GetTop());
|
ct.TrackContainer(resultList.GetTop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aBuilder->IsReusingStackingContextItems()) {
|
||||||
|
if (resultList.IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsDisplayItem* container = resultList.GetBottom();
|
||||||
|
if (resultList.Count() > 1 || container->Frame() != this) {
|
||||||
|
container = MakeDisplayItem<nsDisplayContainer>(
|
||||||
|
aBuilder, this, containerItemASR, &resultList);
|
||||||
|
} else {
|
||||||
|
container = resultList.RemoveBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the outermost display item as reusable. These display items and
|
||||||
|
// their chidren can be reused during the next paint if no ancestor or
|
||||||
|
// descendant frames have been modified.
|
||||||
|
if (!container->IsReusedItem()) {
|
||||||
|
container->SetReusable();
|
||||||
|
}
|
||||||
|
aList->AppendToTop(container);
|
||||||
|
ct.TrackContainer(container);
|
||||||
|
} else {
|
||||||
|
aList->AppendToTop(&resultList);
|
||||||
|
}
|
||||||
|
|
||||||
if (aCreatedContainerItem) {
|
if (aCreatedContainerItem) {
|
||||||
*aCreatedContainerItem = ct.mCreatedContainer;
|
*aCreatedContainerItem = ct.mCreatedContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
aList->AppendToTop(&resultList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static nsDisplayItem* WrapInWrapList(nsDisplayListBuilder* aBuilder,
|
static nsDisplayItem* WrapInWrapList(nsDisplayListBuilder* aBuilder,
|
||||||
@@ -3973,6 +4045,7 @@ void nsIFrame::BuildDisplayListForSimpleChild(nsDisplayListBuilder* aBuilder,
|
|||||||
aBuilder->AdjustWindowDraggingRegion(aChild);
|
aBuilder->AdjustWindowDraggingRegion(aChild);
|
||||||
aBuilder->Check();
|
aBuilder->Check();
|
||||||
aChild->BuildDisplayList(aBuilder, aLists);
|
aChild->BuildDisplayList(aBuilder, aLists);
|
||||||
|
aChild->SetBuiltDisplayList(true);
|
||||||
aBuilder->Check();
|
aBuilder->Check();
|
||||||
aBuilder->DisplayCaret(aChild, aLists.Outlines());
|
aBuilder->DisplayCaret(aChild, aLists.Outlines());
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@@ -4181,8 +4254,6 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||||||
awayFromCommonPath = true;
|
awayFromCommonPath = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
child->SetBuiltDisplayList(true);
|
|
||||||
|
|
||||||
// Child is composited if it's transformed, partially transparent, or has
|
// Child is composited if it's transformed, partially transparent, or has
|
||||||
// SVG effects or a blend mode..
|
// SVG effects or a blend mode..
|
||||||
const nsStyleDisplay* disp = child->StyleDisplay();
|
const nsStyleDisplay* disp = child->StyleDisplay();
|
||||||
@@ -4264,7 +4335,8 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||||||
child->BuildDisplayListForStackingContext(aBuilder, &list,
|
child->BuildDisplayListForStackingContext(aBuilder, &list,
|
||||||
&builtContainerItem);
|
&builtContainerItem);
|
||||||
wrapListASR = contASRTracker.GetContainerASR();
|
wrapListASR = contASRTracker.GetContainerASR();
|
||||||
if (aBuilder->GetCaretFrame() == child) {
|
if (!aBuilder->IsReusingStackingContextItems() &&
|
||||||
|
aBuilder->GetCaretFrame() == child) {
|
||||||
builtContainerItem = false;
|
builtContainerItem = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -4279,6 +4351,7 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||||||
}
|
}
|
||||||
|
|
||||||
child->MarkAbsoluteFramesForDisplayList(aBuilder);
|
child->MarkAbsoluteFramesForDisplayList(aBuilder);
|
||||||
|
child->SetBuiltDisplayList(true);
|
||||||
|
|
||||||
if (!awayFromCommonPath &&
|
if (!awayFromCommonPath &&
|
||||||
// Some SVG frames might change opacity without invalidating the frame,
|
// Some SVG frames might change opacity without invalidating the frame,
|
||||||
@@ -4332,10 +4405,10 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||||||
|
|
||||||
buildingForChild.RestoreBuildingInvisibleItemsValue();
|
buildingForChild.RestoreBuildingInvisibleItemsValue();
|
||||||
|
|
||||||
|
if (!list.IsEmpty()) {
|
||||||
if (isPositioned || isStackingContext) {
|
if (isPositioned || isStackingContext) {
|
||||||
// Genuine stacking contexts, and positioned pseudo-stacking-contexts,
|
// Genuine stacking contexts, and positioned pseudo-stacking-contexts,
|
||||||
// go in this level.
|
// go in this level.
|
||||||
if (!list.IsEmpty()) {
|
|
||||||
nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, wrapListASR,
|
nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, wrapListASR,
|
||||||
builtContainerItem);
|
builtContainerItem);
|
||||||
if (isSVG) {
|
if (isSVG) {
|
||||||
@@ -4343,15 +4416,13 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
|
|||||||
} else {
|
} else {
|
||||||
aLists.PositionedDescendants()->AppendToTop(item);
|
aLists.PositionedDescendants()->AppendToTop(item);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if (!isSVG && disp->IsFloating(child)) {
|
} else if (!isSVG && disp->IsFloating(child)) {
|
||||||
if (!list.IsEmpty()) {
|
|
||||||
aLists.Floats()->AppendToTop(
|
aLists.Floats()->AppendToTop(
|
||||||
WrapInWrapList(aBuilder, child, &list, wrapListASR));
|
WrapInWrapList(aBuilder, child, &list, wrapListASR));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
aLists.Content()->AppendToTop(&list);
|
aLists.Content()->AppendToTop(&list);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// We delay placing the positioned descendants of positioned frames to here,
|
// We delay placing the positioned descendants of positioned frames to here,
|
||||||
// because in the absence of z-index this is the correct order for them.
|
// because in the absence of z-index this is the correct order for them.
|
||||||
// This doesn't affect correctness because the positioned descendants list
|
// This doesn't affect correctness because the positioned descendants list
|
||||||
@@ -7985,6 +8056,14 @@ void nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix,
|
|||||||
aTo += ToString(pseudoType).c_str();
|
aTo += ToString(pseudoType).c_str();
|
||||||
}
|
}
|
||||||
aTo += "]";
|
aTo += "]";
|
||||||
|
|
||||||
|
if (IsFrameModified()) {
|
||||||
|
aTo += nsPrintfCString(" modified");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasModifiedDescendants()) {
|
||||||
|
aTo += nsPrintfCString(" has-modified-descendants");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsIFrame::List(FILE* out, const char* aPrefix, ListFlags aFlags) const {
|
void nsIFrame::List(FILE* out, const char* aPrefix, ListFlags aFlags) const {
|
||||||
|
|||||||
@@ -599,6 +599,7 @@ class nsIFrame : public nsQueryFrame {
|
|||||||
mForceDescendIntoIfVisible(false),
|
mForceDescendIntoIfVisible(false),
|
||||||
mBuiltDisplayList(false),
|
mBuiltDisplayList(false),
|
||||||
mFrameIsModified(false),
|
mFrameIsModified(false),
|
||||||
|
mHasModifiedDescendants(false),
|
||||||
mHasOverrideDirtyRegion(false),
|
mHasOverrideDirtyRegion(false),
|
||||||
mMayHaveWillChangeBudget(false),
|
mMayHaveWillChangeBudget(false),
|
||||||
mIsPrimaryFrame(false),
|
mIsPrimaryFrame(false),
|
||||||
@@ -4893,6 +4894,11 @@ class nsIFrame : public nsQueryFrame {
|
|||||||
mFrameIsModified = aFrameIsModified;
|
mFrameIsModified = aFrameIsModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasModifiedDescendants() const { return mHasModifiedDescendants; }
|
||||||
|
void SetHasModifiedDescendants(const bool aHasModifiedDescendants) {
|
||||||
|
mHasModifiedDescendants = aHasModifiedDescendants;
|
||||||
|
}
|
||||||
|
|
||||||
bool HasOverrideDirtyRegion() const { return mHasOverrideDirtyRegion; }
|
bool HasOverrideDirtyRegion() const { return mHasOverrideDirtyRegion; }
|
||||||
void SetHasOverrideDirtyRegion(const bool aHasDirtyRegion) {
|
void SetHasOverrideDirtyRegion(const bool aHasDirtyRegion) {
|
||||||
mHasOverrideDirtyRegion = aHasDirtyRegion;
|
mHasOverrideDirtyRegion = aHasDirtyRegion;
|
||||||
@@ -5128,8 +5134,22 @@ class nsIFrame : public nsQueryFrame {
|
|||||||
*/
|
*/
|
||||||
bool mBuiltDisplayList : 1;
|
bool mBuiltDisplayList : 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the frame has been marked modified by
|
||||||
|
* |MarkNeedsDisplayItemRebuild()|, usually due to a style change or reflow.
|
||||||
|
*/
|
||||||
bool mFrameIsModified : 1;
|
bool mFrameIsModified : 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the frame has modified descendants. Set before display list
|
||||||
|
* preprocessing and only used during partial display list builds.
|
||||||
|
*/
|
||||||
|
bool mHasModifiedDescendants : 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by merging based retained display lists to restrict the dirty area
|
||||||
|
* during partial display list builds.
|
||||||
|
*/
|
||||||
bool mHasOverrideDirtyRegion : 1;
|
bool mHasOverrideDirtyRegion : 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10293,8 +10293,9 @@ void nsTextFrame::ToCString(nsCString& aBuf) const {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t contentLength = AssertedCast<uint32_t>(GetContentLength());
|
const int32_t length = GetContentEnd() - mContentOffset;
|
||||||
if (0 == contentLength) {
|
if (length <= 0) {
|
||||||
|
// Negative lengths are possible during invalidation.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "RetainedDisplayListBuilder.h"
|
#include "RetainedDisplayListBuilder.h"
|
||||||
|
|
||||||
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/StaticPrefs_layout.h"
|
#include "mozilla/StaticPrefs_layout.h"
|
||||||
#include "nsIFrame.h"
|
#include "nsIFrame.h"
|
||||||
#include "nsIFrameInlines.h"
|
#include "nsIFrameInlines.h"
|
||||||
@@ -112,6 +113,7 @@ static void MarkFramesWithItemsAndImagesModified(nsDisplayList* aList) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (invalidate) {
|
if (invalidate) {
|
||||||
|
DL_LOGV("Invalidating item %p (%s)", i, i->Name());
|
||||||
i->FrameForInvalidation()->MarkNeedsDisplayItemRebuild();
|
i->FrameForInvalidation()->MarkNeedsDisplayItemRebuild();
|
||||||
if (i->GetDependentFrame()) {
|
if (i->GetDependentFrame()) {
|
||||||
i->GetDependentFrame()->MarkNeedsDisplayItemRebuild();
|
i->GetDependentFrame()->MarkNeedsDisplayItemRebuild();
|
||||||
@@ -355,7 +357,7 @@ bool RetainedDisplayListBuilder::PreProcessDisplayList(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RetainedDisplayListBuilder::IncrementSubDocPresShellPaintCount(
|
void IncrementPresShellPaintCount(nsDisplayListBuilder* aBuilder,
|
||||||
nsDisplayItem* aItem) {
|
nsDisplayItem* aItem) {
|
||||||
MOZ_ASSERT(aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT);
|
MOZ_ASSERT(aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT);
|
||||||
|
|
||||||
@@ -366,7 +368,12 @@ void RetainedDisplayListBuilder::IncrementSubDocPresShellPaintCount(
|
|||||||
PresShell* presShell = subDocFrame->GetSubdocumentPresShellForPainting(0);
|
PresShell* presShell = subDocFrame->GetSubdocumentPresShellForPainting(0);
|
||||||
MOZ_ASSERT(presShell);
|
MOZ_ASSERT(presShell);
|
||||||
|
|
||||||
mBuilder.IncrementPresShellPaintCount(presShell);
|
aBuilder->IncrementPresShellPaintCount(presShell);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RetainedDisplayListBuilder::IncrementSubDocPresShellPaintCount(
|
||||||
|
nsDisplayItem* aItem) {
|
||||||
|
IncrementPresShellPaintCount(&mBuilder, aItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Maybe<const ActiveScrolledRoot*> SelectContainerASR(
|
static Maybe<const ActiveScrolledRoot*> SelectContainerASR(
|
||||||
@@ -1401,6 +1408,7 @@ static void ClearFrameProps(nsTArray<nsIFrame*>& aFrames) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f->SetFrameIsModified(false);
|
f->SetFrameIsModified(false);
|
||||||
|
f->SetHasModifiedDescendants(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1424,6 +1432,232 @@ void RetainedDisplayListBuilder::ClearFramesWithProps() {
|
|||||||
&framesWithProps.Frames());
|
&framesWithProps.Frames());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace RDLUtils {
|
||||||
|
|
||||||
|
MOZ_NEVER_INLINE_DEBUG void AssertFrameSubtreeUnmodified(
|
||||||
|
const nsIFrame* aFrame) {
|
||||||
|
for (const auto& childList : aFrame->ChildLists()) {
|
||||||
|
for (nsIFrame* child : childList.mList) {
|
||||||
|
MOZ_ASSERT(!aFrame->IsFrameModified());
|
||||||
|
MOZ_ASSERT(!aFrame->HasModifiedDescendants());
|
||||||
|
AssertFrameSubtreeUnmodified(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_NEVER_INLINE_DEBUG void AssertDisplayListUnmodified(nsDisplayList* aList) {
|
||||||
|
for (nsDisplayItem* item : *aList) {
|
||||||
|
AssertDisplayItemUnmodified(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_NEVER_INLINE_DEBUG void AssertDisplayItemUnmodified(nsDisplayItem* aItem) {
|
||||||
|
MOZ_ASSERT(!aItem->HasDeletedFrame());
|
||||||
|
MOZ_ASSERT(!AnyContentAncestorModified(aItem->FrameForInvalidation()));
|
||||||
|
|
||||||
|
if (aItem->GetChildren()) {
|
||||||
|
AssertDisplayListUnmodified(aItem->GetChildren());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace RDLUtils
|
||||||
|
|
||||||
|
namespace RDL {
|
||||||
|
|
||||||
|
void MarkAncestorFrames(nsIFrame* aFrame,
|
||||||
|
nsTArray<nsIFrame*>& aOutFramesWithProps) {
|
||||||
|
nsIFrame* frame = nsLayoutUtils::GetDisplayListParent(aFrame);
|
||||||
|
while (frame && !frame->HasModifiedDescendants()) {
|
||||||
|
aOutFramesWithProps.AppendElement(frame);
|
||||||
|
frame->SetHasModifiedDescendants(true);
|
||||||
|
frame = nsLayoutUtils::GetDisplayListParent(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over the modified frames array and updates the frame tree flags
|
||||||
|
* so that container frames know whether they have modified descendant frames.
|
||||||
|
* Frames that were marked modified are added to |aOutFramesWithProps|, so that
|
||||||
|
* the modified status can be cleared after the display list build.
|
||||||
|
*/
|
||||||
|
void MarkAllAncestorFrames(const nsTArray<nsIFrame*>& aModifiedFrames,
|
||||||
|
nsTArray<nsIFrame*>& aOutFramesWithProps) {
|
||||||
|
nsAutoString frameName;
|
||||||
|
DL_LOGI("RDL - Modified frames: %zu", aModifiedFrames.Length());
|
||||||
|
for (nsIFrame* frame : aModifiedFrames) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
frame->GetFrameName(frameName);
|
||||||
|
#endif
|
||||||
|
DL_LOGV("RDL - Processing modified frame: %p (%s)", frame,
|
||||||
|
NS_ConvertUTF16toUTF8(frameName).get());
|
||||||
|
|
||||||
|
MarkAncestorFrames(frame, aOutFramesWithProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the given display item |aItem| as reuseable container, and updates the
|
||||||
|
* bounds in case some child items were destroyed.
|
||||||
|
*/
|
||||||
|
MOZ_NEVER_INLINE_DEBUG void ReuseStackingContextItem(
|
||||||
|
nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
|
||||||
|
aItem->SetPreProcessed();
|
||||||
|
|
||||||
|
if (aItem->HasChildren()) {
|
||||||
|
aItem->UpdateBounds(aBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
DL_LOGD("Retaining display item %p", aItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSupportedFrameType(const nsIFrame* aFrame) {
|
||||||
|
// The way table backgrounds are handled makes these frames incompatible with
|
||||||
|
// this retained display list approach.
|
||||||
|
if (aFrame->IsTableColFrame()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aFrame->IsTableColGroupFrame()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aFrame->IsTableRowFrame()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aFrame->IsTableRowGroupFrame()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aFrame->IsTableCellFrame()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything else should work.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsReuseableStackingContextItem(nsDisplayItem* aItem) {
|
||||||
|
if (!IsSupportedFrameType(aItem->Frame())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aItem->IsReusable()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nsIFrame* frame = aItem->FrameForInvalidation();
|
||||||
|
return !frame->HasModifiedDescendants() && !frame->GetPrevContinuation() &&
|
||||||
|
!frame->GetNextContinuation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively visits every display item of the display list and destroys all
|
||||||
|
* display items that depend on deleted or modified frames.
|
||||||
|
* The stacking context display items for unmodified frame subtrees are kept
|
||||||
|
* linked and collected in given |aOutItems| array.
|
||||||
|
*/
|
||||||
|
void CollectStackingContextItems(nsDisplayListBuilder* aBuilder,
|
||||||
|
nsDisplayList* aList,
|
||||||
|
nsTArray<nsDisplayItem*>& aOutItems,
|
||||||
|
nsIFrame* aOuterFrame, int aDepth = 0,
|
||||||
|
bool aParentReused = false) {
|
||||||
|
nsDisplayList out;
|
||||||
|
|
||||||
|
while (nsDisplayItem* item = aList->RemoveBottom()) {
|
||||||
|
if (DL_LOG_TEST(LogLevel::Debug)) {
|
||||||
|
DL_LOGD(
|
||||||
|
"%*s Preprocessing item %p (%s) (frame: %p) "
|
||||||
|
"(children: %d) (depth: %d) (parentReused: %d)",
|
||||||
|
aDepth, "", item, item->Name(),
|
||||||
|
item->HasDeletedFrame() ? nullptr : item->Frame(),
|
||||||
|
item->GetChildren() ? item->GetChildren()->Count() : 0, aDepth,
|
||||||
|
aParentReused);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item->CanBeReused() || item->HasDeletedFrame() ||
|
||||||
|
AnyContentAncestorModified(item->FrameForInvalidation(), aOuterFrame)) {
|
||||||
|
DL_LOGD("%*s Deleted modified or temporary item %p", aDepth, "", item);
|
||||||
|
item->Destroy(aBuilder);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(!AnyContentAncestorModified(item->FrameForInvalidation()));
|
||||||
|
MOZ_ASSERT(!item->IsPreProcessed());
|
||||||
|
item->InvalidateCachedChildInfo(aBuilder);
|
||||||
|
item->SetMergedPreProcessed(false, true);
|
||||||
|
item->SetReused(true);
|
||||||
|
|
||||||
|
const bool isStackingContextItem = IsReuseableStackingContextItem(item);
|
||||||
|
|
||||||
|
if (item->GetChildren()) {
|
||||||
|
CollectStackingContextItems(aBuilder, item->GetChildren(), aOutItems,
|
||||||
|
item->Frame(), aDepth + 1,
|
||||||
|
aParentReused || isStackingContextItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aParentReused) {
|
||||||
|
// Keep the contents of the current container item linked.
|
||||||
|
RDLUtils::AssertDisplayItemUnmodified(item);
|
||||||
|
out.AppendToTop(item);
|
||||||
|
} else if (isStackingContextItem) {
|
||||||
|
// |item| is a stacking context item that can be reused.
|
||||||
|
aOutItems.AppendElement(item);
|
||||||
|
ReuseStackingContextItem(aBuilder, item);
|
||||||
|
} else {
|
||||||
|
// |item| is inside a container item that will be destroyed later.
|
||||||
|
DL_LOGD("%*s Deleted unused item %p", aDepth, "", item);
|
||||||
|
item->Destroy(aBuilder);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
|
||||||
|
IncrementPresShellPaintCount(aBuilder, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aList->AppendToTop(&out);
|
||||||
|
aList->RestoreState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the retained stacking context items that have not been reused and
|
||||||
|
* clears the array.
|
||||||
|
*/
|
||||||
|
void ClearPreviousItems(nsDisplayListBuilder* aBuilder,
|
||||||
|
nsTArray<nsDisplayItem*>& aItems) {
|
||||||
|
const size_t total = aItems.Length();
|
||||||
|
size_t reused = 0;
|
||||||
|
for (auto* item : aItems) {
|
||||||
|
if (item->IsReusedItem()) {
|
||||||
|
reused++;
|
||||||
|
item->SetReusable();
|
||||||
|
} else {
|
||||||
|
item->Destroy(aBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DL_LOGI("RDL - Reused %zu of %zu SC display items", reused, total);
|
||||||
|
aItems.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace RDL
|
||||||
|
|
||||||
|
bool RetainedDisplayListBuilder::TrySimpleUpdate(
|
||||||
|
const nsTArray<nsIFrame*>& aModifiedFrames,
|
||||||
|
nsTArray<nsIFrame*>& aOutFramesWithProps) {
|
||||||
|
if (!mBuilder.IsReusingStackingContextItems()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(mPreviousItems.IsEmpty());
|
||||||
|
RDL::MarkAllAncestorFrames(aModifiedFrames, aOutFramesWithProps);
|
||||||
|
RDL::CollectStackingContextItems(&mBuilder, &mList, mPreviousItems,
|
||||||
|
RootReferenceFrame());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
|
PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
|
||||||
nscolor aBackstop) {
|
nscolor aBackstop) {
|
||||||
DL_LOGI("RDL - AttemptPartialUpdate, root frame: %p", RootReferenceFrame());
|
DL_LOGI("RDL - AttemptPartialUpdate, root frame: %p", RootReferenceFrame());
|
||||||
@@ -1437,33 +1671,44 @@ PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
|
|||||||
|
|
||||||
InvalidateCaretFramesIfNeeded();
|
InvalidateCaretFramesIfNeeded();
|
||||||
|
|
||||||
mBuilder.EnterPresShell(mBuilder.RootReferenceFrame());
|
|
||||||
|
|
||||||
// We set the override dirty regions during ComputeRebuildRegion or in
|
// We set the override dirty regions during ComputeRebuildRegion or in
|
||||||
// DisplayPortUtils::InvalidateForDisplayPortChange. The display port change
|
// DisplayPortUtils::InvalidateForDisplayPortChange. The display port change
|
||||||
// also marks the frame modified, so those regions are cleared here as well.
|
// also marks the frame modified, so those regions are cleared here as well.
|
||||||
AutoClearFramePropsArray modifiedFrames(64);
|
AutoClearFramePropsArray modifiedFrames(64);
|
||||||
AutoClearFramePropsArray framesWithProps;
|
AutoClearFramePropsArray framesWithProps(64);
|
||||||
GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames.Frames(),
|
GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames.Frames(),
|
||||||
&framesWithProps.Frames());
|
&framesWithProps.Frames());
|
||||||
|
|
||||||
// Do not allow partial builds if the |ShouldBuildPartial()| heuristic fails.
|
if (!ShouldBuildPartial(modifiedFrames.Frames())) {
|
||||||
bool shouldBuildPartial = ShouldBuildPartial(modifiedFrames.Frames());
|
// Do not allow partial builds if the |ShouldBuildPartial()| heuristic
|
||||||
|
// fails.
|
||||||
|
mBuilder.SetPartialBuildFailed(true);
|
||||||
|
return PartialUpdateResult::Failed;
|
||||||
|
}
|
||||||
|
|
||||||
nsRect modifiedDirty;
|
nsRect modifiedDirty;
|
||||||
nsDisplayList modifiedDL;
|
nsDisplayList modifiedDL;
|
||||||
nsIFrame* modifiedAGR = nullptr;
|
nsIFrame* modifiedAGR = nullptr;
|
||||||
PartialUpdateResult result = PartialUpdateResult::NoChange;
|
PartialUpdateResult result = PartialUpdateResult::NoChange;
|
||||||
if (!shouldBuildPartial ||
|
const bool simpleUpdate =
|
||||||
!ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
|
TrySimpleUpdate(modifiedFrames.Frames(), framesWithProps.Frames());
|
||||||
|
|
||||||
|
mBuilder.EnterPresShell(RootReferenceFrame());
|
||||||
|
|
||||||
|
if (!simpleUpdate) {
|
||||||
|
if (!ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
|
||||||
&modifiedAGR, framesWithProps.Frames()) ||
|
&modifiedAGR, framesWithProps.Frames()) ||
|
||||||
!PreProcessDisplayList(&mList, modifiedAGR, result,
|
!PreProcessDisplayList(&mList, modifiedAGR, result,
|
||||||
mBuilder.RootReferenceFrame(), nullptr)) {
|
RootReferenceFrame(), nullptr)) {
|
||||||
|
DL_LOGI("RDL - Partial update aborted");
|
||||||
mBuilder.SetPartialBuildFailed(true);
|
mBuilder.SetPartialBuildFailed(true);
|
||||||
mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), nullptr);
|
mBuilder.LeavePresShell(RootReferenceFrame(), nullptr);
|
||||||
mList.DeleteAll(&mBuilder);
|
mList.DeleteAll(&mBuilder);
|
||||||
return PartialUpdateResult::Failed;
|
return PartialUpdateResult::Failed;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
modifiedDirty = mBuilder.GetVisibleRect();
|
||||||
|
}
|
||||||
|
|
||||||
// This is normally handled by EnterPresShell, but we skipped it so that we
|
// This is normally handled by EnterPresShell, but we skipped it so that we
|
||||||
// didn't call MarkFrameForDisplayIfVisible before ComputeRebuildRegion.
|
// didn't call MarkFrameForDisplayIfVisible before ComputeRebuildRegion.
|
||||||
@@ -1499,6 +1744,7 @@ PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
|
|||||||
if (mBuilder.PartialBuildFailed()) {
|
if (mBuilder.PartialBuildFailed()) {
|
||||||
DL_LOGI("RDL - Partial update failed!");
|
DL_LOGI("RDL - Partial update failed!");
|
||||||
mBuilder.LeavePresShell(RootReferenceFrame(), nullptr);
|
mBuilder.LeavePresShell(RootReferenceFrame(), nullptr);
|
||||||
|
RDL::ClearPreviousItems(&mBuilder, mPreviousItems);
|
||||||
mList.DeleteAll(&mBuilder);
|
mList.DeleteAll(&mBuilder);
|
||||||
modifiedDL.DeleteAll(&mBuilder);
|
modifiedDL.DeleteAll(&mBuilder);
|
||||||
Metrics()->mPartialUpdateFailReason = PartialUpdateFailReason::Content;
|
Metrics()->mPartialUpdateFailReason = PartialUpdateFailReason::Content;
|
||||||
@@ -1519,15 +1765,26 @@ PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
|
|||||||
// we call RestoreState on nsDisplayWrapList it resets the clip to the base
|
// we call RestoreState on nsDisplayWrapList it resets the clip to the base
|
||||||
// clip, and we need the UpdateBounds call (within MergeDisplayLists) to
|
// clip, and we need the UpdateBounds call (within MergeDisplayLists) to
|
||||||
// move it to the correct inner clip.
|
// move it to the correct inner clip.
|
||||||
|
if (!simpleUpdate) {
|
||||||
Maybe<const ActiveScrolledRoot*> dummy;
|
Maybe<const ActiveScrolledRoot*> dummy;
|
||||||
if (MergeDisplayLists(&modifiedDL, &mList, &mList, dummy)) {
|
if (MergeDisplayLists(&modifiedDL, &mList, &mList, dummy)) {
|
||||||
result = PartialUpdateResult::Updated;
|
result = PartialUpdateResult::Updated;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(mList.IsEmpty());
|
||||||
|
mList = std::move(modifiedDL);
|
||||||
|
RDL::ClearPreviousItems(&mBuilder, mPreviousItems);
|
||||||
|
result = PartialUpdateResult::Updated;
|
||||||
|
}
|
||||||
|
|
||||||
// printf_stderr("Painting --- Merged list:\n");
|
#if 0
|
||||||
// nsIFrame::PrintDisplayList(&mBuilder, mList);
|
if (DL_LOG_TEST(LogLevel::Verbose)) {
|
||||||
|
printf_stderr("Painting --- Display list:\n");
|
||||||
|
nsIFrame::PrintDisplayList(&mBuilder, mList);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), List());
|
mBuilder.LeavePresShell(RootReferenceFrame(), List());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ class nsWindowSizes;
|
|||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
class nsDisplayItem;
|
||||||
|
class nsDisplayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RetainedDisplayListData contains frame invalidation information. It is stored
|
* RetainedDisplayListData contains frame invalidation information. It is stored
|
||||||
* in root frames, and used by RetainedDisplayListBuilder.
|
* in root frames, and used by RetainedDisplayListBuilder.
|
||||||
@@ -254,15 +257,33 @@ struct RetainedDisplayListBuilder {
|
|||||||
nsIFrame** aOutModifiedAGR);
|
nsIFrame** aOutModifiedAGR);
|
||||||
|
|
||||||
nsIFrame* RootReferenceFrame() { return mBuilder.RootReferenceFrame(); }
|
nsIFrame* RootReferenceFrame() { return mBuilder.RootReferenceFrame(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to perform a simple partial display list build without display list
|
||||||
|
* merging. In this mode, only the top-level stacking context items and their
|
||||||
|
* contents are reused, when the frame subtree has not been modified.
|
||||||
|
*/
|
||||||
|
bool TrySimpleUpdate(const nsTArray<nsIFrame*>& aModifiedFrames,
|
||||||
|
nsTArray<nsIFrame*>& aOutFramesWithProps);
|
||||||
|
|
||||||
friend class MergeState;
|
friend class MergeState;
|
||||||
|
|
||||||
nsDisplayListBuilder mBuilder;
|
nsDisplayListBuilder mBuilder;
|
||||||
RetainedDisplayList mList;
|
RetainedDisplayList mList;
|
||||||
nsRect mPreviousVisibleRect;
|
|
||||||
WeakFrame mPreviousCaret;
|
WeakFrame mPreviousCaret;
|
||||||
RetainedDisplayListMetrics mMetrics;
|
RetainedDisplayListMetrics mMetrics;
|
||||||
|
|
||||||
|
// Stores reusable items collected during display list preprocessing.
|
||||||
|
nsTArray<nsDisplayItem*> mPreviousItems;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace RDLUtils {
|
||||||
|
|
||||||
|
void AssertFrameSubtreeUnmodified(const nsIFrame* aFrame);
|
||||||
|
void AssertDisplayItemUnmodified(nsDisplayItem* aItem);
|
||||||
|
void AssertDisplayListUnmodified(nsDisplayList* aList);
|
||||||
|
|
||||||
|
} // namespace RDLUtils
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
#endif // RETAINEDDISPLAYLISTBUILDER_H_
|
#endif // RETAINEDDISPLAYLISTBUILDER_H_
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ void AssertUniqueItem(nsDisplayItem* aItem) {
|
|||||||
for (nsDisplayItem* i : aItem->Frame()->DisplayItems()) {
|
for (nsDisplayItem* i : aItem->Frame()->DisplayItems()) {
|
||||||
if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() &&
|
if (i != aItem && !i->HasDeletedFrame() && i->Frame() == aItem->Frame() &&
|
||||||
i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
|
i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
|
||||||
if (i->IsPreProcessedItem()) {
|
if (i->IsPreProcessedItem() || i->IsPreProcessed()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
|
MOZ_DIAGNOSTIC_ASSERT(false, "Duplicate display item!");
|
||||||
@@ -694,6 +694,8 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
|
|||||||
static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
|
static_cast<uint32_t>(DisplayItemType::TYPE_MAX) < (1 << TYPE_BITS),
|
||||||
"Check TYPE_MAX should not overflow");
|
"Check TYPE_MAX should not overflow");
|
||||||
mIsForContent = XRE_IsContentProcess();
|
mIsForContent = XRE_IsContentProcess();
|
||||||
|
mIsReusingStackingContextItems =
|
||||||
|
mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc();
|
||||||
}
|
}
|
||||||
|
|
||||||
static PresShell* GetFocusedPresShell() {
|
static PresShell* GetFocusedPresShell() {
|
||||||
@@ -2006,6 +2008,20 @@ void nsDisplayListBuilder::BuildCompositorHitTestInfoIfNeeded(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nsDisplayListBuilder::ReuseDisplayItem(nsDisplayItem* aItem) {
|
||||||
|
const auto* previous = mCurrentContainerASR;
|
||||||
|
const auto* asr = aItem->GetActiveScrolledRoot();
|
||||||
|
mCurrentContainerASR =
|
||||||
|
ActiveScrolledRoot::PickAncestor(asr, mCurrentContainerASR);
|
||||||
|
|
||||||
|
if (previous != mCurrentContainerASR) {
|
||||||
|
DL_LOGV("RDL - Changed mCurrentContainerASR from %p to %p", previous,
|
||||||
|
mCurrentContainerASR);
|
||||||
|
}
|
||||||
|
|
||||||
|
aItem->SetReusedItem();
|
||||||
|
}
|
||||||
|
|
||||||
void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
|
void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const {
|
||||||
aDestination.BorderBackground()->AppendToTop(BorderBackground());
|
aDestination.BorderBackground()->AppendToTop(BorderBackground());
|
||||||
aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
|
aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
|
||||||
|
|||||||
@@ -1691,6 +1691,20 @@ class nsDisplayListBuilder {
|
|||||||
*/
|
*/
|
||||||
nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame);
|
nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this is a retained builder and reuse stacking contexts
|
||||||
|
* mode is enabled by pref.
|
||||||
|
*/
|
||||||
|
bool IsReusingStackingContextItems() const {
|
||||||
|
return mIsReusingStackingContextItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the given display item |aItem| as reused, and updates the necessary
|
||||||
|
* display list builder state.
|
||||||
|
*/
|
||||||
|
void ReuseDisplayItem(nsDisplayItem* aItem);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
|
bool MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
|
||||||
const nsRect& aVisibleRect,
|
const nsRect& aVisibleRect,
|
||||||
@@ -1885,6 +1899,7 @@ class nsDisplayListBuilder {
|
|||||||
gfx::CompositorHitTestInfo mCompositorHitTestInfo;
|
gfx::CompositorHitTestInfo mCompositorHitTestInfo;
|
||||||
|
|
||||||
bool mIsForContent;
|
bool mIsForContent;
|
||||||
|
bool mIsReusingStackingContextItems;
|
||||||
};
|
};
|
||||||
|
|
||||||
class nsDisplayItem;
|
class nsDisplayItem;
|
||||||
@@ -2756,6 +2771,44 @@ class nsDisplayItem : public nsDisplayItemLink {
|
|||||||
|
|
||||||
virtual const HitTestInfo& GetHitTestInfo() { return HitTestInfo::Empty(); }
|
virtual const HitTestInfo& GetHitTestInfo() { return HitTestInfo::Empty(); }
|
||||||
|
|
||||||
|
enum class ReuseState : uint8_t {
|
||||||
|
None,
|
||||||
|
// Set during display list building.
|
||||||
|
Reusable,
|
||||||
|
// Set during display list preprocessing.
|
||||||
|
PreProcessed,
|
||||||
|
// Set during partial display list build.
|
||||||
|
Reused,
|
||||||
|
};
|
||||||
|
|
||||||
|
void SetReusable() {
|
||||||
|
MOZ_ASSERT(mReuseState == ReuseState::None ||
|
||||||
|
mReuseState == ReuseState::Reused);
|
||||||
|
mReuseState = ReuseState::Reusable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsReusable() const { return mReuseState == ReuseState::Reusable; }
|
||||||
|
|
||||||
|
void SetPreProcessed() {
|
||||||
|
MOZ_ASSERT(mReuseState == ReuseState::Reusable);
|
||||||
|
mReuseState = ReuseState::PreProcessed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPreProcessed() const {
|
||||||
|
return mReuseState == ReuseState::PreProcessed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetReusedItem() {
|
||||||
|
MOZ_ASSERT(mReuseState == ReuseState::PreProcessed);
|
||||||
|
mReuseState = ReuseState::Reused;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsReusedItem() const { return mReuseState == ReuseState::Reused; }
|
||||||
|
|
||||||
|
void ResetReuseState() { mReuseState = ReuseState::None; }
|
||||||
|
|
||||||
|
ReuseState GetReuseState() const { return mReuseState; }
|
||||||
|
|
||||||
nsIFrame* mFrame; // 8
|
nsIFrame* mFrame; // 8
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -2784,7 +2837,7 @@ class nsDisplayItem : public nsDisplayItemLink {
|
|||||||
DisplayItemType mType = DisplayItemType::TYPE_ZERO; // 1
|
DisplayItemType mType = DisplayItemType::TYPE_ZERO; // 1
|
||||||
uint8_t mExtraPageForPageNum = 0; // 1
|
uint8_t mExtraPageForPageNum = 0; // 1
|
||||||
uint16_t mPerFrameIndex = 0; // 2
|
uint16_t mPerFrameIndex = 0; // 2
|
||||||
// 2 free bytes here
|
ReuseState mReuseState = ReuseState::None;
|
||||||
OldListIndex mOldListIndex; // 4
|
OldListIndex mOldListIndex; // 4
|
||||||
uintptr_t mOldList = 0; // 8
|
uintptr_t mOldList = 0; // 8
|
||||||
|
|
||||||
@@ -3484,6 +3537,13 @@ class RetainedDisplayList : public nsDisplayList {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RetainedDisplayList& operator=(nsDisplayList&& aOther) {
|
||||||
|
MOZ_ASSERT(!Count(), "Can only move into an empty list!");
|
||||||
|
MOZ_ASSERT(mOldItems.IsEmpty(), "Can only move into an empty list!");
|
||||||
|
AppendToTop(&aOther);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void DeleteAll(nsDisplayListBuilder* aBuilder) override {
|
void DeleteAll(nsDisplayListBuilder* aBuilder) override {
|
||||||
for (OldItemInfo& i : mOldItems) {
|
for (OldItemInfo& i : mOldItems) {
|
||||||
if (i.mItem && i.mOwnsItem) {
|
if (i.mItem && i.mOwnsItem) {
|
||||||
@@ -3686,8 +3746,8 @@ class nsDisplayReflowCount : public nsPaintedDisplayItem {
|
|||||||
PR_BEGIN_MACRO \
|
PR_BEGIN_MACRO \
|
||||||
if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() && \
|
if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() && \
|
||||||
PresShell()->IsPaintingFrameCounts()) { \
|
PresShell()->IsPaintingFrameCounts()) { \
|
||||||
aLists.Outlines()->AppendNewToTop<nsDisplayReflowCount>(aBuilder, this, \
|
aLists.Outlines()->AppendNewToTop<mozilla::nsDisplayReflowCount>( \
|
||||||
_name); \
|
aBuilder, this, _name); \
|
||||||
} \
|
} \
|
||||||
PR_END_MACRO
|
PR_END_MACRO
|
||||||
|
|
||||||
@@ -3695,8 +3755,8 @@ class nsDisplayReflowCount : public nsPaintedDisplayItem {
|
|||||||
PR_BEGIN_MACRO \
|
PR_BEGIN_MACRO \
|
||||||
if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() && \
|
if (!aBuilder->IsBackgroundOnly() && !aBuilder->IsForEventDelivery() && \
|
||||||
PresShell()->IsPaintingFrameCounts()) { \
|
PresShell()->IsPaintingFrameCounts()) { \
|
||||||
aLists.Outlines()->AppendNewToTop<nsDisplayReflowCount>(aBuilder, this, \
|
aLists.Outlines()->AppendNewToTop<mozilla::nsDisplayReflowCount>( \
|
||||||
_name, _color); \
|
aBuilder, this, _name, _color); \
|
||||||
} \
|
} \
|
||||||
PR_END_MACRO
|
PR_END_MACRO
|
||||||
|
|
||||||
@@ -4099,7 +4159,7 @@ class nsDisplayBackgroundImage : public nsPaintedDisplayItem {
|
|||||||
nsIFrame* GetDependentFrame() override { return mDependentFrame; }
|
nsIFrame* GetDependentFrame() override { return mDependentFrame; }
|
||||||
|
|
||||||
void SetDependentFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
|
void SetDependentFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
|
||||||
if (!aBuilder->IsRetainingDisplayList()) {
|
if (!aBuilder->IsRetainingDisplayList() || mDependentFrame == aFrame) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mDependentFrame = aFrame;
|
mDependentFrame = aFrame;
|
||||||
@@ -4388,7 +4448,7 @@ class nsDisplayBackgroundColor : public nsPaintedDisplayItem {
|
|||||||
nsIFrame* GetDependentFrame() override { return mDependentFrame; }
|
nsIFrame* GetDependentFrame() override { return mDependentFrame; }
|
||||||
|
|
||||||
void SetDependentFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
|
void SetDependentFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
|
||||||
if (!aBuilder->IsRetainingDisplayList()) {
|
if (!aBuilder->IsRetainingDisplayList() || mDependentFrame == aFrame) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mDependentFrame = aFrame;
|
mDependentFrame = aFrame;
|
||||||
|
|||||||
@@ -340,8 +340,9 @@ void nsTableCellFrame::InvalidateFrame(uint32_t aDisplayItemKey,
|
|||||||
bool aRebuildDisplayItems) {
|
bool aRebuildDisplayItems) {
|
||||||
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
|
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
|
||||||
if (GetTableFrame()->IsBorderCollapse()) {
|
if (GetTableFrame()->IsBorderCollapse()) {
|
||||||
|
const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
|
||||||
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
|
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
|
||||||
aDisplayItemKey, false);
|
aDisplayItemKey, rebuild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,7 +355,7 @@ void nsTableCellFrame::InvalidateFrameWithRect(const nsRect& aRect,
|
|||||||
// we get an inactive layer created and this is computed
|
// we get an inactive layer created and this is computed
|
||||||
// within FrameLayerBuilder
|
// within FrameLayerBuilder
|
||||||
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
|
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
|
||||||
false);
|
aRebuildDisplayItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nsTableCellFrame::ShouldPaintBordersAndBackgrounds() const {
|
bool nsTableCellFrame::ShouldPaintBordersAndBackgrounds() const {
|
||||||
|
|||||||
@@ -188,8 +188,9 @@ void nsTableColFrame::InvalidateFrame(uint32_t aDisplayItemKey,
|
|||||||
bool aRebuildDisplayItems) {
|
bool aRebuildDisplayItems) {
|
||||||
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
|
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
|
||||||
if (GetTableFrame()->IsBorderCollapse()) {
|
if (GetTableFrame()->IsBorderCollapse()) {
|
||||||
|
const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
|
||||||
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
|
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
|
||||||
aDisplayItemKey, false);
|
aDisplayItemKey, rebuild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,5 +204,5 @@ void nsTableColFrame::InvalidateFrameWithRect(const nsRect& aRect,
|
|||||||
// we get an inactive layer created and this is computed
|
// we get an inactive layer created and this is computed
|
||||||
// within FrameLayerBuilder
|
// within FrameLayerBuilder
|
||||||
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
|
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
|
||||||
false);
|
aRebuildDisplayItems);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -423,8 +423,9 @@ void nsTableColGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey,
|
|||||||
bool aRebuildDisplayItems) {
|
bool aRebuildDisplayItems) {
|
||||||
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
|
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
|
||||||
if (GetTableFrame()->IsBorderCollapse()) {
|
if (GetTableFrame()->IsBorderCollapse()) {
|
||||||
|
const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
|
||||||
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
|
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
|
||||||
aDisplayItemKey, false);
|
aDisplayItemKey, rebuild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +438,7 @@ void nsTableColGroupFrame::InvalidateFrameWithRect(const nsRect& aRect,
|
|||||||
// we get an inactive layer created and this is computed
|
// we get an inactive layer created and this is computed
|
||||||
// within FrameLayerBuilder
|
// within FrameLayerBuilder
|
||||||
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
|
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
|
||||||
false);
|
aRebuildDisplayItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_FRAME_DUMP
|
#ifdef DEBUG_FRAME_DUMP
|
||||||
|
|||||||
@@ -1388,8 +1388,9 @@ void nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey,
|
|||||||
bool aRebuildDisplayItems) {
|
bool aRebuildDisplayItems) {
|
||||||
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
|
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
|
||||||
if (GetTableFrame()->IsBorderCollapse()) {
|
if (GetTableFrame()->IsBorderCollapse()) {
|
||||||
|
const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
|
||||||
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
|
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
|
||||||
aDisplayItemKey, false);
|
aDisplayItemKey, rebuild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1402,7 +1403,7 @@ void nsTableRowFrame::InvalidateFrameWithRect(const nsRect& aRect,
|
|||||||
// we get an inactive layer created and this is computed
|
// we get an inactive layer created and this is computed
|
||||||
// within FrameLayerBuilder
|
// within FrameLayerBuilder
|
||||||
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
|
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
|
||||||
false);
|
aRebuildDisplayItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----- global methods ----- */
|
/* ----- global methods ----- */
|
||||||
|
|||||||
@@ -1933,8 +1933,9 @@ void nsTableRowGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey,
|
|||||||
bool aRebuildDisplayItems) {
|
bool aRebuildDisplayItems) {
|
||||||
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
|
nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
|
||||||
if (GetTableFrame()->IsBorderCollapse()) {
|
if (GetTableFrame()->IsBorderCollapse()) {
|
||||||
|
const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
|
||||||
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
|
GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
|
||||||
aDisplayItemKey, false);
|
aDisplayItemKey, rebuild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1947,5 +1948,5 @@ void nsTableRowGroupFrame::InvalidateFrameWithRect(const nsRect& aRect,
|
|||||||
// we get an inactive layer created and this is computed
|
// we get an inactive layer created and this is computed
|
||||||
// within FrameLayerBuilder
|
// within FrameLayerBuilder
|
||||||
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
|
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
|
||||||
false);
|
aRebuildDisplayItems);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7754,6 +7754,11 @@
|
|||||||
value: true
|
value: true
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
|
- name: layout.display-list.retain.sc
|
||||||
|
type: RelaxedAtomicBool
|
||||||
|
value: false
|
||||||
|
mirror: always
|
||||||
|
|
||||||
# Set the maximum number of modified frames allowed before doing a full
|
# Set the maximum number of modified frames allowed before doing a full
|
||||||
# display list rebuild.
|
# display list rebuild.
|
||||||
- name: layout.display-list.rebuild-frame-limit
|
- name: layout.display-list.rebuild-frame-limit
|
||||||
|
|||||||
Reference in New Issue
Block a user