Bug 1547802 - Compute a single caret frame for the entire display list, and remove the option to invalidate frames during painting. r=miko,smaug

Previously we computed a caret frame each time we started display list building for a pres shell, and tracked a stack of these as we descended through subdocuments.
This meant that we couldn't know if the caret frame had changed before we started building, and we instead had to support invalidations in the middle of building.

Since there should only ever be one focused document, we can instead retrieve this from the focus manager, and find the sole caret frame for all documents we want to paint.

Differential Revision: https://phabricator.services.mozilla.com/D33880

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Matt Woodrow 2019-06-11 02:20:09 +00:00
Родитель 8f5e20cee0
Коммит 72068e14f5
6 изменённых файлов: 63 добавлений и 66 удалений

Просмотреть файл

@ -465,30 +465,6 @@ void nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
needsOwnLayer = true;
}
if (subdocRootFrame && aBuilder->IsRetainingDisplayList()) {
// Caret frame changed, rebuild the entire subdoc.
// We could just invalidate the old and new frame
// areas and save some work here. RetainedDisplayListBuilder
// does this, so we could teach it to find and check all
// subdocs in advance.
if (mPreviousCaret != aBuilder->GetCaretFrame()) {
dirty = visible;
aBuilder->MarkFrameModifiedDuringBuilding(subdocRootFrame);
aBuilder->RebuildAllItemsInCurrentSubtree();
// Mark the old caret frame as invalid so that we remove the
// old nsDisplayCaret. We don't mark the current frame as invalid
// since we want the nsDisplaySubdocument to retain it's place
// in the retained display list.
if (mPreviousCaret) {
aBuilder->MarkFrameModifiedDuringBuilding(mPreviousCaret);
}
if (aBuilder->GetCaretFrame()) {
aBuilder->MarkFrameModifiedDuringBuilding(aBuilder->GetCaretFrame());
}
}
mPreviousCaret = aBuilder->GetCaretFrame();
}
nsDisplayList childItems;
{

Просмотреть файл

@ -162,7 +162,6 @@ class nsSubDocumentFrame final : public nsAtomicContainerFrame,
bool mPostedReflowCallback;
bool mDidCreateDoc;
bool mCallingShow;
WeakFrame mPreviousCaret;
};
/**

Просмотреть файл

@ -1304,8 +1304,12 @@ bool RetainedDisplayListBuilder::ComputeRebuildRegion(
}
}
// Since we set modified to true on the extraFrames, add them to
// aModifiedFrames so that it will get reverted.
aModifiedFrames.AppendElements(extraFrames);
for (nsIFrame* f : extraFrames) {
mBuilder.MarkFrameModifiedDuringBuilding(f);
f->SetFrameIsModified(true);
if (!ProcessFrame(f, &mBuilder, mBuilder.RootReferenceFrame(),
aOutFramesWithProps, true, aOutDirty, aOutModifiedAGR)) {
@ -1361,21 +1365,18 @@ bool RetainedDisplayListBuilder::ShouldBuildPartial(
return true;
}
void RetainedDisplayListBuilder::InvalidateCaretFramesIfNeeded(
nsTArray<nsIFrame*>& aModifiedFrames) {
void RetainedDisplayListBuilder::InvalidateCaretFramesIfNeeded() {
if (mPreviousCaret == mBuilder.GetCaretFrame()) {
// The current caret frame is the same as the previous one.
return;
}
if (mPreviousCaret &&
mBuilder.MarkFrameModifiedDuringBuilding(mPreviousCaret)) {
aModifiedFrames.AppendElement(mPreviousCaret);
if (mPreviousCaret) {
mPreviousCaret->MarkNeedsDisplayItemRebuild();
}
if (mBuilder.GetCaretFrame() &&
mBuilder.MarkFrameModifiedDuringBuilding(mBuilder.GetCaretFrame())) {
aModifiedFrames.AppendElement(mBuilder.GetCaretFrame());
if (mBuilder.GetCaretFrame()) {
mBuilder.GetCaretFrame()->MarkNeedsDisplayItemRebuild();
}
mPreviousCaret = mBuilder.GetCaretFrame();
@ -1423,6 +1424,8 @@ PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
MarkFramesWithItemsAndImagesModified(&mList);
}
InvalidateCaretFramesIfNeeded();
mBuilder.EnterPresShell(mBuilder.RootReferenceFrame());
// We set the override dirty regions during ComputeRebuildRegion or in
@ -1436,10 +1439,6 @@ PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate(
// Do not allow partial builds if the |ShouldBuildPartial()| heuristic fails.
bool shouldBuildPartial = ShouldBuildPartial(modifiedFrames.Frames());
if (shouldBuildPartial) {
InvalidateCaretFramesIfNeeded(modifiedFrames.Frames());
}
nsRect modifiedDirty;
AnimatedGeometryRoot* modifiedAGR = nullptr;
PartialUpdateResult result = PartialUpdateResult::NoChange;

Просмотреть файл

@ -196,7 +196,7 @@ struct RetainedDisplayListBuilder {
/**
* Invalidates the current and previous caret frame if they have changed.
*/
void InvalidateCaretFramesIfNeeded(nsTArray<nsIFrame*>& aModifiedFrames);
void InvalidateCaretFramesIfNeeded();
/**
* A simple early exit heuristic to avoid slow partial display list rebuilds.

Просмотреть файл

@ -96,6 +96,7 @@
#include "nsTableColFrame.h"
#include "nsTextFrame.h"
#include "nsSliderFrame.h"
#include "nsFocusManager.h"
#include "ClientLayerManager.h"
#include "mozilla/layers/RenderRootStateManager.h"
#include "mozilla/layers/StackingContextHelper.h"
@ -1234,6 +1235,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mUsedAGRBudget(0),
mDirtyRect(-1, -1, -1, -1),
mGlassDisplayItem(nullptr),
mCaretFrame(nullptr),
mScrollInfoItemsForHoisting(nullptr),
mFirstClipChainToDestroy(nullptr),
mActiveScrolledRootForRootScrollframe(nullptr),
@ -1292,6 +1294,21 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
"Check TYPE_MAX should not overflow");
}
static PresShell* GetFocusedPresShell() {
nsPIDOMWindowOuter* focusedWnd =
nsFocusManager::GetFocusManager()->GetFocusedWindow();
if (!focusedWnd) {
return nullptr;
}
nsCOMPtr<nsIDocShell> focusedDocShell = focusedWnd->GetDocShell();
if (!focusedDocShell) {
return nullptr;
}
return focusedDocShell->GetPresShell();
}
void nsDisplayListBuilder::BeginFrame() {
nsCSSRendering::BeginFrameTreesLocked();
mCurrentAGR = mRootAGR;
@ -1309,6 +1326,26 @@ void nsDisplayListBuilder::BeginFrame() {
for (auto& renderRootRect : mRenderRootRects) {
renderRootRect = LayoutDeviceRect();
}
if (!mBuildCaret) {
return;
}
RefPtr<PresShell> presShell = GetFocusedPresShell();
if (presShell) {
RefPtr<nsCaret> caret = presShell->GetCaret();
mCaretFrame = caret->GetPaintGeometry(&mCaretRect);
// The focused pres shell may not be in the document that we're
// painting, or be in a popup. Check if the display root for
// the caret matches the display root that we're painting, and
// only use it if it matches.
if (mCaretFrame &&
nsLayoutUtils::GetDisplayRootFrame(mCaretFrame) !=
nsLayoutUtils::GetDisplayRootFrame(mReferenceFrame)) {
mCaretFrame = nullptr;
}
}
}
void nsDisplayListBuilder::EndFrame() {
@ -1321,6 +1358,7 @@ void nsDisplayListBuilder::EndFrame() {
FreeClipChains();
FreeTemporaryItems();
nsCSSRendering::EndFrameTreesLocked();
mCaretFrame = nullptr;
}
void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame,
@ -1598,7 +1636,6 @@ void nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
bool aPointerEventsNoneDoc) {
PresShellState* state = mPresShellStates.AppendElement();
state->mPresShell = aReferenceFrame->PresShell();
state->mCaretFrame = nullptr;
state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
state->mFirstFrameWithOOFData = mFramesWithOOFData.Length();
@ -1660,10 +1697,11 @@ void nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
return;
}
RefPtr<nsCaret> caret = state->mPresShell->GetCaret();
state->mCaretFrame = caret->GetPaintGeometry(&state->mCaretRect);
if (state->mCaretFrame) {
MarkFrameForDisplay(state->mCaretFrame, aReferenceFrame);
// Caret frames add visual area to their frame, but we don't update the
// overflow area. Use flags to make sure we build display items for that frame
// instead.
if (mCaretFrame && mCaretFrame->PresShell() == state->mPresShell) {
MarkFrameForDisplay(mCaretFrame, aReferenceFrame);
}
}

Просмотреть файл

@ -628,10 +628,6 @@ class nsDisplayListBuilder {
bool IsBuilding() const { return mIsBuilding; }
void SetIsBuilding(bool aIsBuilding) {
mIsBuilding = aIsBuilding;
for (nsIFrame* f : mModifiedFramesDuringBuilding) {
f->SetFrameIsModified(false);
}
mModifiedFramesDuringBuilding.Clear();
}
bool InInvalidSubtree() const { return mInInvalidSubtree; }
@ -767,7 +763,7 @@ class nsDisplayListBuilder {
*/
bool DisplayCaret(nsIFrame* aFrame, nsDisplayList* aList) {
nsIFrame* frame = GetCaretFrame();
if (aFrame == frame) {
if (aFrame == frame && !IsBackgroundOnly()) {
frame->DisplayCaret(this, aList);
return true;
}
@ -777,11 +773,11 @@ class nsDisplayListBuilder {
* Get the frame that the caret is supposed to draw in.
* If the caret is currently invisible, this will be null.
*/
nsIFrame* GetCaretFrame() { return CurrentPresShellState()->mCaretFrame; }
nsIFrame* GetCaretFrame() { return mCaretFrame; }
/**
* Get the rectangle we're supposed to draw the caret into.
*/
const nsRect& GetCaretRect() { return CurrentPresShellState()->mCaretRect; }
const nsRect& GetCaretRect() { return mCaretRect; }
/**
* Get the caret associated with the current presshell.
*/
@ -1703,17 +1699,6 @@ class nsDisplayListBuilder {
mBuildingInvisibleItems = aBuildingInvisibleItems;
}
bool MarkFrameModifiedDuringBuilding(nsIFrame* aFrame) {
if (!aFrame->IsFrameModified()) {
mModifiedFramesDuringBuilding.AppendElement(aFrame);
aFrame->SetFrameIsModified(true);
return true;
}
return false;
}
void RebuildAllItemsInCurrentSubtree() { mDirtyRect = mVisibleRect; }
/**
* This is a convenience function to ease the transition until AGRs and ASRs
* are unified.
@ -1832,8 +1817,6 @@ class nsDisplayListBuilder {
#ifdef DEBUG
mozilla::Maybe<nsAutoLayoutPhase> mAutoLayoutPhase;
#endif
nsIFrame* mCaretFrame;
nsRect mCaretRect;
mozilla::Maybe<OutOfFlowDisplayData> mFixedBackgroundDisplayData;
uint32_t mFirstFrameMarkedForDisplay;
uint32_t mFirstFrameWithOOFData;
@ -1911,8 +1894,6 @@ class nsDisplayListBuilder {
// Set of frames already counted in budget
nsTHashtable<nsPtrHashKey<nsIFrame>> mAGRBudgetSet;
nsTArray<nsIFrame*> mModifiedFramesDuringBuilding;
nsDataHashtable<nsPtrHashKey<RemoteBrowser>, EffectsInfo> mEffectsUpdates;
// Relative to mCurrentFrame.
@ -1938,6 +1919,10 @@ class nsDisplayListBuilder {
// If we've encountered a glass item yet, only used during partial display
// list builds.
bool mHasGlassItemDuringPartial;
nsIFrame* mCaretFrame;
nsRect mCaretRect;
// A temporary list that we append scroll info items to while building
// display items for the contents of frames with SVG effects.
// Only non-null when ShouldBuildScrollInfoItemsForHoisting() is true.