--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -122,16 +122,17 @@
#include "mozilla/StyleSetHandle.h"
#include "mozilla/StyleSetHandleInlines.h"
#include "RegionBuilder.h"
#include "SVGSVGElement.h"
#include "nsAutoLayoutPhase.h"
#include "DisplayItemClip.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "prenv.h"
+#include "RetainedDisplayListBuilder.h"
#ifdef MOZ_XUL
#include "nsXULPopupManager.h"
#endif
#include "GeckoProfiler.h"
#include "nsAnimationManager.h"
#include "nsTransitionManager.h"
@@ -3539,384 +3540,16 @@ nsLayoutUtils::ExpireDisplayPortOnAsyncS
// to be ancestors of |scrollAncestor| which will keep its displayport.
// If the trigger succeeded, we stop because when the trigger executes
// it will call this function again to trigger the next ancestor up the
// chain.
break;
}
}
}
-bool IsAnyAncestorModified(nsIFrame* aFrame)
-{
- nsIFrame* f = aFrame;
- while (f) {
- if (f->IsFrameModified()) {
- return true;
- }
- f = nsLayoutUtils::GetCrossDocParentFrame(f);
- }
- return false;
-}
-
-// Removes any display items that belonged to a frame that was deleted,
-// and mark frames that belong to a different AGR so that get their
-// items built again.
-// TODO: We currently descend into all children even if we don't have an AGR
-// to mark, as child stacking contexts might. It would be nice if we could
-// jump into those immediately rather than walking the entire thing.
-void PreProcessRetainedDisplayList(nsDisplayListBuilder* aBuilder,
- nsDisplayList* aList,
- AnimatedGeometryRoot* aAGR)
-{
- nsDisplayList saved;
- while (nsDisplayItem* i = aList->RemoveBottom()) {
- if (i->HasDeletedFrame() || !i->CanBeReused()) {
- i->Destroy(aBuilder);
- continue;
- }
-
- if (i->GetChildren()) {
- AnimatedGeometryRoot *childAGR = aAGR;
- if (i->Frame()->IsStackingContext()) {
- if (i->Frame()->HasOverrideDirtyRegion()) {
- nsDisplayListBuilder::DisplayListBuildingData* data =
- i->Frame()->GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
- if (data) {
- childAGR = data->mModifiedAGR;
- }
- } else {
- childAGR = nullptr;
- }
- }
- PreProcessRetainedDisplayList(aBuilder, i->GetChildren(), childAGR);
- }
-
- // TODO: We should be able to check the clipped bounds relative
- // to the common AGR (of both the existing item and the invalidated
- // frame) and determine if they can ever intersect.
- if (aAGR && i->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
- aBuilder->MarkFrameForDisplayIfVisible(i->Frame());
- }
-
- // TODO: This is here because we sometimes reuse the previous display list
- // completely. For optimization, we could only restore the state for reused
- // display items.
- i->RestoreState();
-
- saved.AppendToTop(i);
- }
- aList->AppendToTop(&saved);
- aList->RestoreState();
-}
-
-bool IsSameItem(nsDisplayItem* aFirst, nsDisplayItem* aSecond)
-{
- return aFirst->Frame() == aSecond->Frame() &&
- aFirst->GetPerFrameKey() == aSecond->GetPerFrameKey();
-}
-
-struct DisplayItemKey {
-
- bool operator ==(const DisplayItemKey& aOther) const {
- return mFrame == aOther.mFrame &&
- mKey == aOther.mKey;
- }
-
- nsIFrame* mFrame;
- uint32_t mKey;
-};
-
-class DisplayItemHashEntry : public PLDHashEntryHdr
-{
-public:
- typedef DisplayItemKey KeyType;
- typedef const DisplayItemKey* KeyTypePointer;
-
- explicit DisplayItemHashEntry(KeyTypePointer aKey)
- : mKey(*aKey) {}
- explicit DisplayItemHashEntry(const DisplayItemHashEntry& aCopy)=default;
-
- ~DisplayItemHashEntry() = default;
-
- KeyType GetKey() const { return mKey; }
- bool KeyEquals(KeyTypePointer aKey) const
- {
- return mKey == *aKey;
- }
-
- static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
- static PLDHashNumber HashKey(KeyTypePointer aKey)
- {
- if (!aKey)
- return 0;
-
- return mozilla::HashGeneric(aKey->mFrame, aKey->mKey);
- }
- enum { ALLOW_MEMMOVE = true };
-
- DisplayItemKey mKey;
-};
-
-template<typename T>
-void SwapAndRemove(nsTArray<T>& aArray, uint32_t aIndex)
-{
- if (aIndex != (aArray.Length() - 1)) {
- T last = aArray.LastElement();
- aArray.LastElement() = aArray[aIndex];
- aArray[aIndex] = last;
- }
-
- aArray.RemoveElementAt(aArray.Length() - 1);
-}
-
-void MergeFrameRects(nsDisplayLayerEventRegions* aOldItem,
- nsDisplayLayerEventRegions* aNewItem,
- nsDisplayLayerEventRegions::FrameRects nsDisplayLayerEventRegions::*aRectList,
- bool aUpdateOld,
- nsTArray<nsIFrame*>& aAddedFrames)
-{
- // Go through the old item's rect list and remove any rectangles
- // belonging to invalidated frames (deleted frames should
- // already be gone at this point)
- nsDisplayLayerEventRegions::FrameRects& oldRects = aOldItem->*aRectList;
- uint32_t i = 0;
- while (i < oldRects.mFrames.Length()) {
- // TODO: As mentioned in nsDisplayLayerEventRegions, this
- // operation might perform really poorly on a vector.
- nsIFrame* f = oldRects.mFrames[i];
- if (IsAnyAncestorModified(f)) {
- MOZ_ASSERT(f != aOldItem->Frame());
- f->RealDisplayItemData().RemoveElement(aOldItem);
- SwapAndRemove(oldRects.mFrames, i);
- SwapAndRemove(oldRects.mBoxes, i);
- } else {
- i++;
- }
- }
- if (!aNewItem) {
- return;
- }
-
- // Copy items from the source list to the dest list, but
- // only if the dest doesn't already include them.
- nsDisplayItem* destItem;
- nsDisplayLayerEventRegions::FrameRects* destRects;
- nsDisplayLayerEventRegions::FrameRects* srcRects;
- if (aUpdateOld) {
- destItem = aOldItem;
- destRects = &(aOldItem->*aRectList);
- srcRects = &(aNewItem->*aRectList);
- } else {
- destItem = aNewItem;
- destRects = &(aNewItem->*aRectList);
- srcRects = &(aOldItem->*aRectList);
- }
-
- for (uint32_t i = 0; i < srcRects->mFrames.Length(); i++) {
- nsIFrame* f = srcRects->mFrames[i];
- if (!f->RealDisplayItemData().Contains(destItem)) {
- // If this frame isn't already in the destination item,
- // then add it!
- destRects->Add(f, srcRects->mBoxes[i]);
-
- // We also need to update RealDisplayItemData for 'f',
- // but that'll mess up this check for the following
- // FrameRects lists, so defer that until the end.
- aAddedFrames.AppendElement(f);
- MOZ_ASSERT(f != aOldItem->Frame());
- }
-
- }
-}
-
-void MergeLayerEventRegions(nsDisplayItem* aOldItem,
- nsDisplayItem* aNewItem,
- bool aUpdateOld)
-{
- nsDisplayLayerEventRegions* oldItem =
- static_cast<nsDisplayLayerEventRegions*>(aOldItem);
- nsDisplayLayerEventRegions* newItem =
- static_cast<nsDisplayLayerEventRegions*>(aNewItem);
-
- nsTArray<nsIFrame*> addedFrames;
-
- MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHitRegion, aUpdateOld, addedFrames);
- MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mMaybeHitRegion, aUpdateOld, addedFrames);
- MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mDispatchToContentHitRegion, aUpdateOld, addedFrames);
- MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mNoActionRegion, aUpdateOld, addedFrames);
- MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHorizontalPanRegion, aUpdateOld, addedFrames);
- MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mVerticalPanRegion, aUpdateOld, addedFrames);
-
- // MergeFrameRects deferred updating the display item data list during
- // processing so that earlier calls didn't change the result of later
- // ones. Fix that up now.
- nsDisplayItem* dest = aUpdateOld ? aOldItem : aNewItem;
- for (nsIFrame* f : addedFrames) {
- if (!f->RealDisplayItemData().Contains(dest)) {
- f->RealDisplayItemData().AppendElement(dest);
- }
- }
-}
-
-static void
-IncrementSubDocPresShellPaintCount(nsDisplayListBuilder* aBuilder,
- nsDisplayItem* aItem)
-{
- MOZ_ASSERT(aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT);
-
- nsSubDocumentFrame* subDocFrame =
- static_cast<nsDisplaySubDocument*>(aItem)->SubDocumentFrame();
- MOZ_ASSERT(subDocFrame);
-
- nsIPresShell* presShell = subDocFrame->GetSubdocumentPresShellForPainting(0);
- MOZ_ASSERT(presShell);
-
- aBuilder->IncrementPresShellPaintCount(presShell);
-}
-
-static void
-MergeDisplayLists(nsDisplayListBuilder* aBuilder,
- nsDisplayList* aNewList,
- nsDisplayList* aOldList,
- nsDisplayList* aOutList,
- uint32_t& aTotalDisplayItems,
- uint32_t& aReusedDisplayItems)
-{
- nsDisplayList merged;
- nsDisplayItem* old;
-
- const auto ReuseItem = [&](nsDisplayItem* aItem) {
- merged.AppendToTop(aItem);
- aTotalDisplayItems++;
- aReusedDisplayItems++;
- aItem->SetReused(true);
-
- if (aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
- IncrementSubDocPresShellPaintCount(aBuilder, aItem);
- }
- };
-
- nsDataHashtable<DisplayItemHashEntry, nsDisplayItem*> oldListLookup(aOldList->Count());
-
- for (nsDisplayItem* i = aOldList->GetBottom(); i != nullptr; i = i->GetAbove()) {
- i->SetReused(false);
-
- if (!aNewList->IsEmpty()) {
- oldListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i);
- }
- }
-
-#ifdef DEBUG
- nsDataHashtable<DisplayItemHashEntry, nsDisplayItem*> newListLookup(aNewList->Count());
- for (nsDisplayItem* i = aNewList->GetBottom(); i != nullptr; i = i->GetAbove()) {
- if (newListLookup.Get({ i->Frame(), i->GetPerFrameKey() }, nullptr)) {
- MOZ_CRASH_UNSAFE_PRINTF("Duplicate display items detected!: %s(0x%p) type=%d key=%d",
- i->Name(), i->Frame(),
- static_cast<int>(i->GetType()), i->GetPerFrameKey());
- }
- newListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i);
- }
-#endif
-
- while (nsDisplayItem* i = aNewList->RemoveBottom()) {
- // If the new item has a matching counterpart in the old list, copy all items
- // up to that one into the merged list, but discard the repeat.
- if (nsDisplayItem* oldItem = oldListLookup.Get({ i->Frame(), i->GetPerFrameKey() })) {
- if (oldItem->IsReused()) {
- // If we've already put the old item into the merged list (we might have iterated over it earlier)
- // then stick with that one. Merge any child lists, and then delete the new item.
-
- if (oldItem->GetChildren()) {
- MOZ_ASSERT(i->GetChildren());
- MergeDisplayLists(aBuilder, i->GetChildren(),
- oldItem->GetChildren(), oldItem->GetChildren(),
- aTotalDisplayItems, aReusedDisplayItems);
- oldItem->UpdateBounds(aBuilder);
- }
- if (oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
- MergeLayerEventRegions(oldItem, i, true);
- }
- i->Destroy(aBuilder);
- } else {
- while ((old = aOldList->RemoveBottom()) && !IsSameItem(i, old)) {
- if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
- ReuseItem(old);
- } else {
- // TODO: Is it going to be safe to call the dtor on a display item that belongs
- // to a deleted frame? Can we ensure that it is? Or do we need to make sure we
- // destroy display items during frame deletion.
- oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
- old->Destroy(aBuilder);
- }
- }
- // Recursively merge any child lists.
- // TODO: We may need to call UpdateBounds on any non-flattenable nsDisplayWrapLists
- // here. Is there any other cached state that we need to update?
- MOZ_ASSERT(old && IsSameItem(i, old));
-
- if (old->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
- !IsAnyAncestorModified(old->FrameForInvalidation())) {
- // Event regions items don't have anything interesting other than
- // the lists of regions and frames, so we have no need to use the
- // newer item. Always use the old item instead since we assume it's
- // likely to have the bigger lists and merging will be quicker.
- MergeLayerEventRegions(old, i, true);
- ReuseItem(old);
- i->Destroy(aBuilder);
- } else {
- if (!IsAnyAncestorModified(old->FrameForInvalidation()) &&
- old->GetChildren()) {
- MOZ_ASSERT(i->GetChildren());
- MergeDisplayLists(aBuilder, i->GetChildren(),
- old->GetChildren(), i->GetChildren(),
- aTotalDisplayItems, aReusedDisplayItems);
- i->UpdateBounds(aBuilder);
- }
-
- old->Destroy(aBuilder);
- merged.AppendToTop(i);
- aTotalDisplayItems++;
- }
-
- }
- } else {
- merged.AppendToTop(i);
- aTotalDisplayItems++;
- }
- }
-
- MOZ_ASSERT(aNewList->IsEmpty());
-
- // Reuse the remaining items from the old display list.
- while ((old = aOldList->RemoveBottom())) {
- if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
- ReuseItem(old);
-
- if (old->GetChildren()) {
- // We are calling MergeDisplayLists() to ensure that the display items
- // with modified or deleted children will be correctly handled.
- // Passing an empty new display list as an argument skips the merging
- // loop above and jumps back here.
- nsDisplayList empty;
-
- MergeDisplayLists(aBuilder, &empty,
- old->GetChildren(), old->GetChildren(),
- aTotalDisplayItems, aReusedDisplayItems);
- old->UpdateBounds(aBuilder);
- }
- if (old->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
- MergeLayerEventRegions(old, nullptr, false);
- }
- } else {
- old->Destroy(aBuilder);
- }
- }
-
- aOutList->AppendToTop(&merged);
-}
bool
nsLayoutUtils::MaybeCreateDisplayPortInFirstScrollFrameEncountered(
nsIFrame* aFrame, nsDisplayListBuilder& aBuilder)
{
nsIScrollableFrame* sf = do_QueryFrame(aFrame);
if (sf) {
if (MaybeCreateDisplayPort(aBuilder, aFrame, RepaintMode::Repaint)) {
@@ -3945,23 +3578,23 @@ nsLayoutUtils::MaybeCreateDisplayPortInF
if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(child, aBuilder)) {
return true;
}
}
return false;
}
-static void
-AddExtraBackgroundItems(nsDisplayListBuilder& aBuilder,
- nsDisplayList& aList,
- nsIFrame* aFrame,
- const nsRect& aCanvasArea,
- const nsRegion& aVisibleRegion,
- nscolor aBackstop)
+void
+nsLayoutUtils::AddExtraBackgroundItems(nsDisplayListBuilder& aBuilder,
+ nsDisplayList& aList,
+ nsIFrame* aFrame,
+ const nsRect& aCanvasArea,
+ const nsRegion& aVisibleRegion,
+ nscolor aBackstop)
{
LayoutFrameType frameType = aFrame->Type();
nsPresContext* presContext = aFrame->PresContext();
nsIPresShell* presShell = presContext->PresShell();
// For the viewport frame in print preview/page layout we want to paint
// the grey background behind the page, not the canvas color.
if (frameType == LayoutFrameType::Viewport &&
@@ -3985,236 +3618,16 @@ AddExtraBackgroundItems(nsDisplayListBui
canvasArea.IntersectRect(aCanvasArea, canvasArea);
nsDisplayListBuilder::AutoBuildingDisplayList
buildingDisplayList(&aBuilder, aFrame, canvasArea, canvasArea, false);
presShell->AddCanvasBackgroundColorItem(
aBuilder, aList, aFrame, canvasArea, aBackstop);
}
}
-static void
-AddModifiedFramesFromRootFrame(std::vector<WeakFrame>& aFrames,
- nsIFrame* aRootFrame)
-{
- MOZ_ASSERT(aRootFrame);
-
- std::vector<WeakFrame>* frames =
- aRootFrame->GetProperty(nsIFrame::ModifiedFrameList());
-
- if (frames) {
- for (WeakFrame& frame : *frames) {
- aFrames.push_back(Move(frame));
- }
-
- frames->clear();
- }
-}
-
-static bool
-SubDocEnumCb(nsIDocument* aDocument, void* aData)
-{
- MOZ_ASSERT(aDocument);
- MOZ_ASSERT(aData);
-
- std::vector<WeakFrame>* modifiedFrames =
- static_cast<std::vector<WeakFrame>*>(aData);
-
- nsIPresShell* presShell = aDocument->GetShell();
- nsIFrame* rootFrame = presShell ? presShell->GetRootFrame() : nullptr;
-
- if (rootFrame) {
- AddModifiedFramesFromRootFrame(*modifiedFrames, rootFrame);
- }
-
- aDocument->EnumerateSubDocuments(SubDocEnumCb, aData);
- return true;
-}
-
-static std::vector<WeakFrame>
-GetModifiedFrames(nsIFrame* aDisplayRootFrame)
-{
- MOZ_ASSERT(aDisplayRootFrame);
-
- std::vector<WeakFrame> modifiedFrames;
- AddModifiedFramesFromRootFrame(modifiedFrames, aDisplayRootFrame);
-
- nsIDocument *rootdoc = aDisplayRootFrame->PresContext()->Document();
-
- if (rootdoc) {
- rootdoc->EnumerateSubDocuments(SubDocEnumCb, &modifiedFrames);
- }
-
- return modifiedFrames;
-}
-
-// ComputeRebuildRegion debugging
-// #define CRR_DEBUG 0
-#if CRR_DEBUG
-# define CRR_LOG(...) printf_stderr(__VA_ARGS__)
-#else
-# define CRR_LOG(...)
-#endif
-
-static bool
-ComputeRebuildRegion(nsDisplayListBuilder& aBuilder,
- std::vector<WeakFrame>& aModifiedFrames,
- nsIFrame* aDisplayRootFrame,
- const nsRect& aRootDirtyRect,
- nsRect* aOutDirty,
- AnimatedGeometryRoot** aOutModifiedAGR,
- nsTArray<nsIFrame*>* aOutFramesWithProps)
-{
- CRR_LOG("Computing rebuild regions for %d frames:\n", aModifiedFrames.size());
- for (nsIFrame* f : aModifiedFrames) {
- if (!f) {
- continue;
- }
-
- if (f->HasOverrideDirtyRegion()) {
- aOutFramesWithProps->AppendElement(f);
- }
-
- // TODO: There is almost certainly a faster way of doing this, probably can be combined with the ancestor
- // walk for TransformFrameRectToAncestor.
- AnimatedGeometryRoot* agr = aBuilder.FindAnimatedGeometryRootFor(f)->GetAsyncAGR();
-
- CRR_LOG("Processing frame %p with agr %p\n", f, agr->mFrame);
-
-
- // Convert the frame's overflow rect into the coordinate space
- // of the nearest stacking context that has an existing display item.
- // We store the overflow rect on that stacking context so that we build
- // all items that intersect that changed frame within the stacking context,
- // and then we use MarkFrameForDisplayIfVisible to make sure the stacking
- // context itself gets built. We don't need to build items that intersect outside
- // of the stacking context, since we know the stacking context item exists in
- // the old list, so we can trivially merge without needing other items.
- nsRect overflow = f->GetVisualOverflowRectRelativeToSelf();
- nsIFrame* currentFrame = f;
-
- while (currentFrame != aDisplayRootFrame) {
- overflow = nsLayoutUtils::TransformFrameRectToAncestor(currentFrame, overflow, aDisplayRootFrame,
- nullptr, nullptr,
- /* aStopAtStackingContextAndDisplayPort = */ true,
- ¤tFrame);
- MOZ_ASSERT(currentFrame);
-
- if (nsLayoutUtils::FrameHasDisplayPort(currentFrame)) {
- CRR_LOG("Frame belongs to displayport frame %p\n", currentFrame);
- nsIScrollableFrame* sf = do_QueryFrame(currentFrame);
- MOZ_ASSERT(sf);
- nsRect displayPort;
- DebugOnly<bool> hasDisplayPort =
- nsLayoutUtils::GetDisplayPort(currentFrame->GetContent(), &displayPort, RelativeTo::ScrollPort);
- MOZ_ASSERT(hasDisplayPort);
- // get it relative to the scrollport (from the scrollframe)
- nsRect r = overflow - sf->GetScrollPortRect().TopLeft();
- r.IntersectRect(r, displayPort);
- if (!r.IsEmpty()) {
- nsRect* rect =
- currentFrame->GetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect());
- if (!rect) {
- rect = new nsRect();
- currentFrame->SetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), rect);
- currentFrame->SetHasOverrideDirtyRegion(true);
- }
- rect->UnionRect(*rect, r);
- aOutFramesWithProps->AppendElement(currentFrame);
- CRR_LOG("Adding area to displayport draw area: %d %d %d %d\n", r.x, r.y, r.width, r.height);
-
- // TODO: Can we just use MarkFrameForDisplayIfVisible, plus MarkFramesForDifferentAGR to
- // ensure that this displayport, plus any items that move relative to it get rebuilt,
- // and then not contribute to the root dirty area?
- overflow = sf->GetScrollPortRect();
- } else {
- // Don't contribute to the root dirty area at all.
- overflow.SetEmpty();
- break;
- }
- }
-
- if (currentFrame->IsStackingContext()) {
- CRR_LOG("Frame belongs to stacking context frame %p\n", currentFrame);
- // If we found an intermediate stacking context with an existing display item
- // then we can store the dirty rect there and stop.
- if (currentFrame != aDisplayRootFrame &&
- currentFrame->RealDisplayItemData().Length() != 0) {
- aBuilder.MarkFrameForDisplayIfVisible(currentFrame);
-
- // Store the stacking context relative dirty area such
- // that display list building will pick it up when it
- // gets to it.
- nsDisplayListBuilder::DisplayListBuildingData* data =
- currentFrame->GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
- if (!data) {
- data = new nsDisplayListBuilder::DisplayListBuildingData;
- currentFrame->SetProperty(nsDisplayListBuilder::DisplayListBuildingRect(), data);
- currentFrame->SetHasOverrideDirtyRegion(true);
- aOutFramesWithProps->AppendElement(currentFrame);
- }
- data->mDirtyRect.UnionRect(data->mDirtyRect, overflow);
- CRR_LOG("Adding area to stacking context draw area: %d %d %d %d\n", overflow.x, overflow.y, overflow.width, overflow.height);
- if (!data->mModifiedAGR) {
- data->mModifiedAGR = agr;
- } else if (data->mModifiedAGR != agr) {
- data->mDirtyRect = currentFrame->GetVisualOverflowRectRelativeToSelf();
- CRR_LOG("Found multiple modified AGRs within this stacking context, giving up\n");
- }
-
- // Don't contribute to the root dirty area at all.
- agr = nullptr;
- overflow.SetEmpty();
- break;
- }
- }
- }
- aOutDirty->UnionRect(*aOutDirty, overflow);
- CRR_LOG("Adding area to root draw area: %d %d %d %d\n", overflow.x, overflow.y, overflow.width, overflow.height);
-
- // If we get changed frames from multiple AGRS, then just give up as it gets really complex to
- // track which items would need to be marked in MarkFramesForDifferentAGR.
- // TODO: We should store the modifiedAGR on the per-stacking context data and only do the
- // marking within the scope of the current stacking context.
- if (!*aOutModifiedAGR) {
- *aOutModifiedAGR = agr;
- } else if (agr && *aOutModifiedAGR != agr) {
- CRR_LOG("Found multiple AGRs in root stacking context, giving up\n");
- return false;
- }
- }
-
- return true;
-}
-
-void MarkFramesWithItemsAndImagesModified(nsDisplayList* aList)
-{
- for (nsDisplayItem* i = aList->GetBottom(); i != nullptr; i = i->GetAbove()) {
- if (!i->HasDeletedFrame() && i->CanBeReused() && !i->Frame()->IsFrameModified()) {
- // If we have existing cached geometry for this item, then check that for
- // whether we need to invalidate for a sync decode. If we don't, then
- // use the item's flags.
- DisplayItemData* data = FrameLayerBuilder::GetOldDataFor(i);
- bool invalidate = false;
- if (data &&
- data->GetGeometry()) {
- invalidate = data->GetGeometry()->InvalidateForSyncDecodeImages();
- } else if (!(i->GetFlags() & TYPE_RENDERS_NO_IMAGES)) {
- invalidate = true;
- }
-
- if (invalidate) {
- i->Frame()->MarkNeedsDisplayItemRebuild();
- }
- }
- if (i->GetChildren()) {
- MarkFramesWithItemsAndImagesModified(i->GetChildren());
- }
- }
-}
-
nsresult
nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
const nsRegion& aDirtyRegion, nscolor aBackstop,
nsDisplayListBuilderMode aBuilderMode,
PaintFrameFlags aFlags)
{
AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame", GRAPHICS);
@@ -4260,18 +3673,18 @@ nsLayoutUtils::PaintFrame(gfxContext* aR
if (!retainedBuilder) {
retainedBuilder =
new RetainedDisplayListBuilder(aFrame, aBuilderMode, buildCaret);
aFrame->SetProperty(RetainedDisplayListBuilder::Cached(), retainedBuilder);
}
MOZ_ASSERT(retainedBuilder);
- builderPtr = &retainedBuilder->mBuilder;
- listPtr = &retainedBuilder->mList;
+ builderPtr = retainedBuilder->Builder();
+ listPtr = retainedBuilder->List();
} else {
builderPtr = new nsDisplayListBuilder(aFrame, aBuilderMode, buildCaret);
listPtr = new nsDisplayList();
}
nsDisplayListBuilder& builder = *builderPtr;
nsDisplayList& list = *listPtr;
builder.BeginFrame();
@@ -4362,20 +3775,16 @@ nsLayoutUtils::PaintFrame(gfxContext* aR
{
AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame:BuildDisplayList",
GRAPHICS);
AutoProfilerTracing tracing("Paint", "DisplayList");
PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::DisplayList);
TimeStamp dlStart = TimeStamp::Now();
- if (retainedBuilder && builder.ShouldSyncDecodeImages()) {
- MarkFramesWithItemsAndImagesModified(&list);
- }
-
builder.EnterPresShell(aFrame);
{
// If a scrollable container layer is created in nsDisplayList::PaintForFrame,
// it will be the scroll parent for display items that are built in the
// BuildDisplayListForStackingContext call below. We need to set the scroll
// parent on the display list builder while we build those items, so that they
// can pick up their scroll parent's id.
ViewID id = FrameMetrics::NULL_SCROLL_ID;
@@ -4398,97 +3807,22 @@ nsLayoutUtils::PaintFrame(gfxContext* aR
}
nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(&builder, id);
builder.SetVisibleRect(dirtyRect);
builder.SetIsBuilding(true);
bool merged = false;
- uint32_t totalDisplayItems = 0;
- uint32_t reusedDisplayItems = 0;
const bool paintedPreviously =
aFrame->HasProperty(nsIFrame::ModifiedFrameList());
if (retainedBuilder && paintedPreviously) {
- builder.RemoveModifiedWindowDraggingRegion();
- std::vector<WeakFrame> modifiedFrames = GetModifiedFrames(aFrame);
- // printf("Attempting merge build with %lu modified frames\n", modifiedFrames.size());
-
- if (retainedBuilder->mPreviousCaret != builder.GetCaretFrame()) {
- if (retainedBuilder->mPreviousCaret) {
- builder.MarkFrameModifiedDuringBuilding(retainedBuilder->mPreviousCaret);
- }
-
- if (builder.GetCaretFrame()) {
- builder.MarkFrameModifiedDuringBuilding(builder.GetCaretFrame());
- }
-
- retainedBuilder->mPreviousCaret = builder.GetCaretFrame();
- }
-
- nsRect modifiedDirty;
- AnimatedGeometryRoot* modifiedAGR = nullptr;
- nsTArray<nsIFrame*> framesWithProps;
- if (!list.IsEmpty() &&
- ComputeRebuildRegion(builder, modifiedFrames, aFrame, dirtyRect,
- &modifiedDirty, &modifiedAGR, &framesWithProps)) {
- modifiedDirty.IntersectRect(modifiedDirty, dirtyRect);
-
- PreProcessRetainedDisplayList(&builder, &list, modifiedAGR);
-
- nsDisplayList modifiedDL;
- if (!modifiedDirty.IsEmpty() || !framesWithProps.IsEmpty()) {
- builder.SetDirtyRect(modifiedDirty);
- builder.SetPartialUpdate(true);
- aFrame->BuildDisplayListForStackingContext(&builder, &modifiedDL);
- AddExtraBackgroundItems(builder, modifiedDL, aFrame, canvasArea, visibleRegion, aBackstop);
- builder.SetPartialUpdate(false);
- //printf_stderr("Painting --- Modified list (dirty %d,%d,%d,%d):\n",
- // modifiedDirty.x, modifiedDirty.y, modifiedDirty.width, modifiedDirty.height);
- //nsFrame::PrintDisplayList(&builder, modifiedDL);
-
- builder.LeavePresShell(aFrame, &modifiedDL);
- builder.EnterPresShell(aFrame);
- } else {
- // TODO: We can also skip layer building and painting if
- // PreProcessRetainedDisplayList didn't end up changing anything
- // Invariant: display items should have their original state here.
- // printf_stderr("Skipping display list building since nothing needed to be done\n");
- }
-
- // |modifiedDL| can sometimes be empty here. We still perform the
- // display list merging to prune unused items (for example, items that
- // are not visible anymore) from the old list.
- // TODO: Optimization opportunity. In this case, MergeDisplayLists()
- // unnecessarily creates a hashtable of the old items.
- MergeDisplayLists(&builder, &modifiedDL, &list, &list,
- totalDisplayItems, reusedDisplayItems);
-
- //printf_stderr("Painting --- Merged list:\n");
- //nsFrame::PrintDisplayList(&builder, list);
-
- merged = true;
- }
-
- // TODO: Do we mark frames as modified during displaylist building? If
- // we do this isn't gonna work.
- for (nsIFrame* f : modifiedFrames) {
- if (f) {
- f->SetFrameIsModified(false);
- }
- }
- modifiedFrames.clear();
-
- for (nsIFrame* f: framesWithProps) {
- f->SetHasOverrideDirtyRegion(false);
- f->DeleteProperty(nsDisplayListBuilder::DisplayListBuildingRect());
- f->DeleteProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect());
- }
+ merged = retainedBuilder->AttemptPartialUpdate(&list, aFrame, aBackstop);
}
if (merged && gfxPrefs::LayoutDisplayListBuildTwice()) {
merged = false;
if (gfxPrefs::LayersDrawFPS()) {
if (RefPtr<LayerManager> lm = builder.GetWidgetLayerManager()) {
if (PaintTiming* pt = ClientLayerManager::MaybeGetPaintTiming(lm)) {
pt->dl2Ms() = (TimeStamp::Now() - dlStart).ToMilliseconds();
@@ -4500,18 +3834,16 @@ nsLayoutUtils::PaintFrame(gfxContext* aR
if (!merged) {
list.DeleteAll(&builder);
builder.SetDirtyRect(dirtyRect);
builder.ClearWindowDraggingRegion();
aFrame->BuildDisplayListForStackingContext(&builder, &list);
AddExtraBackgroundItems(builder, list, aFrame, canvasArea, visibleRegion, aBackstop);
}
-
- // printf("nsLayoutUtils::PaintFrame - recycled %d/%d (%.2f%%) display items\n", reusedDisplayItems, totalDisplayItems, reusedDisplayItems * 100 / float(totalDisplayItems));
}
builder.SetIsBuilding(false);
// if (XRE_IsContentProcess()) {
// printf_stderr("Painting --- Full list:\n");
// nsFrame::PrintDisplayList(&builder, list);
// }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2248,16 +2248,23 @@ public:
* created by script with XHTML.
*
* <body><p contenteditable="true"></p></body>
* returns nullptr because <body> isn't editable.
*/
static nsIContent*
GetEditableRootContentByContentEditable(nsIDocument* aDocument);
+ static void AddExtraBackgroundItems(nsDisplayListBuilder& aBuilder,
+ nsDisplayList& aList,
+ nsIFrame* aFrame,
+ const nsRect& aCanvasArea,
+ const nsRegion& aVisibleRegion,
+ nscolor aBackstop);
+
/**
* Returns true if the passed in prescontext needs the dark grey background
* that goes behind the page of a print preview presentation.
*/
static bool NeedsPrintPreviewBackground(nsPresContext* aPresContext);
/**
* Adds all font faces used in the frame tree starting from aFrame
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -76,16 +76,17 @@
#include "nsBlockFrame.h"
#include "nsDisplayList.h"
#include "nsSVGIntegrationUtils.h"
#include "nsSVGEffects.h"
#include "nsChangeHint.h"
#include "nsDeckFrame.h"
#include "nsSubDocumentFrame.h"
#include "SVGTextFrame.h"
+#include "RetainedDisplayListBuilder.h"
#include "gfxContext.h"
#include "nsAbsoluteContainingBlock.h"
#include "StickyScrollContainer.h"
#include "nsFontInflationData.h"
#include "nsRegion.h"
#include "nsIFrameInlines.h"
#include "nsStyleChangeList.h"
new file mode 100644
--- /dev/null
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -0,0 +1,674 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=2 sw=2 et tw=78:
+ * 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/.
+ */
+
+#include "RetainedDisplayListBuilder.h"
+#include "nsSubDocumentFrame.h"
+
+using namespace mozilla;
+
+void MarkFramesWithItemsAndImagesModified(nsDisplayList* aList)
+{
+ for (nsDisplayItem* i = aList->GetBottom(); i != nullptr; i = i->GetAbove()) {
+ if (!i->HasDeletedFrame() && i->CanBeReused() && !i->Frame()->IsFrameModified()) {
+ // If we have existing cached geometry for this item, then check that for
+ // whether we need to invalidate for a sync decode. If we don't, then
+ // use the item's flags.
+ DisplayItemData* data = FrameLayerBuilder::GetOldDataFor(i);
+ bool invalidate = false;
+ if (data &&
+ data->GetGeometry()) {
+ invalidate = data->GetGeometry()->InvalidateForSyncDecodeImages();
+ } else if (!(i->GetFlags() & TYPE_RENDERS_NO_IMAGES)) {
+ invalidate = true;
+ }
+
+ if (invalidate) {
+ i->Frame()->MarkNeedsDisplayItemRebuild();
+ }
+ }
+ if (i->GetChildren()) {
+ MarkFramesWithItemsAndImagesModified(i->GetChildren());
+ }
+ }
+}
+
+bool IsAnyAncestorModified(nsIFrame* aFrame)
+{
+ nsIFrame* f = aFrame;
+ while (f) {
+ if (f->IsFrameModified()) {
+ return true;
+ }
+ f = nsLayoutUtils::GetCrossDocParentFrame(f);
+ }
+ return false;
+}
+
+// Removes any display items that belonged to a frame that was deleted,
+// and mark frames that belong to a different AGR so that get their
+// items built again.
+// TODO: We currently descend into all children even if we don't have an AGR
+// to mark, as child stacking contexts might. It would be nice if we could
+// jump into those immediately rather than walking the entire thing.
+void
+RetainedDisplayListBuilder::PreProcessDisplayList(nsDisplayList* aList,
+ AnimatedGeometryRoot* aAGR)
+{
+ nsDisplayList saved;
+ while (nsDisplayItem* i = aList->RemoveBottom()) {
+ if (i->HasDeletedFrame() || !i->CanBeReused()) {
+ i->Destroy(&mBuilder);
+ continue;
+ }
+
+ if (i->GetChildren()) {
+ AnimatedGeometryRoot *childAGR = aAGR;
+ if (i->Frame()->IsStackingContext()) {
+ if (i->Frame()->HasOverrideDirtyRegion()) {
+ nsDisplayListBuilder::DisplayListBuildingData* data =
+ i->Frame()->GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
+ if (data) {
+ childAGR = data->mModifiedAGR;
+ }
+ } else {
+ childAGR = nullptr;
+ }
+ }
+ PreProcessDisplayList(i->GetChildren(), childAGR);
+ }
+
+ // TODO: We should be able to check the clipped bounds relative
+ // to the common AGR (of both the existing item and the invalidated
+ // frame) and determine if they can ever intersect.
+ if (aAGR && i->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
+ mBuilder.MarkFrameForDisplayIfVisible(i->Frame());
+ }
+
+ // TODO: This is here because we sometimes reuse the previous display list
+ // completely. For optimization, we could only restore the state for reused
+ // display items.
+ i->RestoreState();
+
+ saved.AppendToTop(i);
+ }
+ aList->AppendToTop(&saved);
+ aList->RestoreState();
+}
+
+bool IsSameItem(nsDisplayItem* aFirst, nsDisplayItem* aSecond)
+{
+ return aFirst->Frame() == aSecond->Frame() &&
+ aFirst->GetPerFrameKey() == aSecond->GetPerFrameKey();
+}
+
+struct DisplayItemKey {
+
+ bool operator ==(const DisplayItemKey& aOther) const {
+ return mFrame == aOther.mFrame &&
+ mKey == aOther.mKey;
+ }
+
+ nsIFrame* mFrame;
+ uint32_t mKey;
+};
+
+class DisplayItemHashEntry : public PLDHashEntryHdr
+{
+public:
+ typedef DisplayItemKey KeyType;
+ typedef const DisplayItemKey* KeyTypePointer;
+
+ explicit DisplayItemHashEntry(KeyTypePointer aKey)
+ : mKey(*aKey) {}
+ explicit DisplayItemHashEntry(const DisplayItemHashEntry& aCopy)=default;
+
+ ~DisplayItemHashEntry() = default;
+
+ KeyType GetKey() const { return mKey; }
+ bool KeyEquals(KeyTypePointer aKey) const
+ {
+ return mKey == *aKey;
+ }
+
+ static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey)
+ {
+ if (!aKey)
+ return 0;
+
+ return mozilla::HashGeneric(aKey->mFrame, aKey->mKey);
+ }
+ enum { ALLOW_MEMMOVE = true };
+
+ DisplayItemKey mKey;
+};
+
+template<typename T>
+void SwapAndRemove(nsTArray<T>& aArray, uint32_t aIndex)
+{
+ if (aIndex != (aArray.Length() - 1)) {
+ T last = aArray.LastElement();
+ aArray.LastElement() = aArray[aIndex];
+ aArray[aIndex] = last;
+ }
+
+ aArray.RemoveElementAt(aArray.Length() - 1);
+}
+
+void MergeFrameRects(nsDisplayLayerEventRegions* aOldItem,
+ nsDisplayLayerEventRegions* aNewItem,
+ nsDisplayLayerEventRegions::FrameRects nsDisplayLayerEventRegions::*aRectList,
+ bool aUpdateOld,
+ nsTArray<nsIFrame*>& aAddedFrames)
+{
+ // Go through the old item's rect list and remove any rectangles
+ // belonging to invalidated frames (deleted frames should
+ // already be gone at this point)
+ nsDisplayLayerEventRegions::FrameRects& oldRects = aOldItem->*aRectList;
+ uint32_t i = 0;
+ while (i < oldRects.mFrames.Length()) {
+ // TODO: As mentioned in nsDisplayLayerEventRegions, this
+ // operation might perform really poorly on a vector.
+ nsIFrame* f = oldRects.mFrames[i];
+ if (IsAnyAncestorModified(f)) {
+ MOZ_ASSERT(f != aOldItem->Frame());
+ f->RealDisplayItemData().RemoveElement(aOldItem);
+ SwapAndRemove(oldRects.mFrames, i);
+ SwapAndRemove(oldRects.mBoxes, i);
+ } else {
+ i++;
+ }
+ }
+ if (!aNewItem) {
+ return;
+ }
+
+ // Copy items from the source list to the dest list, but
+ // only if the dest doesn't already include them.
+ nsDisplayItem* destItem;
+ nsDisplayLayerEventRegions::FrameRects* destRects;
+ nsDisplayLayerEventRegions::FrameRects* srcRects;
+ if (aUpdateOld) {
+ destItem = aOldItem;
+ destRects = &(aOldItem->*aRectList);
+ srcRects = &(aNewItem->*aRectList);
+ } else {
+ destItem = aNewItem;
+ destRects = &(aNewItem->*aRectList);
+ srcRects = &(aOldItem->*aRectList);
+ }
+
+ for (uint32_t i = 0; i < srcRects->mFrames.Length(); i++) {
+ nsIFrame* f = srcRects->mFrames[i];
+ if (!f->RealDisplayItemData().Contains(destItem)) {
+ // If this frame isn't already in the destination item,
+ // then add it!
+ destRects->Add(f, srcRects->mBoxes[i]);
+
+ // We also need to update RealDisplayItemData for 'f',
+ // but that'll mess up this check for the following
+ // FrameRects lists, so defer that until the end.
+ aAddedFrames.AppendElement(f);
+ MOZ_ASSERT(f != aOldItem->Frame());
+ }
+
+ }
+}
+
+void MergeLayerEventRegions(nsDisplayItem* aOldItem,
+ nsDisplayItem* aNewItem,
+ bool aUpdateOld)
+{
+ nsDisplayLayerEventRegions* oldItem =
+ static_cast<nsDisplayLayerEventRegions*>(aOldItem);
+ nsDisplayLayerEventRegions* newItem =
+ static_cast<nsDisplayLayerEventRegions*>(aNewItem);
+
+ nsTArray<nsIFrame*> addedFrames;
+
+ MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHitRegion, aUpdateOld, addedFrames);
+ MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mMaybeHitRegion, aUpdateOld, addedFrames);
+ MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mDispatchToContentHitRegion, aUpdateOld, addedFrames);
+ MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mNoActionRegion, aUpdateOld, addedFrames);
+ MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHorizontalPanRegion, aUpdateOld, addedFrames);
+ MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mVerticalPanRegion, aUpdateOld, addedFrames);
+
+ // MergeFrameRects deferred updating the display item data list during
+ // processing so that earlier calls didn't change the result of later
+ // ones. Fix that up now.
+ nsDisplayItem* dest = aUpdateOld ? aOldItem : aNewItem;
+ for (nsIFrame* f : addedFrames) {
+ if (!f->RealDisplayItemData().Contains(dest)) {
+ f->RealDisplayItemData().AppendElement(dest);
+ }
+ }
+}
+
+void
+RetainedDisplayListBuilder::IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem)
+{
+ MOZ_ASSERT(aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT);
+
+ nsSubDocumentFrame* subDocFrame =
+ static_cast<nsDisplaySubDocument*>(aItem)->SubDocumentFrame();
+ MOZ_ASSERT(subDocFrame);
+
+ nsIPresShell* presShell = subDocFrame->GetSubdocumentPresShellForPainting(0);
+ MOZ_ASSERT(presShell);
+
+ mBuilder.IncrementPresShellPaintCount(presShell);
+}
+
+void
+RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
+ nsDisplayList* aOldList,
+ nsDisplayList* aOutList)
+{
+ nsDisplayList merged;
+ nsDisplayItem* old;
+
+ const auto ReuseItem = [&](nsDisplayItem* aItem) {
+ merged.AppendToTop(aItem);
+ aItem->SetReused(true);
+
+ if (aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
+ IncrementSubDocPresShellPaintCount(aItem);
+ }
+ };
+
+ nsDataHashtable<DisplayItemHashEntry, nsDisplayItem*> oldListLookup(aOldList->Count());
+
+ for (nsDisplayItem* i = aOldList->GetBottom(); i != nullptr; i = i->GetAbove()) {
+ i->SetReused(false);
+
+ if (!aNewList->IsEmpty()) {
+ oldListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i);
+ }
+ }
+
+#ifdef DEBUG
+ nsDataHashtable<DisplayItemHashEntry, nsDisplayItem*> newListLookup(aNewList->Count());
+ for (nsDisplayItem* i = aNewList->GetBottom(); i != nullptr; i = i->GetAbove()) {
+ if (newListLookup.Get({ i->Frame(), i->GetPerFrameKey() }, nullptr)) {
+ MOZ_CRASH_UNSAFE_PRINTF("Duplicate display items detected!: %s(0x%p) type=%d key=%d",
+ i->Name(), i->Frame(),
+ static_cast<int>(i->GetType()), i->GetPerFrameKey());
+ }
+ newListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i);
+ }
+#endif
+
+ while (nsDisplayItem* i = aNewList->RemoveBottom()) {
+ // If the new item has a matching counterpart in the old list, copy all items
+ // up to that one into the merged list, but discard the repeat.
+ if (nsDisplayItem* oldItem = oldListLookup.Get({ i->Frame(), i->GetPerFrameKey() })) {
+ if (oldItem->IsReused()) {
+ // If we've already put the old item into the merged list (we might have iterated over it earlier)
+ // then stick with that one. Merge any child lists, and then delete the new item.
+
+ if (oldItem->GetChildren()) {
+ MOZ_ASSERT(i->GetChildren());
+ MergeDisplayLists(i->GetChildren(), oldItem->GetChildren(), oldItem->GetChildren());
+ oldItem->UpdateBounds(&mBuilder);
+ }
+ if (oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
+ MergeLayerEventRegions(oldItem, i, true);
+ }
+ i->Destroy(&mBuilder);
+ } else {
+ while ((old = aOldList->RemoveBottom()) && !IsSameItem(i, old)) {
+ if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
+ ReuseItem(old);
+ } else {
+ // TODO: Is it going to be safe to call the dtor on a display item that belongs
+ // to a deleted frame? Can we ensure that it is? Or do we need to make sure we
+ // destroy display items during frame deletion.
+ oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
+ old->Destroy(&mBuilder);
+ }
+ }
+ // Recursively merge any child lists.
+ // TODO: We may need to call UpdateBounds on any non-flattenable nsDisplayWrapLists
+ // here. Is there any other cached state that we need to update?
+ MOZ_ASSERT(old && IsSameItem(i, old));
+
+ if (old->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
+ !IsAnyAncestorModified(old->FrameForInvalidation())) {
+ // Event regions items don't have anything interesting other than
+ // the lists of regions and frames, so we have no need to use the
+ // newer item. Always use the old item instead since we assume it's
+ // likely to have the bigger lists and merging will be quicker.
+ MergeLayerEventRegions(old, i, true);
+ ReuseItem(old);
+ i->Destroy(&mBuilder);
+ } else {
+ if (!IsAnyAncestorModified(old->FrameForInvalidation()) &&
+ old->GetChildren()) {
+ MOZ_ASSERT(i->GetChildren());
+ MergeDisplayLists(i->GetChildren(), old->GetChildren(), i->GetChildren());
+ i->UpdateBounds(&mBuilder);
+ }
+
+ old->Destroy(&mBuilder);
+ merged.AppendToTop(i);
+ }
+
+ }
+ } else {
+ merged.AppendToTop(i);
+ }
+ }
+
+ MOZ_ASSERT(aNewList->IsEmpty());
+
+ // Reuse the remaining items from the old display list.
+ while ((old = aOldList->RemoveBottom())) {
+ if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
+ ReuseItem(old);
+
+ if (old->GetChildren()) {
+ // We are calling MergeDisplayLists() to ensure that the display items
+ // with modified or deleted children will be correctly handled.
+ // Passing an empty new display list as an argument skips the merging
+ // loop above and jumps back here.
+ nsDisplayList empty;
+
+ MergeDisplayLists(&empty, old->GetChildren(), old->GetChildren());
+ old->UpdateBounds(&mBuilder);
+ }
+ if (old->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
+ MergeLayerEventRegions(old, nullptr, false);
+ }
+ } else {
+ old->Destroy(&mBuilder);
+ }
+ }
+
+ aOutList->AppendToTop(&merged);
+}
+
+static void
+AddModifiedFramesFromRootFrame(std::vector<WeakFrame>& aFrames,
+ nsIFrame* aRootFrame)
+{
+ MOZ_ASSERT(aRootFrame);
+
+ std::vector<WeakFrame>* frames =
+ aRootFrame->GetProperty(nsIFrame::ModifiedFrameList());
+
+ if (frames) {
+ for (WeakFrame& frame : *frames) {
+ aFrames.push_back(Move(frame));
+ }
+
+ frames->clear();
+ }
+}
+
+static bool
+SubDocEnumCb(nsIDocument* aDocument, void* aData)
+{
+ MOZ_ASSERT(aDocument);
+ MOZ_ASSERT(aData);
+
+ std::vector<WeakFrame>* modifiedFrames =
+ static_cast<std::vector<WeakFrame>*>(aData);
+
+ nsIPresShell* presShell = aDocument->GetShell();
+ nsIFrame* rootFrame = presShell ? presShell->GetRootFrame() : nullptr;
+
+ if (rootFrame) {
+ AddModifiedFramesFromRootFrame(*modifiedFrames, rootFrame);
+ }
+
+ aDocument->EnumerateSubDocuments(SubDocEnumCb, aData);
+ return true;
+}
+
+static std::vector<WeakFrame>
+GetModifiedFrames(nsIFrame* aDisplayRootFrame)
+{
+ MOZ_ASSERT(aDisplayRootFrame);
+
+ std::vector<WeakFrame> modifiedFrames;
+ AddModifiedFramesFromRootFrame(modifiedFrames, aDisplayRootFrame);
+
+ nsIDocument *rootdoc = aDisplayRootFrame->PresContext()->Document();
+
+ if (rootdoc) {
+ rootdoc->EnumerateSubDocuments(SubDocEnumCb, &modifiedFrames);
+ }
+
+ return modifiedFrames;
+}
+
+// ComputeRebuildRegion debugging
+// #define CRR_DEBUG 0
+#if CRR_DEBUG
+# define CRR_LOG(...) printf_stderr(__VA_ARGS__)
+#else
+# define CRR_LOG(...)
+#endif
+
+bool
+RetainedDisplayListBuilder::ComputeRebuildRegion(std::vector<WeakFrame>& aModifiedFrames,
+ nsIFrame* aDisplayRootFrame,
+ nsRect* aOutDirty,
+ AnimatedGeometryRoot** aOutModifiedAGR,
+ nsTArray<nsIFrame*>* aOutFramesWithProps)
+{
+ CRR_LOG("Computing rebuild regions for %d frames:\n", aModifiedFrames.size());
+ for (nsIFrame* f : aModifiedFrames) {
+ if (!f) {
+ continue;
+ }
+
+ if (f->HasOverrideDirtyRegion()) {
+ aOutFramesWithProps->AppendElement(f);
+ }
+
+ // TODO: There is almost certainly a faster way of doing this, probably can be combined with the ancestor
+ // walk for TransformFrameRectToAncestor.
+ AnimatedGeometryRoot* agr = mBuilder.FindAnimatedGeometryRootFor(f)->GetAsyncAGR();
+
+ CRR_LOG("Processing frame %p with agr %p\n", f, agr->mFrame);
+
+
+ // Convert the frame's overflow rect into the coordinate space
+ // of the nearest stacking context that has an existing display item.
+ // We store the overflow rect on that stacking context so that we build
+ // all items that intersect that changed frame within the stacking context,
+ // and then we use MarkFrameForDisplayIfVisible to make sure the stacking
+ // context itself gets built. We don't need to build items that intersect outside
+ // of the stacking context, since we know the stacking context item exists in
+ // the old list, so we can trivially merge without needing other items.
+ nsRect overflow = f->GetVisualOverflowRectRelativeToSelf();
+ nsIFrame* currentFrame = f;
+
+ while (currentFrame != aDisplayRootFrame) {
+ overflow = nsLayoutUtils::TransformFrameRectToAncestor(currentFrame, overflow, aDisplayRootFrame,
+ nullptr, nullptr,
+ /* aStopAtStackingContextAndDisplayPort = */ true,
+ ¤tFrame);
+ MOZ_ASSERT(currentFrame);
+
+ if (nsLayoutUtils::FrameHasDisplayPort(currentFrame)) {
+ CRR_LOG("Frame belongs to displayport frame %p\n", currentFrame);
+ nsIScrollableFrame* sf = do_QueryFrame(currentFrame);
+ MOZ_ASSERT(sf);
+ nsRect displayPort;
+ DebugOnly<bool> hasDisplayPort =
+ nsLayoutUtils::GetDisplayPort(currentFrame->GetContent(), &displayPort, RelativeTo::ScrollPort);
+ MOZ_ASSERT(hasDisplayPort);
+ // get it relative to the scrollport (from the scrollframe)
+ nsRect r = overflow - sf->GetScrollPortRect().TopLeft();
+ r.IntersectRect(r, displayPort);
+ if (!r.IsEmpty()) {
+ nsRect* rect =
+ currentFrame->GetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect());
+ if (!rect) {
+ rect = new nsRect();
+ currentFrame->SetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), rect);
+ currentFrame->SetHasOverrideDirtyRegion(true);
+ }
+ rect->UnionRect(*rect, r);
+ aOutFramesWithProps->AppendElement(currentFrame);
+ CRR_LOG("Adding area to displayport draw area: %d %d %d %d\n", r.x, r.y, r.width, r.height);
+
+ // TODO: Can we just use MarkFrameForDisplayIfVisible, plus MarkFramesForDifferentAGR to
+ // ensure that this displayport, plus any items that move relative to it get rebuilt,
+ // and then not contribute to the root dirty area?
+ overflow = sf->GetScrollPortRect();
+ } else {
+ // Don't contribute to the root dirty area at all.
+ overflow.SetEmpty();
+ break;
+ }
+ }
+
+ if (currentFrame->IsStackingContext()) {
+ CRR_LOG("Frame belongs to stacking context frame %p\n", currentFrame);
+ // If we found an intermediate stacking context with an existing display item
+ // then we can store the dirty rect there and stop.
+ if (currentFrame != aDisplayRootFrame &&
+ currentFrame->RealDisplayItemData().Length() != 0) {
+ mBuilder.MarkFrameForDisplayIfVisible(currentFrame);
+
+ // Store the stacking context relative dirty area such
+ // that display list building will pick it up when it
+ // gets to it.
+ nsDisplayListBuilder::DisplayListBuildingData* data =
+ currentFrame->GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
+ if (!data) {
+ data = new nsDisplayListBuilder::DisplayListBuildingData;
+ currentFrame->SetProperty(nsDisplayListBuilder::DisplayListBuildingRect(), data);
+ currentFrame->SetHasOverrideDirtyRegion(true);
+ aOutFramesWithProps->AppendElement(currentFrame);
+ }
+ data->mDirtyRect.UnionRect(data->mDirtyRect, overflow);
+ CRR_LOG("Adding area to stacking context draw area: %d %d %d %d\n", overflow.x, overflow.y, overflow.width, overflow.height);
+ if (!data->mModifiedAGR) {
+ data->mModifiedAGR = agr;
+ } else if (data->mModifiedAGR != agr) {
+ data->mDirtyRect = currentFrame->GetVisualOverflowRectRelativeToSelf();
+ CRR_LOG("Found multiple modified AGRs within this stacking context, giving up\n");
+ }
+
+ // Don't contribute to the root dirty area at all.
+ agr = nullptr;
+ overflow.SetEmpty();
+ break;
+ }
+ }
+ }
+ aOutDirty->UnionRect(*aOutDirty, overflow);
+ CRR_LOG("Adding area to root draw area: %d %d %d %d\n", overflow.x, overflow.y, overflow.width, overflow.height);
+
+ // If we get changed frames from multiple AGRS, then just give up as it gets really complex to
+ // track which items would need to be marked in MarkFramesForDifferentAGR.
+ // TODO: We should store the modifiedAGR on the per-stacking context data and only do the
+ // marking within the scope of the current stacking context.
+ if (!*aOutModifiedAGR) {
+ *aOutModifiedAGR = agr;
+ } else if (agr && *aOutModifiedAGR != agr) {
+ CRR_LOG("Found multiple AGRs in root stacking context, giving up\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool
+RetainedDisplayListBuilder::AttemptPartialUpdate(nsDisplayList* aList,
+ nsIFrame* aFrame,
+ nscolor aBackstop)
+{
+ mBuilder.RemoveModifiedWindowDraggingRegion();
+ if (mBuilder.ShouldSyncDecodeImages()) {
+ MarkFramesWithItemsAndImagesModified(&mList);
+ }
+
+ std::vector<WeakFrame> modifiedFrames = GetModifiedFrames(aFrame);
+
+ if (mPreviousCaret != mBuilder.GetCaretFrame()) {
+ if (mPreviousCaret) {
+ mBuilder.MarkFrameModifiedDuringBuilding(mPreviousCaret);
+ }
+
+ if (mBuilder.GetCaretFrame()) {
+ mBuilder.MarkFrameModifiedDuringBuilding(mBuilder.GetCaretFrame());
+ }
+
+ mPreviousCaret = mBuilder.GetCaretFrame();
+ }
+
+ nsRect modifiedDirty;
+ AnimatedGeometryRoot* modifiedAGR = nullptr;
+ nsTArray<nsIFrame*> framesWithProps;
+ bool merged = false;
+ if (!aList->IsEmpty() &&
+ ComputeRebuildRegion(modifiedFrames, aFrame, &modifiedDirty, &modifiedAGR, &framesWithProps)) {
+ modifiedDirty.IntersectRect(modifiedDirty, aFrame->GetVisualOverflowRectRelativeToSelf());
+
+ PreProcessDisplayList(aList, modifiedAGR);
+
+ nsDisplayList modifiedDL;
+ if (!modifiedDirty.IsEmpty() || !framesWithProps.IsEmpty()) {
+ mBuilder.SetDirtyRect(modifiedDirty);
+ mBuilder.SetPartialUpdate(true);
+ aFrame->BuildDisplayListForStackingContext(&mBuilder, &modifiedDL);
+ nsLayoutUtils::AddExtraBackgroundItems(mBuilder, modifiedDL, aFrame,
+ nsRect(nsPoint(0, 0), aFrame->GetSize()),
+ aFrame->GetVisualOverflowRectRelativeToSelf(),
+ aBackstop);
+ mBuilder.SetPartialUpdate(false);
+
+ //printf_stderr("Painting --- Modified list (dirty %d,%d,%d,%d):\n",
+ // modifiedDirty.x, modifiedDirty.y, modifiedDirty.width, modifiedDirty.height);
+ //nsFrame::PrintDisplayList(&builder, modifiedDL);
+
+ mBuilder.LeavePresShell(aFrame, &modifiedDL);
+ mBuilder.EnterPresShell(aFrame);
+ } else {
+ // TODO: We can also skip layer building and painting if
+ // PreProcessDisplayList didn't end up changing anything
+ // Invariant: display items should have their original state here.
+ // printf_stderr("Skipping display list building since nothing needed to be done\n");
+ }
+
+ // |modifiedDL| can sometimes be empty here. We still perform the
+ // display list merging to prune unused items (for example, items that
+ // are not visible anymore) from the old list.
+ // TODO: Optimization opportunity. In this case, MergeDisplayLists()
+ // unnecessarily creates a hashtable of the old items.
+ MergeDisplayLists(&modifiedDL, aList, aList);
+
+ //printf_stderr("Painting --- Merged list:\n");
+ //nsFrame::PrintDisplayList(&builder, list);
+
+ merged = true;
+ }
+
+ // TODO: Do we mark frames as modified during displaylist building? If
+ // we do this isn't gonna work.
+ for (nsIFrame* f : modifiedFrames) {
+ if (f) {
+ f->SetFrameIsModified(false);
+ }
+ }
+ modifiedFrames.clear();
+
+ for (nsIFrame* f: framesWithProps) {
+ f->SetHasOverrideDirtyRegion(false);
+ f->DeleteProperty(nsDisplayListBuilder::DisplayListBuildingRect());
+ f->DeleteProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect());
+ }
+
+ return merged;
+}
new file mode 100644
--- /dev/null
+++ b/layout/painting/RetainedDisplayListBuilder.h
@@ -0,0 +1,50 @@
+/* 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/. */
+
+#ifndef RETAINEDDISPLAYLISTBUILDER_H_
+#define RETAINEDDISPLAYLISTBUILDER_H_
+
+#include "nsDisplayList.h"
+
+struct RetainedDisplayListBuilder {
+ RetainedDisplayListBuilder(nsIFrame* aReferenceFrame,
+ nsDisplayListBuilderMode aMode,
+ bool aBuildCaret)
+ : mBuilder(aReferenceFrame, aMode, aBuildCaret, true)
+ {}
+ ~RetainedDisplayListBuilder()
+ {
+ mList.DeleteAll(&mBuilder);
+ }
+
+ nsDisplayListBuilder* Builder() { return &mBuilder; }
+
+ nsDisplayList* List() { return &mList; }
+
+ bool AttemptPartialUpdate(nsDisplayList* aList, nsIFrame* aFrame, nscolor aBackstop);
+
+ NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
+
+private:
+ void PreProcessDisplayList(nsDisplayList* aList, AnimatedGeometryRoot* aAGR);
+
+ void MergeDisplayLists(nsDisplayList* aNewList,
+ nsDisplayList* aOldList,
+ nsDisplayList* aOutList);
+
+ bool ComputeRebuildRegion(std::vector<WeakFrame>& aModifiedFrames,
+ nsIFrame* aDisplayRootFrame,
+ nsRect* aOutDirty,
+ AnimatedGeometryRoot** aOutModifiedAGR,
+ nsTArray<nsIFrame*>* aOutFramesWithProps);
+
+ void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);
+
+ nsDisplayListBuilder mBuilder;
+ nsDisplayList mList;
+ WeakFrame mPreviousCaret;
+
+};
+
+#endif // RETAINEDDISPLAYLISTBUILDER_H_
--- a/layout/painting/moz.build
+++ b/layout/painting/moz.build
@@ -17,16 +17,17 @@ EXPORTS += [
'LayerState.h',
'nsCSSRenderingBorders.h',
'nsCSSRenderingGradients.h',
'nsDisplayItemTypes.h',
'nsDisplayItemTypesList.h',
'nsDisplayList.h',
'nsDisplayListInvalidation.h',
'nsImageRenderer.h',
+ 'RetainedDisplayListBuilder.h',
]
EXPORTS.mozilla += [
'PaintTracker.h',
]
UNIFIED_SOURCES += [
'ActiveLayerTracker.cpp',
@@ -40,16 +41,17 @@ UNIFIED_SOURCES += [
'MaskLayerImageCache.cpp',
'nsCSSRendering.cpp',
'nsCSSRenderingBorders.cpp',
'nsCSSRenderingGradients.cpp',
'nsDisplayList.cpp',
'nsDisplayListInvalidation.cpp',
'nsImageRenderer.cpp',
'PaintTracker.cpp',
+ 'RetainedDisplayListBuilder.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
'/docshell/base',
'/dom/base',
'/layout/base',
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -3006,34 +3006,16 @@ private:
// is empty (i.e. everything that was visible is covered by some
// opaque content in this list).
bool mIsOpaque;
// This is set to true by FrameLayerBuilder if any display item in this
// list needs to force the surface containing this list to be transparent.
bool mForceTransparentSurface;
};
-struct RetainedDisplayListBuilder {
- RetainedDisplayListBuilder(nsIFrame* aReferenceFrame,
- nsDisplayListBuilderMode aMode,
- bool aBuildCaret)
- : mBuilder(aReferenceFrame, aMode, aBuildCaret, true)
- {}
- ~RetainedDisplayListBuilder()
- {
- mList.DeleteAll(&mBuilder);
- }
-
- nsDisplayListBuilder mBuilder;
- nsDisplayList mList;
- WeakFrame mPreviousCaret;
-
- NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
-};
-
/**
* This is passed as a parameter to nsIFrame::BuildDisplayList. That method
* will put any generated items onto the appropriate list given here. It's
* basically just a collection with one list for each separate stacking layer.
* The lists themselves are external to this object and thus can be shared
* with others. Some of the list pointers may even refer to the same list.
*/
class nsDisplayListSet {
--- a/layout/painting/nsImageRenderer.cpp
+++ b/layout/painting/nsImageRenderer.cpp
@@ -17,16 +17,17 @@
#include "nsContentUtils.h"
#include "nsCSSRendering.h"
#include "nsCSSRenderingGradients.h"
#include "nsIFrame.h"
#include "nsStyleStructInlines.h"
#include "nsSVGDisplayableFrame.h"
#include "nsSVGEffects.h"
#include "nsSVGIntegrationUtils.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::image;
using namespace mozilla::layers;
nsSize
CSSSizeOrRatio::ComputeConcreteSize() const