SVGTextFrame.h (23171B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef LAYOUT_SVG_SVGTEXTFRAME_H_ 8 #define LAYOUT_SVG_SVGTEXTFRAME_H_ 9 10 #include "gfxMatrix.h" 11 #include "gfxRect.h" 12 #include "gfxTextRun.h" 13 #include "mozilla/Attributes.h" 14 #include "mozilla/Maybe.h" 15 #include "mozilla/PresShellForwards.h" 16 #include "mozilla/RefPtr.h" 17 #include "mozilla/SVGContainerFrame.h" 18 #include "mozilla/gfx/2D.h" 19 #include "nsIContent.h" // for GetContent 20 #include "nsStubMutationObserver.h" 21 #include "nsTextFrame.h" 22 23 class gfxContext; 24 25 namespace mozilla { 26 27 class CharIterator; 28 class DisplaySVGText; 29 class SVGContextPaint; 30 class SVGTextFrame; 31 class TextFrameIterator; 32 class TextNodeCorrespondenceRecorder; 33 struct TextRenderedRun; 34 class TextRenderedRunIterator; 35 36 namespace dom { 37 struct DOMPointInit; 38 class DOMSVGPoint; 39 class SVGRect; 40 class SVGGeometryElement; 41 } // namespace dom 42 } // namespace mozilla 43 44 nsIFrame* NS_NewSVGTextFrame(mozilla::PresShell* aPresShell, 45 mozilla::ComputedStyle* aStyle); 46 47 namespace mozilla { 48 49 /** 50 * Information about the positioning for a single character in an SVG <text> 51 * element. 52 * 53 * During SVG text layout, we use infinity values to represent positions and 54 * rotations that are not explicitly specified with x/y/rotate attributes. 55 */ 56 struct CharPosition { 57 CharPosition() 58 : mAngle(0), 59 mHidden(false), 60 mUnaddressable(false), 61 mClusterOrLigatureGroupMiddle(false), 62 mRunBoundary(false), 63 mStartOfChunk(false) {} 64 65 CharPosition(gfxPoint aPosition, double aAngle) 66 : mPosition(aPosition), 67 mAngle(aAngle), 68 mHidden(false), 69 mUnaddressable(false), 70 mClusterOrLigatureGroupMiddle(false), 71 mRunBoundary(false), 72 mStartOfChunk(false) {} 73 74 static CharPosition Unspecified(bool aUnaddressable) { 75 CharPosition cp(UnspecifiedPoint(), UnspecifiedAngle()); 76 cp.mUnaddressable = aUnaddressable; 77 return cp; 78 } 79 80 bool IsAngleSpecified() const { return mAngle != UnspecifiedAngle(); } 81 82 bool IsXSpecified() const { return mPosition.x != UnspecifiedCoord(); } 83 84 bool IsYSpecified() const { return mPosition.y != UnspecifiedCoord(); } 85 86 gfxPoint mPosition; 87 double mAngle; 88 89 // not displayed due to falling off the end of a <textPath> 90 bool mHidden; 91 92 // skipped in positioning attributes due to being collapsed-away white space 93 bool mUnaddressable; 94 95 // a preceding character is what positioning attributes address 96 bool mClusterOrLigatureGroupMiddle; 97 98 // rendering is split here since an explicit position or rotation was given 99 bool mRunBoundary; 100 101 // an anchored chunk begins here 102 bool mStartOfChunk; 103 104 private: 105 static gfxFloat UnspecifiedCoord() { 106 return std::numeric_limits<gfxFloat>::infinity(); 107 } 108 109 static double UnspecifiedAngle() { 110 return std::numeric_limits<double>::infinity(); 111 } 112 113 static gfxPoint UnspecifiedPoint() { 114 return gfxPoint(UnspecifiedCoord(), UnspecifiedCoord()); 115 } 116 }; 117 118 /** 119 * A runnable to mark glyph positions as needing to be recomputed 120 * and to invalid the bounds of the SVGTextFrame frame. 121 */ 122 class GlyphMetricsUpdater : public Runnable { 123 public: 124 NS_DECL_NSIRUNNABLE 125 explicit GlyphMetricsUpdater(SVGTextFrame* aFrame) 126 : Runnable("GlyphMetricsUpdater"), mFrame(aFrame) {} 127 static void Run(SVGTextFrame* aFrame); 128 void Revoke() { mFrame = nullptr; } 129 130 private: 131 SVGTextFrame* mFrame; 132 }; 133 134 /** 135 * Frame class for SVG <text> elements. 136 * 137 * An SVGTextFrame manages SVG text layout, painting and interaction for 138 * all descendent text content elements. The frame tree will look like this: 139 * 140 * SVGTextFrame -- for <text> 141 * <anonymous block frame> 142 * ns{Block,Inline,Text}Frames -- for text nodes, <tspan>s, <a>s, etc. 143 * 144 * SVG text layout is done by: 145 * 146 * 1. Reflowing the anonymous block frame. 147 * 2. Inspecting the (app unit) positions of the glyph for each character in 148 * the nsTextFrames underneath the anonymous block frame. 149 * 3. Determining the (user unit) positions for each character in the <text> 150 * using the x/y/dx/dy/rotate attributes on all the text content elements, 151 * and using the step 2 results to fill in any gaps. 152 * 4. Applying any other SVG specific text layout (anchoring and text paths) 153 * to the positions computed in step 3. 154 * 155 * Rendering of the text is done by splitting up each nsTextFrame into ranges 156 * that can be contiguously painted. (For example <text x="10 20">abcd</text> 157 * would have two contiguous ranges: one for the "a" and one for the "bcd".) 158 * Each range is called a "text rendered run", represented by a TextRenderedRun 159 * object. The TextRenderedRunIterator class performs that splitting and 160 * returns a TextRenderedRun for each bit of text to be painted separately. 161 * 162 * Each rendered run is painted by calling nsTextFrame::PaintText. If the text 163 * formatting is simple enough (solid fill, no stroking, etc.), PaintText will 164 * itself do the painting. Otherwise, a DrawPathCallback is passed to 165 * PaintText so that we can fill the text geometry with SVG paint servers. 166 */ 167 class SVGTextFrame final : public SVGDisplayContainerFrame { 168 friend nsIFrame* ::NS_NewSVGTextFrame(mozilla::PresShell* aPresShell, 169 ComputedStyle* aStyle); 170 171 friend class CharIterator; 172 friend class DisplaySVGText; 173 friend class GlyphMetricsUpdater; 174 friend class MutationObserver; 175 friend class TextFrameIterator; 176 friend class TextNodeCorrespondenceRecorder; 177 friend struct TextRenderedRun; 178 friend class TextRenderedRunIterator; 179 180 using Range = gfxTextRun::Range; 181 using DrawTarget = gfx::DrawTarget; 182 using Path = gfx::Path; 183 using Point = gfx::Point; 184 using Rect = gfx::Rect; 185 186 protected: 187 explicit SVGTextFrame(ComputedStyle* aStyle, nsPresContext* aPresContext) 188 : SVGDisplayContainerFrame(aStyle, aPresContext, kClassID) { 189 AddStateBits(NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_SVG_TEXT | 190 NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY | 191 NS_STATE_SVG_POSITIONING_DIRTY); 192 } 193 194 ~SVGTextFrame() = default; 195 196 public: 197 NS_DECL_QUERYFRAME 198 NS_DECL_FRAMEARENA_HELPERS(SVGTextFrame) 199 200 // nsIFrame: 201 void Init(nsIContent* aContent, nsContainerFrame* aParent, 202 nsIFrame* aPrevInFlow) override; 203 204 void DidSetComputedStyle(ComputedStyle* aOldComputedStyle) override; 205 206 nsresult AttributeChanged(int32_t aNamespaceID, nsAtom* aAttribute, 207 AttrModType aModType) override; 208 209 nsContainerFrame* GetContentInsertionFrame() override { 210 return PrincipalChildList().FirstChild()->GetContentInsertionFrame(); 211 } 212 213 void BuildDisplayList(nsDisplayListBuilder* aBuilder, 214 const nsDisplayListSet& aLists) override; 215 216 #ifdef DEBUG_FRAME_DUMP 217 nsresult GetFrameName(nsAString& aResult) const override { 218 return MakeFrameName(u"SVGText"_ns, aResult); 219 } 220 #endif 221 222 /** 223 * Finds the nsTextFrame for the closest rendered run to the specified point. 224 */ 225 void FindCloserFrameForSelection( 226 const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) override; 227 228 // ISVGDisplayableFrame interface: 229 void NotifySVGChanged(ChangeFlags aFlags) override; 230 void PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform, 231 imgDrawingParams& aImgParams) override; 232 nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override; 233 void ReflowSVG() override; 234 SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace, 235 uint32_t aFlags) override; 236 237 // SVG DOM text methods: 238 uint32_t GetNumberOfChars(nsIContent* aContent); 239 float GetComputedTextLength(nsIContent* aContent); 240 MOZ_CAN_RUN_SCRIPT_BOUNDARY void SelectSubString(nsIContent* aContent, 241 uint32_t charnum, 242 uint32_t nchars, 243 ErrorResult& aRv); 244 bool RequiresSlowFallbackForSubStringLength(); 245 float GetSubStringLengthFastPath(nsIContent* aContent, uint32_t charnum, 246 uint32_t nchars, ErrorResult& aRv); 247 /** 248 * This fallback version of GetSubStringLength takes 249 * into account glyph positioning and requires us to have flushed layout 250 * before calling it. As per the SVG 2 spec, typically glyph 251 * positioning does not affect the results of getSubStringLength, but one 252 * exception is text in a textPath where we need to ignore characters that 253 * fall off the end of the textPath path. 254 */ 255 float GetSubStringLengthSlowFallback(nsIContent* aContent, uint32_t charnum, 256 uint32_t nchars, ErrorResult& aRv); 257 258 int32_t GetCharNumAtPosition(nsIContent* aContent, 259 const dom::DOMPointInit& aPoint); 260 261 already_AddRefed<dom::DOMSVGPoint> GetStartPositionOfChar( 262 nsIContent* aContent, uint32_t aCharNum, ErrorResult& aRv); 263 already_AddRefed<dom::DOMSVGPoint> GetEndPositionOfChar(nsIContent* aContent, 264 uint32_t aCharNum, 265 ErrorResult& aRv); 266 already_AddRefed<dom::SVGRect> GetExtentOfChar(nsIContent* aContent, 267 uint32_t aCharNum, 268 ErrorResult& aRv); 269 float GetRotationOfChar(nsIContent* aContent, uint32_t aCharNum, 270 ErrorResult& aRv); 271 272 // SVGTextFrame methods: 273 274 /** 275 * Handles a base or animated attribute value change to a descendant 276 * text content element. 277 */ 278 void HandleAttributeChangeInDescendant(dom::Element* aElement, 279 int32_t aNameSpaceID, 280 nsAtom* aAttribute); 281 282 /** 283 * Calls ScheduleReflowSVGNonDisplayText if this is a non-display frame, 284 * and SVGUtils::ScheduleReflowSVG otherwise. 285 */ 286 void ScheduleReflowSVG(); 287 288 /** 289 * Reflows the anonymous block frame of this non-display SVGTextFrame. 290 * 291 * When we are under SVGDisplayContainerFrame::ReflowSVG, we need to 292 * reflow any SVGTextFrame frames in the subtree in case they are 293 * being observed (by being for example in a <mask>) and the change 294 * that caused the reflow would not already have caused a reflow. 295 * 296 * Note that displayed SVGTextFrames are reflowed as needed, when PaintSVG 297 * is called or some SVG DOM method is called on the element. 298 */ 299 void ReflowSVGNonDisplayText(); 300 301 /** 302 * This is a function that behaves similarly to SVGUtils::ScheduleReflowSVG, 303 * but which will skip over any ancestor non-display container frames on the 304 * way to the SVGOuterSVGFrame. It exists for the situation where a 305 * non-display <text> element has changed and needs to ensure ReflowSVG will 306 * be called on its closest display container frame, so that 307 * SVGDisplayContainerFrame::ReflowSVG will call ReflowSVGNonDisplayText on 308 * it. 309 * 310 * We have to do this in two cases: in response to a style change on a 311 * non-display <text>, where aReason will be 312 * IntrinsicDirty::FrameAncestorsAndDescendants (the common case), and also in 313 * response to glyphs changes on non-display <text> (i.e., animated 314 * SVG-in-OpenType glyphs), in which case aReason will be None, since layout 315 * doesn't need to be recomputed. 316 */ 317 void ScheduleReflowSVGNonDisplayText(IntrinsicDirty aReason); 318 319 /** 320 * Updates the mFontSizeScaleFactor value by looking at the range of 321 * font-sizes used within the <text>. 322 * 323 * @return Whether mFontSizeScaleFactor changed. 324 */ 325 bool UpdateFontSizeScaleFactor(); 326 327 double GetFontSizeScaleFactor() const; 328 329 /** 330 * Takes a point from the <text> element's user space and 331 * converts it to the appropriate frame user space of aChildFrame, 332 * according to which rendered run the point hits. 333 */ 334 Point TransformFramePointToTextChild(const Point& aPoint, 335 const nsIFrame* aChildFrame); 336 337 /** 338 * Takes an app unit rectangle in the coordinate space of a given descendant 339 * frame of this frame, and returns a rectangle in the <text> element's user 340 * space that covers all parts of rendered runs that intersect with the 341 * rectangle. 342 */ 343 gfxRect TransformFrameRectFromTextChild(const nsRect& aRect, 344 const nsIFrame* aChildFrame); 345 346 /** As above, but taking and returning a device px rect. */ 347 Rect TransformFrameRectFromTextChild(const Rect& aRect, 348 const nsIFrame* aChildFrame); 349 350 /** As above, but with a single point */ 351 Point TransformFramePointFromTextChild(const Point& aPoint, 352 const nsIFrame* aChildFrame); 353 354 // Return our ::-moz-svg-text anonymous box. 355 void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override; 356 357 private: 358 /** 359 * Mutation observer used to watch for text positioning attribute changes 360 * on descendent text content elements (like <tspan>s). 361 */ 362 class MutationObserver final : public nsStubMutationObserver { 363 public: 364 explicit MutationObserver(SVGTextFrame* aFrame) : mFrame(aFrame) { 365 MOZ_ASSERT(mFrame, "MutationObserver needs a non-null frame"); 366 mFrame->GetContent()->AddMutationObserver(this); 367 SetEnabledCallbacks(kCharacterDataChanged | kAttributeChanged | 368 kContentAppended | kContentInserted | 369 kContentWillBeRemoved); 370 } 371 372 // nsISupports 373 NS_DECL_ISUPPORTS 374 375 // nsIMutationObserver 376 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED 377 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED 378 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED 379 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED 380 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED 381 382 private: 383 ~MutationObserver() { mFrame->GetContent()->RemoveMutationObserver(this); } 384 385 SVGTextFrame* const mFrame; 386 }; 387 388 /** 389 * Resolves Bidi for the anonymous block child if it needs it. 390 */ 391 void MaybeResolveBidiForAnonymousBlockChild(); 392 393 /** 394 * Reflows the anonymous block child if it is dirty or has dirty 395 * children, or if the SVGTextFrame itself is dirty. 396 */ 397 void MaybeReflowAnonymousBlockChild(); 398 399 /** 400 * Performs the actual work of reflowing the anonymous block child. 401 */ 402 void DoReflow(); 403 404 /** 405 * Schedules mPositions to be recomputed and the covered region to be 406 * updated. 407 */ 408 void NotifyGlyphMetricsChange(bool aUpdateTextCorrespondence); 409 410 /** 411 * Recomputes mPositions by calling DoGlyphPositioning if this information 412 * is out of date. 413 */ 414 void UpdateGlyphPositioning(); 415 416 /** 417 * Populates mPositions with positioning information for each character 418 * within the <text>. 419 */ 420 void DoGlyphPositioning(); 421 422 /** 423 * Converts the specified index into mPositions to an addressable 424 * character index (as can be used with the SVG DOM text methods) 425 * relative to the specified text child content element. 426 * 427 * @param aIndex The global character index. 428 * @param aContent The descendant text child content element that 429 * the returned addressable index will be relative to; null 430 * means the same as the <text> element. 431 * @return The addressable index, or -1 if the index cannot be 432 * represented as an addressable index relative to aContent. 433 */ 434 int32_t ConvertTextElementCharIndexToAddressableIndex(int32_t aIndex, 435 nsIContent* aContent); 436 437 /** 438 * Recursive helper for ResolvePositions below. 439 * 440 * @param aContent The current node. 441 * @param aIndex (in/out) The current character index. 442 * @param aInTextPath Whether we are currently under a <textPath> element. 443 * @param aForceStartOfChunk (in/out) Whether the next character we find 444 * should start a new anchored chunk. 445 * @param aDeltas (in/out) Receives the resolved dx/dy values for each 446 * character. 447 * @return false if we discover that mPositions did not have enough 448 * elements; true otherwise. 449 */ 450 bool ResolvePositionsForNode(nsIContent* aContent, uint32_t& aIndex, 451 bool aInTextPath, bool& aForceStartOfChunk, 452 nsTArray<gfxPoint>& aDeltas); 453 454 /** 455 * Initializes mPositions with character position information based on 456 * x/y/rotate attributes, leaving unspecified values in the array if a 457 * position was not given for that character. Also fills aDeltas with values 458 * based on dx/dy attributes. 459 * 460 * @param aDeltas (in/out) Receives the resolved dx/dy values for each 461 * character. 462 * @param aRunPerGlyph Whether mPositions should record that a new run begins 463 * at each glyph. 464 * @return false if we did not record any positions (due to having no 465 * displayed characters) or if we discover that mPositions did not have 466 * enough elements; true otherwise. 467 */ 468 bool ResolvePositions(nsTArray<gfxPoint>& aDeltas, bool aRunPerGlyph); 469 470 /** 471 * Determines the position, in app units, of each character in the <text> as 472 * laid out by reflow, and appends them to aPositions. Any characters that 473 * are undisplayed or trimmed away just get the last position. 474 */ 475 void DetermineCharPositions(nsTArray<nsPoint>& aPositions); 476 477 /** 478 * Sets mStartOfChunk to true for each character in mPositions that starts a 479 * line of text. 480 */ 481 void AdjustChunksForLineBreaks(); 482 483 /** 484 * Adjusts recorded character positions in mPositions to account for glyph 485 * boundaries. Four things are done: 486 * 487 * 1. mClusterOrLigatureGroupMiddle is set to true for all such characters. 488 * 489 * 2. Any run and anchored chunk boundaries that begin in the middle of a 490 * cluster/ligature group get moved to the start of the next 491 * cluster/ligature group. 492 * 493 * 3. The position of any character in the middle of a cluster/ligature 494 * group is updated to take into account partial ligatures and any 495 * rotation the glyph as a whole has. (The values that come out of 496 * DetermineCharPositions which then get written into mPositions in 497 * ResolvePositions store the same position value for each part of the 498 * ligature.) 499 * 500 * 4. The rotation of any character in the middle of a cluster/ligature 501 * group is set to the rotation of the first character. 502 */ 503 void AdjustPositionsForClusters(); 504 505 /** 506 * Updates the character positions stored in mPositions to account for 507 * text anchoring. 508 */ 509 void DoAnchoring(); 510 511 /** 512 * Updates character positions in mPositions for those characters inside a 513 * <textPath>. 514 */ 515 void DoTextPathLayout(); 516 517 /** 518 * Returns whether we need to render the text using 519 * nsTextFrame::DrawPathCallbacks rather than directly painting 520 * the text frames. 521 * 522 * @param aContextPaint Used by context-fill and context-stroke. 523 * @param aShouldPaintSVGGlyphs (out) Whether SVG glyphs in the text 524 * should be painted. 525 */ 526 bool ShouldRenderAsPath(nsTextFrame* aFrame, SVGContextPaint* aContextPaint, 527 bool& aShouldPaintSVGGlyphs); 528 529 // Methods to get information for a <textPath> frame. 530 already_AddRefed<Path> GetTextPath(nsIFrame* aTextPathFrame); 531 gfxFloat GetOffsetScale(nsIFrame* aTextPathFrame); 532 gfxFloat GetStartOffset(nsIFrame* aTextPathFrame); 533 534 /** 535 * The MutationObserver we have registered for the <text> element subtree. 536 */ 537 RefPtr<MutationObserver> mMutationObserver; 538 539 /** 540 * The number of characters in the DOM after the final nsTextFrame. For 541 * example, with 542 * 543 * <text>abcd<tspan display="none">ef</tspan></text> 544 * 545 * mTrailingUndisplayedCharacters would be 2. 546 */ 547 uint32_t mTrailingUndisplayedCharacters = 0; 548 549 /** 550 * Computed position information for each DOM character within the <text>. 551 */ 552 nsTArray<CharPosition> mPositions; 553 554 /** 555 * mFontSizeScaleFactor is used to cause the nsTextFrames to create text 556 * runs with a font size different from the actual font-size property value. 557 * This is used so that, for example with: 558 * 559 * <svg> 560 * <g transform="scale(2)"> 561 * <text font-size="10">abc</text> 562 * </g> 563 * </svg> 564 * 565 * a font size of 20 would be used. It's preferable to use a font size that 566 * is identical or close to the size that the text will appear on the screen, 567 * because at very small or large font sizes, text metrics will be computed 568 * differently due to the limited precision that text runs have. 569 * 570 * mFontSizeScaleFactor is the amount the actual font-size property value 571 * should be multiplied by to cause the text run font size to (a) be within a 572 * "reasonable" range, and (b) be close to the actual size to be painted on 573 * screen. (The "reasonable" range as determined by some #defines in 574 * SVGTextFrame.cpp is 8..200.) 575 */ 576 float mFontSizeScaleFactor = 1.0f; 577 578 /** 579 * The scale of the context that we last used to compute mFontSizeScaleFactor. 580 * We record this so that we can tell when our scale transform has changed 581 * enough to warrant reflowing the text. 582 */ 583 float mLastContextScale = 1.0f; 584 585 /** 586 * The amount that we need to scale each rendered run to account for 587 * lengthAdjust="spacingAndGlyphs". 588 */ 589 float mLengthAdjustScaleFactor = 1.0f; 590 591 public: 592 struct CachedMeasuredRange { 593 Range mRange; 594 nscoord mAdvance; 595 }; 596 597 void SetCurrentFrameForCaching(const nsTextFrame* aFrame) { 598 if (mFrameForCachedRanges != aFrame) { 599 PodArrayZero(mCachedRanges); 600 mFrameForCachedRanges = aFrame; 601 } 602 } 603 604 enum WhichRange { 605 Before, 606 After, 607 CachedRangeCount, 608 }; 609 610 CachedMeasuredRange& CachedRange(WhichRange aWhichRange) { 611 return mCachedRanges[aWhichRange]; 612 } 613 614 // Return a reference to a PropertyProvider for the given textframe; 615 // the provider is cached by SVGTextFrame to avoid creating it afresh 616 // for repeated operations involving the same textframe. 617 nsTextFrame::PropertyProvider& PropertyProviderFor(nsTextFrame* aFrame) { 618 if (!mCachedProvider || aFrame != mCachedProvider->GetFrame()) { 619 mCachedProvider.reset(); 620 mCachedProvider.emplace(aFrame, 621 aFrame->EnsureTextRun(nsTextFrame::eInflated)); 622 } 623 return mCachedProvider.ref(); 624 } 625 626 // Forget any cached PropertyProvider. This should be called at the end of 627 // any method that relied on PropertyProviderFor(), to avoid leaving a 628 // cached provider that may become invalid. 629 void ForgetCachedProvider() { mCachedProvider.reset(); } 630 631 private: 632 const nsTextFrame* mFrameForCachedRanges = nullptr; 633 CachedMeasuredRange mCachedRanges[CachedRangeCount]; 634 635 Maybe<nsTextFrame::PropertyProvider> mCachedProvider; 636 }; 637 638 } // namespace mozilla 639 640 #endif // LAYOUT_SVG_SVGTEXTFRAME_H_