Bug 1725555 - patch 2 - Enable the primary nsTextFrame to provide, and cache, an array of all the continuations in the chain. r=emilio

This allows us to binary-search the continuations from nsRange::CollectClientRectsAndText,
instead of linear-searching the linked list for every range we need to look up.

Depends on D122998

Differential Revision: https://phabricator.services.mozilla.com/D122999
This commit is contained in:
Jonathan Kew 2021-08-26 16:56:43 +00:00
Родитель 2dddbe283f
Коммит 6481b69a6c
3 изменённых файлов: 103 добавлений и 5 удалений

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

@ -2647,10 +2647,40 @@ static nsresult GetPartialTextRect(RectCallback* aCallback,
if (textFrame) { if (textFrame) {
nsIFrame* relativeTo = nsIFrame* relativeTo =
nsLayoutUtils::GetContainingBlockForClientRect(textFrame); nsLayoutUtils::GetContainingBlockForClientRect(textFrame);
for (nsTextFrame* f = textFrame; f;
f = static_cast<nsTextFrame*>(f->GetNextContinuation())) { // Try to binary-search the list of continuations for the starting point.
// (GetContinuations is fallible; if it returns nullptr, we'll just start
// from the beginning.)
nsTArray<nsTextFrame*>* continuations = textFrame->GetContinuations();
nsTextFrame* f = textFrame;
if (continuations) {
size_t index;
if (BinarySearchIf(
*continuations, 0, continuations->Length(),
[=](nsTextFrame* aFrame) -> int {
if (aStartOffset < aFrame->GetContentOffset()) {
return -1;
}
if (aStartOffset > aFrame->GetContentOffset()) {
return 1;
}
return 0;
},
&index)) {
f = (*continuations)[index];
} else {
f = (*continuations)[index ? index - 1 : 0];
}
}
for (; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd(); int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd();
if (fend <= aStartOffset || fstart >= aEndOffset) continue; if (fend <= aStartOffset) {
continue;
}
if (fstart >= aEndOffset) {
break;
}
// Calculate the text content offsets we'll need if text is requested. // Calculate the text content offsets we'll need if text is requested.
int32_t textContentStart = fstart; int32_t textContentStart = fstart;

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

@ -4384,6 +4384,35 @@ void nsTextFrame::DestroyFrom(nsIFrame* aDestructRoot,
nsIFrame::DestroyFrom(aDestructRoot, aPostDestroyData); nsIFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
} }
nsTArray<nsTextFrame*>* nsTextFrame::GetContinuations() {
MOZ_ASSERT(NS_IsMainThread());
// Only for use on the primary frame, which has no prev-continuation.
MOZ_ASSERT(!GetPrevContinuation());
if (!mNextContinuation) {
return nullptr;
}
if (mHasContinuationsProperty) {
return GetProperty(ContinuationsProperty());
}
size_t count = 0;
for (nsIFrame* f = this; f; f = f->GetNextContinuation()) {
++count;
}
auto* continuations = new nsTArray<nsTextFrame*>;
if (continuations->SetCapacity(count, fallible)) {
for (nsTextFrame* f = this; f;
f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
continuations->AppendElement(f);
}
} else {
delete continuations;
continuations = nullptr;
}
AddProperty(ContinuationsProperty(), continuations);
mHasContinuationsProperty = true;
return continuations;
}
class nsContinuingTextFrame final : public nsTextFrame { class nsContinuingTextFrame final : public nsTextFrame {
public: public:
NS_DECL_FRAMEARENA_HELPERS(nsContinuingTextFrame) NS_DECL_FRAMEARENA_HELPERS(nsContinuingTextFrame)
@ -4410,10 +4439,16 @@ class nsContinuingTextFrame final : public nsTextFrame {
nsTextFrame* prevFirst = mFirstContinuation; nsTextFrame* prevFirst = mFirstContinuation;
if (mPrevContinuation) { if (mPrevContinuation) {
mFirstContinuation = mPrevContinuation->FirstContinuation(); mFirstContinuation = mPrevContinuation->FirstContinuation();
if (mFirstContinuation) {
mFirstContinuation->ClearCachedContinuations();
}
} else { } else {
mFirstContinuation = nullptr; mFirstContinuation = nullptr;
} }
if (mFirstContinuation != prevFirst) { if (mFirstContinuation != prevFirst) {
if (prevFirst) {
prevFirst->ClearCachedContinuations();
}
auto* f = static_cast<nsContinuingTextFrame*>(mNextContinuation); auto* f = static_cast<nsContinuingTextFrame*>(mNextContinuation);
while (f) { while (f) {
f->mFirstContinuation = mFirstContinuation; f->mFirstContinuation = mFirstContinuation;
@ -4438,10 +4473,16 @@ class nsContinuingTextFrame final : public nsTextFrame {
nsTextFrame* prevFirst = mFirstContinuation; nsTextFrame* prevFirst = mFirstContinuation;
if (mPrevContinuation) { if (mPrevContinuation) {
mFirstContinuation = mPrevContinuation->FirstContinuation(); mFirstContinuation = mPrevContinuation->FirstContinuation();
if (mFirstContinuation) {
mFirstContinuation->ClearCachedContinuations();
}
} else { } else {
mFirstContinuation = nullptr; mFirstContinuation = nullptr;
} }
if (mFirstContinuation != prevFirst) { if (mFirstContinuation != prevFirst) {
if (prevFirst) {
prevFirst->ClearCachedContinuations();
}
auto* f = static_cast<nsContinuingTextFrame*>(mNextContinuation); auto* f = static_cast<nsContinuingTextFrame*>(mNextContinuation);
while (f) { while (f) {
f->mFirstContinuation = mFirstContinuation; f->mFirstContinuation = mFirstContinuation;
@ -4457,6 +4498,8 @@ class nsContinuingTextFrame final : public nsTextFrame {
return mFirstContinuation; return mFirstContinuation;
}; };
nsTArray<nsTextFrame*>* GetContinuations() final { return nullptr; }
void AddInlineMinISize(gfxContext* aRenderingContext, void AddInlineMinISize(gfxContext* aRenderingContext,
InlineMinISizeData* aData) final; InlineMinISizeData* aData) final;
void AddInlinePrefISize(gfxContext* aRenderingContext, void AddInlinePrefISize(gfxContext* aRenderingContext,
@ -9121,6 +9164,12 @@ void nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
RemoveStateBits(TEXT_REFLOW_FLAGS | TEXT_WHITESPACE_FLAGS); RemoveStateBits(TEXT_REFLOW_FLAGS | TEXT_WHITESPACE_FLAGS);
mReflowRequestedForCharDataChange = false; mReflowRequestedForCharDataChange = false;
RemoveProperty(WebRenderTextBounds()); RemoveProperty(WebRenderTextBounds());
// Discard cached continuations array that will be invalidated by the reflow.
if (nsTextFrame* first = FirstContinuation()) {
first->ClearCachedContinuations();
}
// Temporarily map all possible content while we construct our new textrun. // Temporarily map all possible content while we construct our new textrun.
// so that when doing reflow our styles prevail over any part of the // so that when doing reflow our styles prevail over any part of the
// textrun we look at. Note that next-in-flows may be mapping the same // textrun we look at. Note that next-in-flows may be mapping the same

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

@ -214,6 +214,9 @@ class nsTextFrame : public nsIFrame {
// nsQueryFrame // nsQueryFrame
NS_DECL_QUERYFRAME NS_DECL_QUERYFRAME
NS_DECLARE_FRAME_PROPERTY_DELETABLE(ContinuationsProperty,
nsTArray<nsTextFrame*>)
// nsIFrame // nsIFrame
void BuildDisplayList(nsDisplayListBuilder* aBuilder, void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists) final; const nsDisplayListSet& aLists) final;
@ -781,6 +784,11 @@ class nsTextFrame : public nsIFrame {
nsRect WebRenderBounds(); nsRect WebRenderBounds();
// Return pointer to an array of all frames in the continuation chain, or
// null if we're too short of memory. (This is only meant to be called on the
// first text frame in the chain; continuations will always return null.)
virtual nsTArray<nsTextFrame*>* GetContinuations();
protected: protected:
virtual ~nsTextFrame(); virtual ~nsTextFrame();
@ -815,6 +823,9 @@ class nsTextFrame : public nsIFrame {
}; };
mutable SelectionState mIsSelected; mutable SelectionState mIsSelected;
// Whether a cached continuations array is present.
bool mHasContinuationsProperty = false;
/** /**
* Return true if the frame is part of a Selection. * Return true if the frame is part of a Selection.
* Helper method to implement the public IsSelected() API. * Helper method to implement the public IsSelected() API.
@ -999,6 +1010,16 @@ class nsTextFrame : public nsIFrame {
void ClearMetrics(ReflowOutput& aMetrics); void ClearMetrics(ReflowOutput& aMetrics);
// Clear any cached continuations array; this should be called whenever the
// chain is modified.
void ClearCachedContinuations() {
MOZ_ASSERT(NS_IsMainThread());
if (mHasContinuationsProperty) {
RemoveProperty(ContinuationsProperty());
mHasContinuationsProperty = false;
}
}
/** /**
* UpdateIteratorFromOffset() updates the iterator from a given offset. * UpdateIteratorFromOffset() updates the iterator from a given offset.
* Also, aInOffset may be updated to cluster start if aInOffset isn't * Also, aInOffset may be updated to cluster start if aInOffset isn't
@ -1010,8 +1031,6 @@ class nsTextFrame : public nsIFrame {
nsPoint GetPointFromIterator(const gfxSkipCharsIterator& aIter, nsPoint GetPointFromIterator(const gfxSkipCharsIterator& aIter,
PropertyProvider& aProperties); PropertyProvider& aProperties);
public:
}; };
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsTextFrame::TrimmedOffsetFlags) MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsTextFrame::TrimmedOffsetFlags)