Bug 1404181 - Part 23: Only rebuild items within a displayport when the displayport changes, rather than rebuilding the whole document. r?mstange
MozReview-Commit-ID: IYEPCKSvtBY
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -469,16 +469,19 @@ nsDOMWindowUtils::SetDisplayPortForEleme
nsLayoutUtils::UsesAsyncScrolling(rootScrollFrame))
{
// We are setting a root displayport for a document.
// The pres shell needs a special flag set.
presShell->SetIgnoreViewportScrolling(true);
}
}
+ nsLayoutUtils::InvalidateForDisplayPortChange(content, !!currentData,
+ currentData ? currentData->mRect : nsRect(), displayport);
+
nsIFrame* rootFrame = presShell->FrameManager()->GetRootFrame();
if (rootFrame) {
rootFrame->SchedulePaint();
// If we are hiding something that is a display root then send empty paint
// transaction in order to release retained layers because it won't get
// any more paint requests when it is hidden.
if (displayport.IsEmpty() &&
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1370,16 +1370,64 @@ nsLayoutUtils::GetDisplayPortForVisibili
bool usingDisplayPort = GetDisplayPortImpl(aContent, aResult, 1.0f,
MaxSizeExceededBehaviour::eDrop);
if (usingDisplayPort && aRelativeTo == RelativeTo::ScrollFrame) {
TranslateFromScrollPortToScrollFrame(aContent, aResult);
}
return usingDisplayPort;
}
+void
+nsLayoutUtils::InvalidateForDisplayPortChange(nsIContent* aContent,
+ bool aHadDisplayPort,
+ const nsRect& aOldDisplayPort,
+ const nsRect& aNewDisplayPort,
+ RepaintMode aRepaintMode)
+{
+ if (aRepaintMode != RepaintMode::Repaint) {
+ return;
+ }
+
+ bool changed = !aHadDisplayPort ||
+ !aOldDisplayPort.IsEqualEdges(aNewDisplayPort);
+
+ nsIFrame* frame = GetScrollFrameFromContent(aContent);
+ if (frame) {
+ frame = do_QueryFrame(frame->GetScrollTargetFrame());
+ }
+
+ if (changed && frame) {
+ // It is important to call SchedulePaint on the same frame that we set the dirty
+ // rect properties on so we can find the frame later to remove the properties.
+ frame->SchedulePaint();
+
+ nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(frame);
+ RetainedDisplayListBuilder* retainedBuilder =
+ displayRoot->GetProperty(RetainedDisplayListBuilder::Cached());
+ if (retainedBuilder) {
+ nsRect* rect =
+ frame->GetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect());
+ if (!rect) {
+ rect = new nsRect();
+ frame->SetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), rect);
+ frame->SetHasOverrideDirtyRegion(true);
+ }
+ if (aHadDisplayPort) {
+ // We only need to build a display list for any new areas added
+ nsRegion newRegion(aNewDisplayPort);
+ newRegion.SubOut(aOldDisplayPort);
+ rect->UnionRect(*rect, newRegion.GetBounds());
+ } else {
+ rect->UnionRect(*rect, aNewDisplayPort);
+ }
+ }
+ }
+
+}
+
bool
nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent,
nsIPresShell* aPresShell,
const ScreenMargin& aMargins,
uint32_t aPriority,
RepaintMode aRepaintMode)
{
MOZ_ASSERT(aContent);
@@ -1398,38 +1446,31 @@ nsLayoutUtils::SetDisplayPortMargins(nsI
new DisplayPortMarginsPropertyData(
aMargins, aPriority),
nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
nsRect newDisplayPort;
DebugOnly<bool> hasDisplayPort = GetHighResolutionDisplayPort(aContent, &newDisplayPort);
MOZ_ASSERT(hasDisplayPort);
- bool changed = !hadDisplayPort ||
- !oldDisplayPort.IsEqualEdges(newDisplayPort);
-
if (gfxPrefs::LayoutUseContainersForRootFrames()) {
nsIFrame* rootScrollFrame = aPresShell->GetRootScrollFrame();
if (rootScrollFrame &&
aContent == rootScrollFrame->GetContent() &&
nsLayoutUtils::UsesAsyncScrolling(rootScrollFrame))
{
// We are setting a root displayport for a document.
// If we have APZ, then set a special flag on the pres shell so
// that we don't get scrollbars drawn.
aPresShell->SetIgnoreViewportScrolling(true);
}
}
- if (changed && aRepaintMode == RepaintMode::Repaint) {
- nsIFrame* frame = aContent->GetPrimaryFrame();
- if (frame) {
- frame->SchedulePaint();
- }
- }
+ InvalidateForDisplayPortChange(aContent, hadDisplayPort, oldDisplayPort,
+ newDisplayPort, aRepaintMode);
nsIFrame* frame = GetScrollFrameFromContent(aContent);
nsIScrollableFrame* scrollableFrame = frame ? frame->GetScrollTargetFrame() : nullptr;
if (!scrollableFrame) {
return true;
}
scrollableFrame->TriggerDisplayPortExpiration();
@@ -3718,17 +3759,17 @@ nsLayoutUtils::PaintFrame(gfxContext* aR
}
}
builder.ClearHaveScrollableDisplayPort();
if (builder.IsPaintingToWindow()) {
MaybeCreateDisplayPortInFirstScrollFrameEncountered(aFrame, builder);
}
- nsRect dirtyRect = visibleRegion.GetBounds();
+ nsRect visibleRect = visibleRegion.GetBounds();
{
AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame:BuildDisplayList",
GRAPHICS);
AUTO_PROFILER_TRACING("Paint", "DisplayList");
PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::DisplayList);
TimeStamp dlStart = TimeStamp::Now();
@@ -3755,34 +3796,34 @@ nsLayoutUtils::PaintFrame(gfxContext* aR
// nsGfxScrollFrame::BuilDisplayList will do it instead.
if (dom::Element* element = presShell->GetDocument()->GetDocumentElement()) {
id = nsLayoutUtils::FindOrCreateIDFor(element);
}
}
nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(&builder, id);
- builder.SetVisibleRect(dirtyRect);
+ builder.SetVisibleRect(visibleRect);
builder.SetIsBuilding(true);
const bool paintedPreviously =
aFrame->HasProperty(nsIFrame::ModifiedFrameList());
// Attempt to do a partial build and merge into the existing list.
// This calls BuildDisplayListForStacking context on a subset of the
// viewport.
bool merged = false;
if (retainedBuilder && paintedPreviously) {
merged = retainedBuilder->AttemptPartialUpdate(aBackstop);
}
if (!merged) {
list.DeleteAll(&builder);
builder.EnterPresShell(aFrame);
- builder.SetDirtyRect(dirtyRect);
+ builder.SetDirtyRect(visibleRect);
builder.ClearWindowDraggingRegion();
aFrame->BuildDisplayListForStackingContext(&builder, &list);
AddExtraBackgroundItems(builder, list, aFrame, canvasArea, visibleRegion, aBackstop);
builder.LeavePresShell(aFrame, &list);
}
}
@@ -3833,17 +3874,17 @@ nsLayoutUtils::PaintFrame(gfxContext* aR
" image.removeAttribute('src');\n"
" } else {\n"
" image.src = array[index];\n"
" }\n"
"}</script></head><body>";
}
#endif
*ss << nsPrintfCString("Painting --- before optimization (dirty %d,%d,%d,%d):\n",
- dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height).get();
+ visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height).get();
nsFrame::PrintDisplayList(&builder, list, *ss, gfxEnv::DumpPaintToFile());
if (gfxEnv::DumpPaint() || gfxEnv::DumpPaintItems()) {
// Flush stream now to avoid reordering dump output relative to
// messages dumped by PaintRoot below.
fprint_stderr(gfxUtils::sDumpPaintFile, *ss);
ss = MakeUnique<std::stringstream>();
}
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -248,16 +248,25 @@ public:
RelativeTo aRelativeTo = RelativeTo::ScrollPort);
enum class RepaintMode : uint8_t {
Repaint,
DoNotRepaint
};
/**
+ * Invalidate for displayport change.
+ */
+ static void InvalidateForDisplayPortChange(nsIContent* aContent,
+ bool aHadDisplayPort,
+ const nsRect& aOldDisplayPort,
+ const nsRect& aNewDisplayPort,
+ RepaintMode aRepaintMode = RepaintMode::Repaint);
+
+ /**
* Set the display port margins for a content element to be used with a
* display port base (see SetDisplayPortBase()).
* See also nsIDOMWindowUtils.setDisplayPortMargins.
* @param aContent the content element for which to set the margins
* @param aPresShell the pres shell for the document containing the element
* @param aMargins the margins to set
* @param aAlignmentX, alignmentY the amount of pixels to which to align the
* displayport built by combining the base
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3297,16 +3297,19 @@ ScrollFrameHelper::BuildDisplayList(nsDi
if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) {
mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint;
}
} else {
mScrollPosForLayerPixelAlignment = nsPoint(-1,-1);
}
}
+ // It's safe to get this value before the DecideScrollableLayer call below
+ // because that call cannot create a displayport for root scroll frames,
+ // and hence it cannot create an ignore scroll frame.
bool ignoringThisScrollFrame =
aBuilder->GetIgnoreScrollFrame() == mOuter || IsIgnoringViewportClipping();
// Overflow clipping can never clip frames outside our subtree, so there
// is no need to worry about whether we are a moving frame that might clip
// non-moving frames.
// Not all our descendants will be clipped by overflow clipping, but all
// the ones that aren't clipped will be out of flow frames that have already