tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit aa124118714b8171a27d35a2820bd45c51eaec36
parent 4bc276362491d28731154efdf229b95463e4ef29
Author: Jonathan Kew <jkew@mozilla.com>
Date:   Sat,  4 Oct 2025 09:10:54 +0000

Bug 1991689 - Cache a PropertyProvider in SVGTextFrame so that we don't have to create a fresh one for every call to the same textframe. r=firefox-svg-reviewers,longsonr

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

Diffstat:
Mlayout/generic/nsTextFrame.h | 7+++++--
Mlayout/svg/SVGTextFrame.cpp | 24+++++++++---------------
Mlayout/svg/SVGTextFrame.h | 15+++++++++++++++
3 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h @@ -112,9 +112,12 @@ class nsTextFrame : public nsIFrame { /** * An implementation of gfxTextRun::PropertyProvider that computes spacing and * hyphenation based on CSS properties for a text frame. + * + * nsTextFrame normally creates a PropertyProvider as a temporary object on + * on the stack, but this is not marked MOZ_STACK_CLASS because SVGTextFrame + * wants to cache an instance across multiple calls using the same textframe. */ - class MOZ_STACK_CLASS PropertyProvider final - : public gfxTextRun::PropertyProvider { + class PropertyProvider final : public gfxTextRun::PropertyProvider { using HyphenType = gfxTextRun::HyphenType; public: diff --git a/layout/svg/SVGTextFrame.cpp b/layout/svg/SVGTextFrame.cpp @@ -835,7 +835,6 @@ SVGBBox TextRenderedRun::GetRunUserSpaceRect(nsPresContext* aContext, vertical ? -self.y : -self.x); gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated); - gfxSkipCharsIterator start = it; gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated); // Get the content range for this rendered run. @@ -845,10 +844,7 @@ SVGBBox TextRenderedRun::GetRunUserSpaceRect(nsPresContext* aContext, return r; } - // FIXME(heycam): We could create a single PropertyProvider for all - // TextRenderedRuns that correspond to the text frame, rather than recreate - // it each time here. - nsTextFrame::PropertyProvider provider(mFrame, start); + auto& provider = mRoot->PropertyProviderFor(mFrame); // Measure that range. gfxTextRun::Metrics metrics = textRun->MeasureText( @@ -946,7 +942,7 @@ void TextRenderedRun::GetClipEdges(nscoord& aVisIStartEdge, gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated); gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated); - nsTextFrame::PropertyProvider provider(mFrame, it); + auto& provider = mRoot->PropertyProviderFor(mFrame); // Get the covered content offset/length for this rendered run in skipped // characters, since that is what GetAdvanceWidth expects. @@ -1018,7 +1014,7 @@ void TextRenderedRun::GetClipEdges(nscoord& aVisIStartEdge, nscoord TextRenderedRun::GetAdvanceWidth() const { gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated); gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated); - nsTextFrame::PropertyProvider provider(mFrame, it); + auto& provider = mRoot->PropertyProviderFor(mFrame); Range range = ConvertOriginalToSkipped(it, mTextFrameContentOffset, mTextFrameContentLength); @@ -1068,7 +1064,7 @@ int32_t TextRenderedRun::GetCharNumAtPosition(nsPresContext* aContext, gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated); gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated); - nsTextFrame::PropertyProvider provider(mFrame, it); + auto& provider = mRoot->PropertyProviderFor(mFrame); // Next check that the point lies horizontally within the left and right // edges of the text. @@ -2355,10 +2351,7 @@ gfxFloat CharIterator::GetAdvance(nsPresContext* aContext) const { float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel()); - gfxSkipCharsIterator start = - TextFrame()->EnsureTextRun(nsTextFrame::eInflated); - nsTextFrame::PropertyProvider provider(TextFrame(), start); - + auto& provider = mFrameIterator.Root()->PropertyProviderFor(TextFrame()); uint32_t offset = mSkipCharsIterator.GetSkippedOffset(); gfxFloat advance = mTextRun->GetAdvanceWidth(Range(offset, offset + 1), &provider); @@ -3714,7 +3707,7 @@ float SVGTextFrame::GetSubStringLengthFastPath(nsIContent* aContent, gfxSkipCharsIterator it = frame->EnsureTextRun(nsTextFrame::eInflated); gfxTextRun* textRun = frame->GetTextRun(nsTextFrame::eInflated); - nsTextFrame::PropertyProvider provider(frame, it); + auto& provider = PropertyProviderFor(frame); Range range = ConvertOriginalToSkipped(it, offset, trimmedLength); @@ -3782,7 +3775,7 @@ float SVGTextFrame::GetSubStringLengthSlowFallback(nsIContent* aContent, gfxSkipCharsIterator it = run.mFrame->EnsureTextRun(nsTextFrame::eInflated); gfxTextRun* textRun = run.mFrame->GetTextRun(nsTextFrame::eInflated); - nsTextFrame::PropertyProvider provider(run.mFrame, it); + auto& provider = PropertyProviderFor(run.mFrame); Range range = ConvertOriginalToSkipped(it, offset, length); @@ -4337,7 +4330,7 @@ void SVGTextFrame::DetermineCharPositions(nsTArray<nsPoint>& aPositions) { for (nsTextFrame* frame = frit.Current(); frame; frame = frit.Next()) { gfxSkipCharsIterator it = frame->EnsureTextRun(nsTextFrame::eInflated); gfxTextRun* textRun = frame->GetTextRun(nsTextFrame::eInflated); - nsTextFrame::PropertyProvider provider(frame, it); + auto& provider = PropertyProviderFor(frame); // Reset the position to the new frame's position. position = frit.Position(); @@ -5144,6 +5137,7 @@ void SVGTextFrame::DoReflow() { // Forget any cached measurements of one of our children. mFrameForCachedRanges = nullptr; + mCachedProvider.reset(); nsPresContext* presContext = PresContext(); nsIFrame* kid = PrincipalChildList().FirstChild(); diff --git a/layout/svg/SVGTextFrame.h b/layout/svg/SVGTextFrame.h @@ -11,6 +11,7 @@ #include "gfxRect.h" #include "gfxTextRun.h" #include "mozilla/Attributes.h" +#include "mozilla/Maybe.h" #include "mozilla/PresShellForwards.h" #include "mozilla/RefPtr.h" #include "mozilla/SVGContainerFrame.h" @@ -610,9 +611,23 @@ class SVGTextFrame final : public SVGDisplayContainerFrame { return mCachedRanges[aWhichRange]; } + // Return a reference to a PropertyProvider for the given textframe; + // the provider is cached by SVGTextFrame to avoid creating it afresh + // for repeated operations involving the same textframe. + nsTextFrame::PropertyProvider& PropertyProviderFor(nsTextFrame* aFrame) { + if (!mCachedProvider || aFrame != mCachedProvider->GetFrame()) { + mCachedProvider.reset(); + mCachedProvider.emplace(aFrame, + aFrame->EnsureTextRun(nsTextFrame::eInflated)); + } + return mCachedProvider.ref(); + } + private: const nsTextFrame* mFrameForCachedRanges = nullptr; CachedMeasuredRange mCachedRanges[CachedRangeCount]; + + Maybe<nsTextFrame::PropertyProvider> mCachedProvider; }; } // namespace mozilla