Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/svg/./../../../layout/svg/SVGTextFrame.cpp
Warning:line 5102, column 14
Value stored to 'end' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_layout_svg1.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/svg -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/svg -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/layout/svg -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/svg -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/layout/base -I /root/firefox-clang/layout/generic -I /root/firefox-clang/layout/painting -I /root/firefox-clang/layout/style -I /root/firefox-clang/dom/base -I /root/firefox-clang/dom/svg -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-10-17-231001-351276-1 -x c++ Unified_cpp_layout_svg1.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5// Main header first:
6#include "SVGTextFrame.h"
7
8// Keep others in (case-insensitive) order:
9#include <algorithm>
10#include <cmath>
11#include <limits>
12#include <numbers>
13#include <numeric>
14
15#include "DOMSVGPoint.h"
16#include "SVGAnimatedNumberList.h"
17#include "SVGContentUtils.h"
18#include "SVGContextPaint.h"
19#include "SVGLengthList.h"
20#include "SVGNumberList.h"
21#include "SVGPaintServerFrame.h"
22#include "TextDrawTarget.h"
23#include "gfx2DGlue.h"
24#include "gfxContext.h"
25#include "gfxFont.h"
26#include "gfxSkipChars.h"
27#include "gfxTypes.h"
28#include "gfxUtils.h"
29#include "mozilla/CaretAssociationHint.h"
30#include "mozilla/DisplaySVGItem.h"
31#include "mozilla/Likely.h"
32#include "mozilla/PresShell.h"
33#include "mozilla/ReflowInput.h"
34#include "mozilla/SVGObserverUtils.h"
35#include "mozilla/SVGOuterSVGFrame.h"
36#include "mozilla/SVGUtils.h"
37#include "mozilla/StaticPrefs_gfx.h"
38#include "mozilla/dom/SVGGeometryElement.h"
39#include "mozilla/dom/SVGRect.h"
40#include "mozilla/dom/SVGTextContentElementBinding.h"
41#include "mozilla/dom/SVGTextPathElement.h"
42#include "mozilla/dom/SVGTextPathElementBinding.h"
43#include "mozilla/dom/Selection.h"
44#include "mozilla/dom/Text.h"
45#include "mozilla/gfx/2D.h"
46#include "mozilla/gfx/PatternHelpers.h"
47#include "mozilla/layers/StackingContextHelper.h"
48#include "mozilla/webrender/WebRenderAPI.h"
49#include "nsBidiPresUtils.h"
50#include "nsBlockFrame.h"
51#include "nsCaret.h"
52#include "nsContentUtils.h"
53#include "nsFrameSelection.h"
54#include "nsGkAtoms.h"
55#include "nsLayoutUtils.h"
56#include "nsStyleStructInlines.h"
57#include "nsTArray.h"
58#include "nsTHashSet.h"
59#include "nsTextFrame.h"
60
61using namespace mozilla::dom;
62using namespace mozilla::dom::SVGTextContentElement_Binding;
63using namespace mozilla::gfx;
64using namespace mozilla::image;
65
66namespace mozilla {
67
68// ============================================================================
69// Utility functions
70
71/**
72 * Using the specified gfxSkipCharsIterator, converts an offset and length
73 * in original char indexes to skipped char indexes.
74 *
75 * @param aIterator The gfxSkipCharsIterator to use for the conversion.
76 * @param aOriginalOffset The original offset.
77 * @param aOriginalLength The original length.
78 */
79static gfxTextRun::Range ConvertOriginalToSkipped(
80 gfxSkipCharsIterator& aIterator, uint32_t aOriginalOffset,
81 uint32_t aOriginalLength) {
82 uint32_t start = aIterator.ConvertOriginalToSkipped(aOriginalOffset);
83 aIterator.AdvanceOriginal(aOriginalLength);
84 return gfxTextRun::Range(start, aIterator.GetSkippedOffset());
85}
86
87/**
88 * Converts an nsPoint from app units to user space units using the specified
89 * nsPresContext and returns it as a gfxPoint.
90 */
91static gfxPoint AppUnitsToGfxUnits(const nsPoint& aPoint,
92 const nsPresContext* aContext) {
93 return gfxPoint(aContext->AppUnitsToGfxUnits(aPoint.x),
94 aContext->AppUnitsToGfxUnits(aPoint.y));
95}
96
97/**
98 * Converts a nsRect that is in app units to CSS pixels and returns it
99 * as a gfxRect.
100 */
101static gfxRect AppUnitsToFloatCSSPixels(const nsRect& aRect) {
102 return gfxRect(nsPresContext::AppUnitsToFloatCSSPixels(aRect.x),
103 nsPresContext::AppUnitsToFloatCSSPixels(aRect.y),
104 nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
105 nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
106}
107
108/**
109 * Gets the measured ascent and descent of the text in the given nsTextFrame
110 * in app units.
111 *
112 * @param aFrame The text frame.
113 * @param aAscent The ascent in app units (output).
114 * @param aDescent The descent in app units (output).
115 */
116static void GetAscentAndDescentInAppUnits(nsTextFrame* aFrame,
117 gfxFloat& aAscent,
118 gfxFloat& aDescent) {
119 gfxSkipCharsIterator it = aFrame->EnsureTextRun(nsTextFrame::eInflated);
120 gfxTextRun* textRun = aFrame->GetTextRun(nsTextFrame::eInflated);
121
122 gfxTextRun::Range range = ConvertOriginalToSkipped(
123 it, aFrame->GetContentOffset(), aFrame->GetContentLength());
124
125 textRun->GetLineHeightMetrics(range, aAscent, aDescent);
126}
127
128/**
129 * Updates an interval by intersecting it with another interval.
130 * The intervals are specified using a start index and a length.
131 */
132static void IntersectInterval(uint32_t& aStart, uint32_t& aLength,
133 uint32_t aStartOther, uint32_t aLengthOther) {
134 uint32_t aEnd = aStart + aLength;
135 uint32_t aEndOther = aStartOther + aLengthOther;
136
137 if (aStartOther >= aEnd || aStart >= aEndOther) {
138 aLength = 0;
139 } else {
140 if (aStartOther >= aStart) {
141 aStart = aStartOther;
142 }
143 aLength = std::min(aEnd, aEndOther) - aStart;
144 }
145}
146
147/**
148 * Intersects an interval as IntersectInterval does but by taking
149 * the offset and length of the other interval from a
150 * nsTextFrame::TrimmedOffsets object.
151 */
152static void TrimOffsets(uint32_t& aStart, uint32_t& aLength,
153 const nsTextFrame::TrimmedOffsets& aTrimmedOffsets) {
154 IntersectInterval(aStart, aLength, aTrimmedOffsets.mStart,
155 aTrimmedOffsets.mLength);
156}
157
158/**
159 * Returns the closest ancestor-or-self node that is not an SVG <a>
160 * element.
161 */
162static nsIContent* GetFirstNonAAncestor(nsIContent* aContent) {
163 while (aContent && aContent->IsSVGElement(nsGkAtoms::a)) {
164 aContent = aContent->GetParent();
165 }
166 return aContent;
167}
168
169/**
170 * Returns whether the given node is a text content element[1], taking into
171 * account whether it has a valid parent.
172 *
173 * For example, in:
174 *
175 * <svg xmlns="http://www.w3.org/2000/svg">
176 * <text><a/><text/></text>
177 * <tspan/>
178 * </svg>
179 *
180 * true would be returned for the outer <text> element and the <a> element,
181 * and false for the inner <text> element (since a <text> is not allowed
182 * to be a child of another <text>) and the <tspan> element (because it
183 * must be inside a <text> subtree).
184 *
185 * [1] https://svgwg.org/svg2-draft/intro.html#TermTextContentElement
186 */
187static bool IsTextContentElement(const nsIContent* aContent) {
188 if (aContent->IsSVGElement(nsGkAtoms::text)) {
189 const nsIContent* parent = GetFirstNonAAncestor(aContent->GetParent());
190 return !parent || !IsTextContentElement(parent);
191 }
192
193 if (aContent->IsSVGElement(nsGkAtoms::textPath)) {
194 const nsIContent* parent = GetFirstNonAAncestor(aContent->GetParent());
195 return parent && parent->IsSVGElement(nsGkAtoms::text);
196 }
197
198 return aContent->IsAnyOfSVGElements(nsGkAtoms::a, nsGkAtoms::tspan);
199}
200
201/**
202 * Returns whether the specified frame is an nsTextFrame that has some text
203 * content.
204 */
205static bool IsNonEmptyTextFrame(const nsIFrame* aFrame) {
206 const nsTextFrame* textFrame = do_QueryFrame(aFrame);
207 if (!textFrame) {
208 return false;
209 }
210
211 return textFrame->GetContentLength() != 0;
212}
213
214/**
215 * Takes an nsIFrame and if it is a text frame that has some text content,
216 * returns it as an nsTextFrame and its corresponding Text.
217 *
218 * @param aFrame The frame to look at.
219 * @param aTextFrame aFrame as an nsTextFrame (output).
220 * @param aTextNode The Text content of aFrame (output).
221 * @return true if aFrame is a non-empty text frame, false otherwise.
222 */
223static bool GetNonEmptyTextFrameAndNode(nsIFrame* aFrame,
224 nsTextFrame*& aTextFrame,
225 Text*& aTextNode) {
226 nsTextFrame* text = do_QueryFrame(aFrame);
227 bool isNonEmptyTextFrame = text && text->GetContentLength() != 0;
228
229 if (isNonEmptyTextFrame) {
230 nsIContent* content = text->GetContent();
231 NS_ASSERTION(content && content->IsText(),do { if (!(content && content->IsText())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "unexpected content type for nsTextFrame"
, "content && content->IsText()", "./../../../layout/svg/SVGTextFrame.cpp"
, 232); MOZ_PretendNoReturn(); } } while (0)
232 "unexpected content type for nsTextFrame")do { if (!(content && content->IsText())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "unexpected content type for nsTextFrame"
, "content && content->IsText()", "./../../../layout/svg/SVGTextFrame.cpp"
, 232); MOZ_PretendNoReturn(); } } while (0)
;
233
234 Text* node = content->AsText();
235 MOZ_ASSERT(node->TextLength() != 0,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node->TextLength() != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(node->TextLength() != 0))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("node->TextLength() != 0"
" (" "frame's GetContentLength() should be 0 if the text node "
"has no content" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "node->TextLength() != 0"
") (" "frame's GetContentLength() should be 0 if the text node "
"has no content" ")"); do { MOZ_CrashSequence(__null, 237); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
236 "frame's GetContentLength() should be 0 if the text node "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node->TextLength() != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(node->TextLength() != 0))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("node->TextLength() != 0"
" (" "frame's GetContentLength() should be 0 if the text node "
"has no content" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "node->TextLength() != 0"
") (" "frame's GetContentLength() should be 0 if the text node "
"has no content" ")"); do { MOZ_CrashSequence(__null, 237); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
237 "has no content")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node->TextLength() != 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(node->TextLength() != 0))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("node->TextLength() != 0"
" (" "frame's GetContentLength() should be 0 if the text node "
"has no content" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "node->TextLength() != 0"
") (" "frame's GetContentLength() should be 0 if the text node "
"has no content" ")"); do { MOZ_CrashSequence(__null, 237); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
238
239 aTextFrame = text;
240 aTextNode = node;
241 }
242
243 MOZ_ASSERT(IsNonEmptyTextFrame(aFrame) == isNonEmptyTextFrame,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsNonEmptyTextFrame(aFrame) == isNonEmptyTextFrame)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(IsNonEmptyTextFrame(aFrame) == isNonEmptyTextFrame))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsNonEmptyTextFrame(aFrame) == isNonEmptyTextFrame"
" (" "our logic should agree with IsNonEmptyTextFrame" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsNonEmptyTextFrame(aFrame) == isNonEmptyTextFrame"
") (" "our logic should agree with IsNonEmptyTextFrame" ")")
; do { MOZ_CrashSequence(__null, 244); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
244 "our logic should agree with IsNonEmptyTextFrame")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsNonEmptyTextFrame(aFrame) == isNonEmptyTextFrame)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(IsNonEmptyTextFrame(aFrame) == isNonEmptyTextFrame))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsNonEmptyTextFrame(aFrame) == isNonEmptyTextFrame"
" (" "our logic should agree with IsNonEmptyTextFrame" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsNonEmptyTextFrame(aFrame) == isNonEmptyTextFrame"
") (" "our logic should agree with IsNonEmptyTextFrame" ")")
; do { MOZ_CrashSequence(__null, 244); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
245 return isNonEmptyTextFrame;
246}
247
248/**
249 * Returns whether the specified atom is for one of the five
250 * glyph positioning attributes that can appear on SVG text
251 * elements -- x, y, dx, dy or rotate.
252 */
253static bool IsGlyphPositioningAttribute(const nsAtom* aAttribute) {
254 return aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
255 aAttribute == nsGkAtoms::dx || aAttribute == nsGkAtoms::dy ||
256 aAttribute == nsGkAtoms::rotate;
257}
258
259/**
260 * Returns the position in app units of a given baseline (using an
261 * SVG dominant-baseline property value) for a given nsTextFrame.
262 *
263 * @param aFrame The text frame to inspect.
264 * @param aTextRun The text run of aFrame.
265 * @param aDominantBaseline The dominant-baseline value to use.
266 */
267static nscoord GetBaselinePosition(nsTextFrame* aFrame,
268 const gfxTextRun* aTextRun,
269 StyleDominantBaseline aDominantBaseline,
270 float aFontSizeScaleFactor) {
271 WritingMode writingMode = aFrame->GetWritingMode();
272 gfxFloat ascent, descent;
273 aTextRun->GetLineHeightMetrics(ascent, descent);
274
275 auto convertIfVerticalRL = [&](gfxFloat dominantBaseline) {
276 return writingMode.IsVerticalRL() ? ascent + descent - dominantBaseline
277 : dominantBaseline;
278 };
279
280 switch (aDominantBaseline) {
281 case StyleDominantBaseline::Hanging:
282 return convertIfVerticalRL(ascent * 0.2);
283 case StyleDominantBaseline::TextTop:
284 return convertIfVerticalRL(0);
285
286 case StyleDominantBaseline::Alphabetic:
287 return writingMode.IsVerticalRL()
288 ? ascent * 0.5
289 : aFrame->GetLogicalBaseline(writingMode);
290
291 case StyleDominantBaseline::Auto:
292 return convertIfVerticalRL(aFrame->GetLogicalBaseline(writingMode));
293
294 case StyleDominantBaseline::Middle:
295 return convertIfVerticalRL(aFrame->GetLogicalBaseline(writingMode) -
296 SVGContentUtils::GetFontXHeight(aFrame) / 2.0 *
297 AppUnitsPerCSSPixel() *
298 aFontSizeScaleFactor);
299
300 case StyleDominantBaseline::TextBottom:
301 case StyleDominantBaseline::Ideographic:
302 return convertIfVerticalRL(ascent + descent);
303
304 case StyleDominantBaseline::Central:
305 return (ascent + descent) / 2.0;
306 case StyleDominantBaseline::Mathematical:
307 return convertIfVerticalRL(ascent / 2.0);
308 }
309
310 MOZ_ASSERT_UNREACHABLE("unexpected dominant-baseline value")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"unexpected dominant-baseline value" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 310); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "unexpected dominant-baseline value"
")"); do { MOZ_CrashSequence(__null, 310); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
311 return convertIfVerticalRL(aFrame->GetLogicalBaseline(writingMode));
312}
313
314/**
315 * Truncates an array to be at most the length of another array.
316 *
317 * @param aArrayToTruncate The array to truncate.
318 * @param aReferenceArray The array whose length will be used to truncate
319 * aArrayToTruncate to.
320 */
321template <typename T, typename U>
322static void TruncateTo(nsTArray<T>& aArrayToTruncate,
323 const nsTArray<U>& aReferenceArray) {
324 uint32_t length = aReferenceArray.Length();
325 if (aArrayToTruncate.Length() > length) {
326 aArrayToTruncate.TruncateLength(length);
327 }
328}
329
330/**
331 * Asserts that the anonymous block child of the SVGTextFrame has been
332 * reflowed (or does not exist). Returns null if the child has not been
333 * reflowed, and the frame otherwise.
334 *
335 * We check whether the kid has been reflowed and not the frame itself
336 * since we sometimes need to call this function during reflow, after the
337 * kid has been reflowed but before we have cleared the dirty bits on the
338 * frame itself.
339 */
340static SVGTextFrame* FrameIfAnonymousChildReflowed(SVGTextFrame* aFrame) {
341 MOZ_ASSERT(aFrame, "aFrame must not be null")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aFrame)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aFrame))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aFrame" " (" "aFrame must not be null"
")", "./../../../layout/svg/SVGTextFrame.cpp", 341); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aFrame" ") (" "aFrame must not be null" ")"
); do { MOZ_CrashSequence(__null, 341); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
342 nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
343 if (kid->IsSubtreeDirty()) {
344 MOZ_ASSERT(false, "should have already reflowed the anonymous block child")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "should have already reflowed the anonymous block child"
")", "./../../../layout/svg/SVGTextFrame.cpp", 344); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "should have already reflowed the anonymous block child"
")"); do { MOZ_CrashSequence(__null, 344); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
345 return nullptr;
346 }
347 return aFrame;
348}
349
350// FIXME(emilio): SVG is a special-case where transforms affect layout. We don't
351// want that to go outside the SVG stuff (and really we should aim to remove
352// that).
353static float GetContextScale(SVGTextFrame* aFrame) {
354 if (aFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
355 // When we are non-display, we could be painted in different coordinate
356 // spaces, and we don't want to have to reflow for each of these. We just
357 // assume that the context scale is 1.0 for them all, so we don't get stuck
358 // with a font size scale factor based on whichever referencing frame
359 // happens to reflow first.
360 return 1.0f;
361 }
362 auto matrix = nsLayoutUtils::GetTransformToAncestor(
363 RelativeTo{aFrame}, RelativeTo{SVGUtils::GetOuterSVGFrame(aFrame)});
364 Matrix transform2D;
365 if (!matrix.CanDraw2D(&transform2D)) {
366 return 1.0f;
367 }
368 auto scales = transform2D.ScaleFactors();
369 return std::max(0.0f, std::max(scales.xScale, scales.yScale));
370}
371
372// ============================================================================
373// Utility classes
374
375// ----------------------------------------------------------------------------
376// TextRenderedRun
377
378/**
379 * A run of text within a single nsTextFrame whose glyphs can all be painted
380 * with a single call to nsTextFrame::PaintText. A text rendered run can
381 * be created for a sequence of two or more consecutive glyphs as long as:
382 *
383 * - Only the first glyph has (or none of the glyphs have) been positioned
384 * with SVG text positioning attributes
385 * - All of the glyphs have zero rotation
386 * - The glyphs are not on a text path
387 * - The glyphs correspond to content within the one nsTextFrame
388 *
389 * A TextRenderedRunIterator produces TextRenderedRuns required for painting a
390 * whole SVGTextFrame.
391 */
392struct TextRenderedRun {
393 using Range = gfxTextRun::Range;
394
395 /**
396 * Constructs a TextRenderedRun that is uninitialized except for mFrame
397 * being null.
398 */
399 TextRenderedRun() : mFrame(nullptr) {}
400
401 /**
402 * Constructs a TextRenderedRun with all of the information required to
403 * paint it. See the comments documenting the member variables below
404 * for descriptions of the arguments.
405 */
406 TextRenderedRun(nsTextFrame* aFrame, SVGTextFrame* aSVGTextFrame,
407 const gfxPoint& aPosition, double aRotate,
408 float aFontSizeScaleFactor, nscoord aBaseline,
409 uint32_t aTextFrameContentOffset,
410 uint32_t aTextFrameContentLength,
411 uint32_t aTextElementCharIndex)
412 : mFrame(aFrame),
413 mRoot(aSVGTextFrame),
414 mPosition(aPosition),
415 mLengthAdjustScaleFactor(mRoot->mLengthAdjustScaleFactor),
416 mRotate(static_cast<float>(aRotate)),
417 mFontSizeScaleFactor(aFontSizeScaleFactor),
418 mBaseline(aBaseline),
419 mTextFrameContentOffset(aTextFrameContentOffset),
420 mTextFrameContentLength(aTextFrameContentLength),
421 mTextElementCharIndex(aTextElementCharIndex) {}
422
423 /**
424 * Returns the text run for the text frame that this rendered run is part of.
425 */
426 gfxTextRun* GetTextRun() const {
427 mFrame->EnsureTextRun(nsTextFrame::eInflated);
428 return mFrame->GetTextRun(nsTextFrame::eInflated);
429 }
430
431 /**
432 * Return true if the logical inline direction is reversed compared to
433 * normal physical coordinates (i.e. if it is leftwards or upwards).
434 */
435 bool IsInlineReversed() const { return GetTextRun()->IsInlineReversed(); }
436
437 /**
438 * Returns whether this rendered run is vertical.
439 */
440 bool IsVertical() const { return GetTextRun()->IsVertical(); }
441
442 /**
443 * Returns the transform that converts from a <text> element's user space into
444 * the coordinate space that rendered runs can be painted directly in.
445 *
446 * The difference between this method and
447 * GetTransformFromRunUserSpaceToUserSpace is that when calling in to
448 * nsTextFrame::PaintText, it will already take into account any left clip
449 * edge (that is, it doesn't just apply a visual clip to the rendered text, it
450 * shifts the glyphs over so that they are painted with their left edge at the
451 * x coordinate passed in to it). Thus we need to account for this in our
452 * transform.
453 *
454 *
455 * Assume that we have:
456 *
457 * <text x="100" y="100" rotate="0 0 1 0 0 * 1">abcdef</text>.
458 *
459 * This would result in four text rendered runs:
460 *
461 * - one for "ab"
462 * - one for "c"
463 * - one for "de"
464 * - one for "f"
465 *
466 * Assume now that we are painting the third TextRenderedRun. It will have
467 * a left clip edge that is the sum of the advances of "abc", and it will
468 * have a right clip edge that is the advance of "f". In
469 * SVGTextFrame::PaintSVG(), we pass in nsPoint() (i.e., the origin)
470 * as the point at which to paint the text frame, and we pass in the
471 * clip edge values. The nsTextFrame will paint the substring of its
472 * text such that the top-left corner of the "d"'s glyph cell will be at
473 * (0, 0) in the current coordinate system.
474 *
475 * Thus, GetTransformFromUserSpaceForPainting must return a transform from
476 * whatever user space the <text> element is in to a coordinate space in
477 * device pixels (as that's what nsTextFrame works in) where the origin is at
478 * the same position as our user space mPositions[i].mPosition value for
479 * the "d" glyph, which will be (100 + userSpaceAdvance("abc"), 100).
480 * The translation required to do this (ignoring the scale to get from
481 * user space to device pixels, and ignoring the
482 * (100 + userSpaceAdvance("abc"), 100) translation) is:
483 *
484 * (-leftEdge, -baseline)
485 *
486 * where baseline is the distance between the baseline of the text and the top
487 * edge of the nsTextFrame. We translate by -leftEdge horizontally because
488 * the nsTextFrame will already shift the glyphs over by that amount and start
489 * painting glyphs at x = 0. We translate by -baseline vertically so that
490 * painting the top edges of the glyphs at y = 0 will result in their
491 * baselines being at our desired y position.
492 *
493 *
494 * Now for an example with RTL text. Assume our content is now
495 * <text x="100" y="100" rotate="0 0 1 0 0 1">WERBEH</text>. We'd have
496 * the following text rendered runs:
497 *
498 * - one for "EH"
499 * - one for "B"
500 * - one for "ER"
501 * - one for "W"
502 *
503 * Again, we are painting the third TextRenderedRun. The left clip edge
504 * is the advance of the "W" and the right clip edge is the sum of the
505 * advances of "BEH". Our translation to get the rendered "ER" glyphs
506 * in the right place this time is:
507 *
508 * (-frameWidth + rightEdge, -baseline)
509 *
510 * which is equivalent to:
511 *
512 * (-(leftEdge + advance("ER")), -baseline)
513 *
514 * The reason we have to shift left additionally by the width of the run
515 * of glyphs we are painting is that although the nsTextFrame is RTL,
516 * we still supply the top-left corner to paint the frame at when calling
517 * nsTextFrame::PaintText, even though our user space positions for each
518 * glyph in mPositions specifies the origin of each glyph, which for RTL
519 * glyphs is at the right edge of the glyph cell.
520 *
521 *
522 * For any other use of an nsTextFrame in the context of a particular run
523 * (such as hit testing, or getting its rectangle),
524 * GetTransformFromRunUserSpaceToUserSpace should be used.
525 *
526 * @param aContext The context to use for unit conversions.
527 */
528 gfxMatrix GetTransformFromUserSpaceForPainting(
529 nsPresContext* aContext, const nscoord aVisIStartEdge,
530 const nscoord aVisIEndEdge) const;
531
532 /**
533 * Returns the transform that converts from "run user space" to a <text>
534 * element's user space. Run user space is a coordinate system that has the
535 * same size as the <text>'s user space but rotated and translated such that
536 * (0,0) is the top-left of the rectangle that bounds the text.
537 *
538 * @param aContext The context to use for unit conversions.
539 */
540 gfxMatrix GetTransformFromRunUserSpaceToUserSpace(
541 nsPresContext* aContext) const;
542
543 /**
544 * Returns the transform that converts from "run user space" to float pixels
545 * relative to the nsTextFrame that this rendered run is a part of.
546 *
547 * @param aContext The context to use for unit conversions.
548 */
549 gfxMatrix GetTransformFromRunUserSpaceToFrameUserSpace(
550 nsPresContext* aContext) const;
551
552 /**
553 * Flag values used for the aFlags arguments of GetRunUserSpaceRect,
554 * GetFrameUserSpaceRect and GetUserSpaceRect.
555 */
556 enum class GeometryFlag {
557 // Includes the fill geometry of the text in the returned rectangle.
558 IncludeFill,
559 // Includes the stroke geometry of the text in the returned rectangle.
560 IncludeStroke,
561 // Don't include any horizontal glyph overflow in the returned rectangle.
562 NoHorizontalOverflow
563 };
564 using GeometryFlags = EnumSet<GeometryFlag>;
565
566 /**
567 * Returns a rectangle that bounds the fill and/or stroke of the rendered run
568 * in run user space.
569 *
570 * @param aFlags Flags indicating what parts of the text to include in
571 * the rectangle.
572 */
573 SVGBBox GetRunUserSpaceRect(GeometryFlags aFlags) const;
574
575 /**
576 * Returns a rectangle that covers the fill and/or stroke of the rendered run
577 * in "frame user space".
578 *
579 * Frame user space is a coordinate space of the same scale as the <text>
580 * element's user space, but with its rotation set to the rotation of
581 * the glyphs within this rendered run and its origin set to the position
582 * such that placing the nsTextFrame there would result in the glyphs in
583 * this rendered run being at their correct positions.
584 *
585 * For example, say we have <text x="100 150" y="100">ab</text>. Assume
586 * the advance of both the "a" and the "b" is 12 user units, and the
587 * ascent of the text is 8 user units and its descent is 6 user units,
588 * and that we are not measuing the stroke of the text, so that we stay
589 * entirely within the glyph cells.
590 *
591 * There will be two text rendered runs, one for "a" and one for "b".
592 *
593 * The frame user space for the "a" run will have its origin at
594 * (100, 100 - 8) in the <text> element's user space and will have its
595 * axes aligned with the user space (since there is no rotate="" or
596 * text path involve) and with its scale the same as the user space.
597 * The rect returned by this method will be (0, 0, 12, 14), since the "a"
598 * glyph is right at the left of the nsTextFrame.
599 *
600 * The frame user space for the "b" run will have its origin at
601 * (150 - 12, 100 - 8), and scale/rotation the same as above. The rect
602 * returned by this method will be (12, 0, 12, 14), since we are
603 * advance("a") horizontally in to the text frame.
604 *
605 * @param aContext The context to use for unit conversions.
606 * @param aFlags Flags indicating what parts of the text to include in
607 * the rectangle.
608 */
609 SVGBBox GetFrameUserSpaceRect(nsPresContext* aContext,
610 GeometryFlags aFlags) const;
611
612 /**
613 * Returns a rectangle that covers the fill and/or stroke of the rendered run
614 * in the <text> element's user space.
615 *
616 * @param aContext The context to use for unit conversions.
617 * @param aFlags A combination of flags indicating what parts of
618 * the text to include in the rectangle.
619 * @param aAdditionalTransform An additional transform to apply to the
620 * frame user space rectangle before its bounds are transformed into
621 * user space.
622 */
623 SVGBBox GetUserSpaceRect(
624 nsPresContext* aContext, GeometryFlags aFlags,
625 const gfxMatrix* aAdditionalTransform = nullptr) const;
626
627 /**
628 * Gets the app unit amounts to clip from the left and right edges of
629 * the nsTextFrame in order to paint just this rendered run.
630 *
631 * Note that if clip edge amounts land in the middle of a glyph, the
632 * glyph won't be painted at all. The clip edges are thus more of
633 * a selection mechanism for which glyphs will be painted, rather
634 * than a geometric clip.
635 */
636 void GetClipEdges(nscoord& aVisIStartEdge, nscoord& aVisIEndEdge) const;
637
638 /**
639 * Returns the advance width of the whole rendered run.
640 */
641 nscoord GetAdvanceWidth() const;
642
643 /**
644 * Returns the index of the character into this rendered run whose
645 * glyph cell contains the given point, or -1 if there is no such
646 * character. This does not hit test against any overflow.
647 *
648 * @param aContext The context to use for unit conversions.
649 * @param aPoint The point in the user space of the <text> element.
650 */
651 int32_t GetCharNumAtPosition(nsPresContext* aContext,
652 const gfxPoint& aPoint) const;
653
654 /**
655 * The text frame that this rendered run lies within.
656 */
657 nsTextFrame* mFrame;
658
659 /**
660 * The SVGTextFrame to which our text frame belongs.
661 */
662 SVGTextFrame* mRoot;
663
664 /**
665 * The point in user space that the text is positioned at.
666 *
667 * For a horizontal run:
668 * The x coordinate is the left edge of a LTR run of text or the right edge of
669 * an RTL run. The y coordinate is the baseline of the text.
670 * For a vertical run:
671 * The x coordinate is the baseline of the text.
672 * The y coordinate is the top edge of a LTR run, or bottom of RTL.
673 */
674 gfxPoint mPosition;
675
676 /**
677 * The horizontal scale factor to apply when painting glyphs to take
678 * into account textLength="".
679 */
680 float mLengthAdjustScaleFactor;
681
682 /**
683 * The rotation in radians in the user coordinate system that the text has.
684 */
685 float mRotate;
686
687 /**
688 * The scale factor that was used to transform the text run's original font
689 * size into a sane range for painting and measurement.
690 */
691 double mFontSizeScaleFactor;
692
693 /**
694 * The baseline in app units of this text run. The measurement is from the
695 * top of the text frame. (From the left edge if vertical.)
696 */
697 nscoord mBaseline;
698
699 /**
700 * The offset and length in mFrame's content Text that corresponds to
701 * this text rendered run. These are original char indexes.
702 */
703 uint32_t mTextFrameContentOffset;
704 uint32_t mTextFrameContentLength;
705
706 /**
707 * The character index in the whole SVG <text> element that this text rendered
708 * run begins at.
709 */
710 uint32_t mTextElementCharIndex;
711};
712
713gfxMatrix TextRenderedRun::GetTransformFromUserSpaceForPainting(
714 nsPresContext* aContext, const nscoord aVisIStartEdge,
715 const nscoord aVisIEndEdge) const {
716 // We transform to device pixels positioned such that painting the text frame
717 // at (0,0) with aItem will result in the text being in the right place.
718
719 gfxMatrix m;
720 if (!mFrame) {
721 return m;
722 }
723
724 float cssPxPerDevPx =
725 nsPresContext::AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
726
727 // Glyph position in user space.
728 m.PreTranslate(mPosition / cssPxPerDevPx);
729
730 // Take into account any font size scaling and scaling due to textLength="".
731 m.PreScale(1.0 / mFontSizeScaleFactor, 1.0 / mFontSizeScaleFactor);
732
733 // Rotation due to rotate="" or a <textPath>.
734 m.PreRotate(mRotate);
735
736 // Scale for textLength="" and translate to get the text frame
737 // to the right place.
738 nsPoint t;
739 if (IsVertical()) {
740 m.PreScale(1.0, mLengthAdjustScaleFactor);
741 t = nsPoint(-mBaseline, IsInlineReversed()
742 ? -mFrame->GetRect().height + aVisIEndEdge
743 : -aVisIStartEdge);
744 } else {
745 m.PreScale(mLengthAdjustScaleFactor, 1.0);
746 t = nsPoint(IsInlineReversed() ? -mFrame->GetRect().width + aVisIEndEdge
747 : -aVisIStartEdge,
748 -mBaseline);
749 }
750 m.PreTranslate(AppUnitsToGfxUnits(t, aContext));
751
752 return m;
753}
754
755gfxMatrix TextRenderedRun::GetTransformFromRunUserSpaceToUserSpace(
756 nsPresContext* aContext) const {
757 gfxMatrix m;
758 if (!mFrame) {
759 return m;
760 }
761
762 float cssPxPerDevPx =
763 nsPresContext::AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
764
765 nscoord start, end;
766 GetClipEdges(start, end);
767
768 // Glyph position in user space.
769 m.PreTranslate(mPosition);
770
771 // Rotation due to rotate="" or a <textPath>.
772 m.PreRotate(mRotate);
773
774 // Scale for textLength="" and translate to get the text frame
775 // to the right place.
776
777 nsPoint t;
778 if (IsVertical()) {
779 m.PreScale(1.0, mLengthAdjustScaleFactor);
780 t = nsPoint(-mBaseline, IsInlineReversed()
781 ? -mFrame->GetRect().height + start + end
782 : 0);
783 } else {
784 m.PreScale(mLengthAdjustScaleFactor, 1.0);
785 t = nsPoint(IsInlineReversed() ? -mFrame->GetRect().width + start + end : 0,
786 -mBaseline);
787 }
788 m.PreTranslate(AppUnitsToGfxUnits(t, aContext) * cssPxPerDevPx /
789 mFontSizeScaleFactor);
790
791 return m;
792}
793
794gfxMatrix TextRenderedRun::GetTransformFromRunUserSpaceToFrameUserSpace(
795 nsPresContext* aContext) const {
796 gfxMatrix m;
797 if (!mFrame) {
798 return m;
799 }
800
801 nscoord start, end;
802 GetClipEdges(start, end);
803
804 // Translate by the horizontal distance into the text frame this
805 // rendered run is.
806 gfxFloat appPerCssPx = AppUnitsPerCSSPixel();
807 gfxPoint t = IsVertical() ? gfxPoint(0, start / appPerCssPx)
808 : gfxPoint(start / appPerCssPx, 0);
809 return m.PreTranslate(t);
810}
811
812SVGBBox TextRenderedRun::GetRunUserSpaceRect(GeometryFlags aFlags) const {
813 SVGBBox r;
814 if (!mFrame) {
815 return r;
816 }
817
818 // Determine the amount of overflow around frame's mRect.
819 //
820 // We need to call InkOverflowRectRelativeToSelf because this includes
821 // overflowing decorations, which the MeasureText call below does not.
822 nsRect self = mFrame->InkOverflowRectRelativeToSelf();
823 nsRect rect = mFrame->GetRect();
824 bool vertical = IsVertical();
825 nsMargin inkOverflow(
826 vertical ? -self.x : -self.y,
827 vertical ? self.YMost() - rect.height : self.XMost() - rect.width,
828 vertical ? self.XMost() - rect.width : self.YMost() - rect.height,
829 vertical ? -self.y : -self.x);
830
831 gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
832 gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
833
834 // Get the content range for this rendered run.
835 Range range = ConvertOriginalToSkipped(it, mTextFrameContentOffset,
836 mTextFrameContentLength);
837 if (range.Length() == 0) {
838 return r;
839 }
840
841 auto& provider = mRoot->PropertyProviderFor(mFrame);
842
843 // Measure that range.
844 gfxTextRun::Metrics metrics = textRun->MeasureText(
845 range, gfxFont::LOOSE_INK_EXTENTS, nullptr, &provider);
846 // Make sure it includes the font-box.
847 gfxRect fontBox(0, -metrics.mAscent, metrics.mAdvanceWidth,
848 metrics.mAscent + metrics.mDescent);
849 metrics.mBoundingBox.UnionRect(metrics.mBoundingBox, fontBox);
850
851 // Determine the rectangle that covers the rendered run's fill,
852 // taking into account the measured overflow due to decorations.
853 nscoord baseline =
854 NSToCoordRoundWithClamp(metrics.mBoundingBox.y + metrics.mAscent);
855 gfxFloat x, width;
856 if (aFlags.contains(GeometryFlag::NoHorizontalOverflow)) {
857 x = 0.0;
858 width = textRun->GetAdvanceWidth(range, &provider);
859 if (width < 0.0) {
860 x = width;
861 width = -width;
862 }
863 } else {
864 x = metrics.mBoundingBox.x;
865 width = metrics.mBoundingBox.width;
866 }
867 nsRect fillInAppUnits(NSToCoordRoundWithClamp(x), baseline,
868 NSToCoordRoundWithClamp(width),
869 NSToCoordRoundWithClamp(metrics.mBoundingBox.height));
870 fillInAppUnits.Inflate(inkOverflow);
871 if (textRun->IsVertical()) {
872 // Swap line-relative textMetrics dimensions to physical coordinates.
873 std::swap(fillInAppUnits.x, fillInAppUnits.y);
874 std::swap(fillInAppUnits.width, fillInAppUnits.height);
875 }
876
877 // Convert the app units rectangle to user units.
878 gfxRect fill = AppUnitsToFloatCSSPixels(fillInAppUnits);
879
880 // Scale the rectangle up due to any mFontSizeScaleFactor.
881 fill.Scale(1.0 / mFontSizeScaleFactor);
882
883 // Include the fill if requested.
884 if (aFlags.contains(GeometryFlag::IncludeFill)) {
885 r = fill;
886 }
887
888 // Include the stroke if requested.
889 if (aFlags.contains(GeometryFlag::IncludeStroke) && !fill.IsEmpty() &&
890 SVGUtils::GetStrokeWidth(mFrame) > 0) {
891 r.UnionEdges(
892 SVGUtils::PathExtentsToMaxStrokeExtents(fill, mFrame, gfxMatrix()));
893 }
894
895 return r;
896}
897
898SVGBBox TextRenderedRun::GetFrameUserSpaceRect(nsPresContext* aContext,
899 GeometryFlags aFlags) const {
900 SVGBBox r = GetRunUserSpaceRect(aFlags);
901 if (r.IsEmpty()) {
902 return r;
903 }
904 gfxMatrix m = GetTransformFromRunUserSpaceToFrameUserSpace(aContext);
905 return m.TransformBounds(r.ToThebesRect());
906}
907
908SVGBBox TextRenderedRun::GetUserSpaceRect(
909 nsPresContext* aContext, GeometryFlags aFlags,
910 const gfxMatrix* aAdditionalTransform) const {
911 SVGBBox r = GetRunUserSpaceRect(aFlags);
912 if (r.IsEmpty()) {
913 return r;
914 }
915 gfxMatrix m = GetTransformFromRunUserSpaceToUserSpace(aContext);
916 if (aAdditionalTransform) {
917 m *= *aAdditionalTransform;
918 }
919 return m.TransformBounds(r.ToThebesRect());
920}
921
922void TextRenderedRun::GetClipEdges(nscoord& aVisIStartEdge,
923 nscoord& aVisIEndEdge) const {
924 uint32_t contentLength = mFrame->GetContentLength();
925 if (mTextFrameContentOffset == 0 &&
926 mTextFrameContentLength == contentLength) {
927 // If the rendered run covers the entire content, we know we don't need
928 // to clip without having to measure anything.
929 aVisIStartEdge = 0;
930 aVisIEndEdge = 0;
931 return;
932 }
933
934 gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
935 gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
936 auto& provider = mRoot->PropertyProviderFor(mFrame);
937
938 // Get the covered content offset/length for this rendered run in skipped
939 // characters, since that is what GetAdvanceWidth expects.
940 Range runRange = ConvertOriginalToSkipped(it, mTextFrameContentOffset,
941 mTextFrameContentLength);
942
943 // Get the offset/length of the whole nsTextFrame.
944 uint32_t frameOffset = mFrame->GetContentOffset();
945 uint32_t frameLength = mFrame->GetContentLength();
946
947 // Trim the whole-nsTextFrame offset/length to remove any leading/trailing
948 // white space, as the nsTextFrame when painting does not include them when
949 // interpreting clip edges.
950 nsTextFrame::TrimmedOffsets trimmedOffsets =
951 mFrame->GetTrimmedOffsets(mFrame->CharacterDataBuffer());
952 TrimOffsets(frameOffset, frameLength, trimmedOffsets);
953
954 // Convert the trimmed whole-nsTextFrame offset/length into skipped
955 // characters.
956 Range frameRange = ConvertOriginalToSkipped(it, frameOffset, frameLength);
957
958 // Get the advance of aRange, using the aCachedRange if available to
959 // accelerate textrun measurement.
960 auto MeasureUsingCache = [&](SVGTextFrame::CachedMeasuredRange& aCachedRange,
961 const Range& aRange) -> nscoord {
962 if (aRange.Intersects(aCachedRange.mRange)) {
963 // Figure out the deltas between the cached range and the new one at the
964 // start and end edges.
965 Range startDelta, endDelta;
966 int startSign = 0, endSign = 0;
967 if (aRange.start < aCachedRange.mRange.start) {
968 // This range extends the cached range at the start.
969 startSign = 1;
970 startDelta = Range(aRange.start, aCachedRange.mRange.start);
971 } else if (aRange.start > aCachedRange.mRange.start) {
972 // This range trims the cached range at the start.
973 startSign = -1;
974 startDelta = Range(aCachedRange.mRange.start, aRange.start);
975 }
976 if (aRange.end > aCachedRange.mRange.end) {
977 // This range extends the cached range at the end.
978 endSign = 1;
979 endDelta = Range(aCachedRange.mRange.end, aRange.end);
980 } else if (aRange.end < aCachedRange.mRange.end) {
981 // This range trims the cached range at the end.
982 endSign = -1;
983 endDelta = Range(aRange.end, aCachedRange.mRange.end);
984 }
985 // If the total of the deltas is less than the length of aRange,
986 // it will be cheaper to measure them and adjust the cached advance
987 // instead of measuring the whole of aRange.
988 if (startDelta.Length() + endDelta.Length() < aRange.Length()) {
989 if (startSign) {
990 aCachedRange.mAdvance +=
991 startSign * textRun->GetAdvanceWidth(startDelta, &provider);
992 }
993 if (endSign) {
994 aCachedRange.mAdvance +=
995 endSign * textRun->GetAdvanceWidth(endDelta, &provider);
996 }
997 } else {
998 aCachedRange.mAdvance = textRun->GetAdvanceWidth(aRange, &provider);
999 }
1000 } else {
1001 // Just measure the range, and cache the result.
1002 aCachedRange.mAdvance = textRun->GetAdvanceWidth(aRange, &provider);
1003 }
1004 aCachedRange.mRange = aRange;
1005 return aCachedRange.mAdvance;
1006 };
1007
1008 mRoot->SetCurrentFrameForCaching(mFrame);
1009 nscoord startEdge =
1010 MeasureUsingCache(mRoot->CachedRange(SVGTextFrame::WhichRange::Before),
1011 Range(frameRange.start, runRange.start));
1012 nscoord endEdge =
1013 MeasureUsingCache(mRoot->CachedRange(SVGTextFrame::WhichRange::After),
1014 Range(runRange.end, frameRange.end));
1015
1016 if (textRun->IsInlineReversed()) {
1017 aVisIStartEdge = endEdge;
1018 aVisIEndEdge = startEdge;
1019 } else {
1020 aVisIStartEdge = startEdge;
1021 aVisIEndEdge = endEdge;
1022 }
1023}
1024
1025nscoord TextRenderedRun::GetAdvanceWidth() const {
1026 gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
1027 gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
1028 auto& provider = mRoot->PropertyProviderFor(mFrame);
1029
1030 Range range = ConvertOriginalToSkipped(it, mTextFrameContentOffset,
1031 mTextFrameContentLength);
1032
1033 return textRun->GetAdvanceWidth(range, &provider);
1034}
1035
1036int32_t TextRenderedRun::GetCharNumAtPosition(nsPresContext* aContext,
1037 const gfxPoint& aPoint) const {
1038 if (mTextFrameContentLength == 0) {
1039 return -1;
1040 }
1041
1042 float cssPxPerDevPx =
1043 nsPresContext::AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
1044
1045 // Convert the point from user space into run user space, and take
1046 // into account any mFontSizeScaleFactor.
1047 gfxMatrix m = GetTransformFromRunUserSpaceToUserSpace(aContext);
1048 if (!m.Invert()) {
1049 return -1;
1050 }
1051 gfxPoint p = m.TransformPoint(aPoint) / cssPxPerDevPx * mFontSizeScaleFactor;
1052
1053 // First check that the point lies vertically between the top and bottom
1054 // edges of the text.
1055 gfxFloat ascent, descent;
1056 GetAscentAndDescentInAppUnits(mFrame, ascent, descent);
1057
1058 WritingMode writingMode = mFrame->GetWritingMode();
1059 if (writingMode.IsVertical()) {
1060 gfxFloat leftEdge = mFrame->GetLogicalBaseline(writingMode) -
1061 (writingMode.IsVerticalRL() ? ascent : descent);
1062 gfxFloat rightEdge = leftEdge + ascent + descent;
1063 if (p.x < aContext->AppUnitsToGfxUnits(leftEdge) ||
1064 p.x > aContext->AppUnitsToGfxUnits(rightEdge)) {
1065 return -1;
1066 }
1067 } else {
1068 gfxFloat topEdge = mFrame->GetLogicalBaseline(writingMode) - ascent;
1069 gfxFloat bottomEdge = topEdge + ascent + descent;
1070 if (p.y < aContext->AppUnitsToGfxUnits(topEdge) ||
1071 p.y > aContext->AppUnitsToGfxUnits(bottomEdge)) {
1072 return -1;
1073 }
1074 }
1075
1076 gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated);
1077 gfxTextRun* textRun = mFrame->GetTextRun(nsTextFrame::eInflated);
1078 auto& provider = mRoot->PropertyProviderFor(mFrame);
1079
1080 // Next check that the point lies horizontally within the left and right
1081 // edges of the text.
1082 Range range = ConvertOriginalToSkipped(it, mTextFrameContentOffset,
1083 mTextFrameContentLength);
1084 gfxFloat runAdvance =
1085 aContext->AppUnitsToGfxUnits(textRun->GetAdvanceWidth(range, &provider));
1086
1087 gfxFloat pos = writingMode.IsVertical() ? p.y : p.x;
1088 if (pos < 0 || pos >= runAdvance) {
1089 return -1;
1090 }
1091
1092 // Finally, measure progressively smaller portions of the rendered run to
1093 // find which glyph it lies within. This will need to change once we
1094 // support letter-spacing and word-spacing.
1095 bool ir = textRun->IsInlineReversed();
1096 for (int32_t i = mTextFrameContentLength - 1; i >= 0; i--) {
1097 range = ConvertOriginalToSkipped(it, mTextFrameContentOffset, i);
1098 gfxFloat advance = aContext->AppUnitsToGfxUnits(
1099 textRun->GetAdvanceWidth(range, &provider));
1100 if ((ir && pos < runAdvance - advance) || (!ir && pos >= advance)) {
1101 return i;
1102 }
1103 }
1104 return -1;
1105}
1106
1107// ----------------------------------------------------------------------------
1108// TextNodeIterator
1109
1110enum class SubtreePosition { Before, Within, After };
1111
1112/**
1113 * An iterator class for Text that are descendants of a given node, the
1114 * root. Nodes are iterated in document order. An optional subtree can be
1115 * specified, in which case the iterator will track whether the current state of
1116 * the traversal over the tree is within that subtree or is past that subtree.
1117 */
1118class TextNodeIterator {
1119 public:
1120 /**
1121 * Constructs a TextNodeIterator with the specified root node and optional
1122 * subtree.
1123 */
1124 explicit TextNodeIterator(nsIContent* aRoot, nsIContent* aSubtree = nullptr)
1125 : mRoot(aRoot),
1126 mSubtree(aSubtree == aRoot ? nullptr : aSubtree),
1127 mCurrent(aRoot),
1128 mSubtreePosition(mSubtree ? SubtreePosition::Before
1129 : SubtreePosition::Within) {
1130 NS_ASSERTION(aRoot, "expected non-null root")do { if (!(aRoot)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "expected non-null root"
, "aRoot", "./../../../layout/svg/SVGTextFrame.cpp", 1130); MOZ_PretendNoReturn
(); } } while (0)
;
1131 if (!aRoot->IsText()) {
1132 GetNext();
1133 }
1134 }
1135
1136 /**
1137 * Returns the current Text, or null if the iterator has finished.
1138 */
1139 Text* GetCurrent() const { return mCurrent ? mCurrent->AsText() : nullptr; }
1140
1141 /**
1142 * Advances to the next Text and returns it, or null if the end of
1143 * iteration has been reached.
1144 */
1145 Text* GetNext();
1146
1147 /**
1148 * Returns whether the iterator is currently within the subtree rooted
1149 * at mSubtree. Returns true if we are not tracking a subtree (we consider
1150 * that we're always within the subtree).
1151 */
1152 bool IsWithinSubtree() const {
1153 return mSubtreePosition == SubtreePosition::Within;
1154 }
1155
1156 /**
1157 * Returns whether the iterator is past the subtree rooted at mSubtree.
1158 * Returns false if we are not tracking a subtree.
1159 */
1160 bool IsAfterSubtree() const {
1161 return mSubtreePosition == SubtreePosition::After;
1162 }
1163
1164 private:
1165 /**
1166 * The root under which all Text will be iterated over.
1167 */
1168 nsIContent* const mRoot;
1169
1170 /**
1171 * The node rooting the subtree to track.
1172 */
1173 nsIContent* const mSubtree;
1174
1175 /**
1176 * The current node during iteration.
1177 */
1178 nsIContent* mCurrent;
1179
1180 /**
1181 * The current iterator position relative to mSubtree.
1182 */
1183 SubtreePosition mSubtreePosition;
1184};
1185
1186Text* TextNodeIterator::GetNext() {
1187 // Starting from mCurrent, we do a non-recursive traversal to the next
1188 // Text beneath mRoot, updating mSubtreePosition appropriately if we
1189 // encounter mSubtree.
1190 if (mCurrent) {
1191 do {
1192 nsIContent* next =
1193 IsTextContentElement(mCurrent) ? mCurrent->GetFirstChild() : nullptr;
1194 if (next) {
1195 mCurrent = next;
1196 if (mCurrent == mSubtree) {
1197 mSubtreePosition = SubtreePosition::Within;
1198 }
1199 } else {
1200 for (;;) {
1201 if (mCurrent == mRoot) {
1202 mCurrent = nullptr;
1203 break;
1204 }
1205 if (mCurrent == mSubtree) {
1206 mSubtreePosition = SubtreePosition::After;
1207 }
1208 next = mCurrent->GetNextSibling();
1209 if (next) {
1210 mCurrent = next;
1211 if (mCurrent == mSubtree) {
1212 mSubtreePosition = SubtreePosition::Within;
1213 }
1214 break;
1215 }
1216 if (mCurrent == mSubtree) {
1217 mSubtreePosition = SubtreePosition::After;
1218 }
1219 mCurrent = mCurrent->GetParent();
1220 }
1221 }
1222 } while (mCurrent && !mCurrent->IsText());
1223 }
1224
1225 return mCurrent ? mCurrent->AsText() : nullptr;
1226}
1227
1228// ----------------------------------------------------------------------------
1229// TextNodeCorrespondenceRecorder
1230
1231/**
1232 * TextNodeCorrespondence is used as the value of a frame property that
1233 * is stored on all its descendant nsTextFrames. It stores the number of DOM
1234 * characters between it and the previous nsTextFrame that did not have an
1235 * nsTextFrame created for them, due to either not being in a correctly
1236 * parented text content element, or because they were display:none.
1237 * These are called "undisplayed characters".
1238 *
1239 * See also TextNodeCorrespondenceRecorder below, which is what sets the
1240 * frame property.
1241 */
1242struct TextNodeCorrespondence {
1243 explicit TextNodeCorrespondence(uint32_t aUndisplayedCharacters)
1244 : mUndisplayedCharacters(aUndisplayedCharacters) {}
1245
1246 uint32_t mUndisplayedCharacters;
1247};
1248
1249NS_DECLARE_FRAME_PROPERTY_DELETABLE(TextNodeCorrespondenceProperty,static const mozilla::FramePropertyDescriptor<TextNodeCorrespondence
>* TextNodeCorrespondenceProperty() { static const auto descriptor
= mozilla::FramePropertyDescriptor<TextNodeCorrespondence
>::NewWithDestructor<DeleteValue>(); return &descriptor
; }
1250 TextNodeCorrespondence)static const mozilla::FramePropertyDescriptor<TextNodeCorrespondence
>* TextNodeCorrespondenceProperty() { static const auto descriptor
= mozilla::FramePropertyDescriptor<TextNodeCorrespondence
>::NewWithDestructor<DeleteValue>(); return &descriptor
; }
1251
1252/**
1253 * Returns the number of undisplayed characters before the specified
1254 * nsTextFrame.
1255 */
1256static uint32_t GetUndisplayedCharactersBeforeFrame(nsTextFrame* aFrame) {
1257 void* value = aFrame->GetProperty(TextNodeCorrespondenceProperty());
1258 TextNodeCorrespondence* correspondence =
1259 static_cast<TextNodeCorrespondence*>(value);
1260 if (!correspondence) {
1261 // FIXME bug 903785
1262 NS_ERROR(do { NS_DebugBreak(NS_DEBUG_ASSERTION, "expected a TextNodeCorrespondenceProperty on nsTextFrame "
"used for SVG text", "Error", "./../../../layout/svg/SVGTextFrame.cpp"
, 1264); MOZ_PretendNoReturn(); } while (0)
1263 "expected a TextNodeCorrespondenceProperty on nsTextFrame "do { NS_DebugBreak(NS_DEBUG_ASSERTION, "expected a TextNodeCorrespondenceProperty on nsTextFrame "
"used for SVG text", "Error", "./../../../layout/svg/SVGTextFrame.cpp"
, 1264); MOZ_PretendNoReturn(); } while (0)
1264 "used for SVG text")do { NS_DebugBreak(NS_DEBUG_ASSERTION, "expected a TextNodeCorrespondenceProperty on nsTextFrame "
"used for SVG text", "Error", "./../../../layout/svg/SVGTextFrame.cpp"
, 1264); MOZ_PretendNoReturn(); } while (0)
;
1265 return 0;
1266 }
1267 return correspondence->mUndisplayedCharacters;
1268}
1269
1270/**
1271 * Traverses the nsTextFrames for an SVGTextFrame and records a
1272 * TextNodeCorrespondenceProperty on each for the number of undisplayed DOM
1273 * characters between each frame. This is done by iterating simultaneously
1274 * over the Text and nsTextFrames and noting when Text (or
1275 * parts of them) are skipped when finding the next nsTextFrame.
1276 */
1277class TextNodeCorrespondenceRecorder {
1278 public:
1279 /**
1280 * Entry point for the TextNodeCorrespondenceProperty recording.
1281 */
1282 static void RecordCorrespondence(SVGTextFrame* aRoot);
1283
1284 private:
1285 explicit TextNodeCorrespondenceRecorder(SVGTextFrame* aRoot)
1286 : mNodeIterator(aRoot->GetContent()),
1287 mPreviousNode(nullptr),
1288 mNodeCharIndex(0) {}
1289
1290 void Record(SVGTextFrame* aRoot);
1291 void TraverseAndRecord(nsIFrame* aFrame);
1292
1293 /**
1294 * Returns the next non-empty Text.
1295 */
1296 Text* NextNode();
1297
1298 /**
1299 * The iterator over the Text that we use as we simultaneously
1300 * iterate over the nsTextFrames.
1301 */
1302 TextNodeIterator mNodeIterator;
1303
1304 /**
1305 * The previous Text we iterated over.
1306 */
1307 Text* mPreviousNode;
1308
1309 /**
1310 * The index into the current Text's character content.
1311 */
1312 uint32_t mNodeCharIndex;
1313};
1314
1315/* static */
1316void TextNodeCorrespondenceRecorder::RecordCorrespondence(SVGTextFrame* aRoot) {
1317 if (aRoot->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY)) {
1318 // Resolve bidi so that continuation frames are created if necessary:
1319 aRoot->MaybeResolveBidiForAnonymousBlockChild();
1320 TextNodeCorrespondenceRecorder recorder(aRoot);
1321 recorder.Record(aRoot);
1322 aRoot->RemoveStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY);
1323 }
1324}
1325
1326void TextNodeCorrespondenceRecorder::Record(SVGTextFrame* aRoot) {
1327 if (!mNodeIterator.GetCurrent()) {
1328 // If there are no Text nodes then there is nothing to do.
1329 return;
1330 }
1331
1332 // Traverse over all the nsTextFrames and record the number of undisplayed
1333 // characters.
1334 TraverseAndRecord(aRoot);
1335
1336 // Find how many undisplayed characters there are after the final nsTextFrame.
1337 uint32_t undisplayed = 0;
1338 if (mNodeIterator.GetCurrent()) {
1339 if (mPreviousNode && mPreviousNode->TextLength() != mNodeCharIndex) {
1340 // The last nsTextFrame ended part way through a Text node. The
1341 // remaining characters count as undisplayed.
1342 NS_ASSERTION(mNodeCharIndex < mPreviousNode->TextLength(),do { if (!(mNodeCharIndex < mPreviousNode->TextLength()
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "incorrect tracking of undisplayed characters in "
"text nodes", "mNodeCharIndex < mPreviousNode->TextLength()"
, "./../../../layout/svg/SVGTextFrame.cpp", 1344); MOZ_PretendNoReturn
(); } } while (0)
1343 "incorrect tracking of undisplayed characters in "do { if (!(mNodeCharIndex < mPreviousNode->TextLength()
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "incorrect tracking of undisplayed characters in "
"text nodes", "mNodeCharIndex < mPreviousNode->TextLength()"
, "./../../../layout/svg/SVGTextFrame.cpp", 1344); MOZ_PretendNoReturn
(); } } while (0)
1344 "text nodes")do { if (!(mNodeCharIndex < mPreviousNode->TextLength()
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "incorrect tracking of undisplayed characters in "
"text nodes", "mNodeCharIndex < mPreviousNode->TextLength()"
, "./../../../layout/svg/SVGTextFrame.cpp", 1344); MOZ_PretendNoReturn
(); } } while (0)
;
1345 undisplayed += mPreviousNode->TextLength() - mNodeCharIndex;
1346 }
1347 // All the remaining Text that we iterate must also be undisplayed.
1348 for (Text* textNode = mNodeIterator.GetCurrent(); textNode;
1349 textNode = NextNode()) {
1350 undisplayed += textNode->TextLength();
1351 }
1352 }
1353
1354 // Record the trailing number of undisplayed characters on the
1355 // SVGTextFrame.
1356 aRoot->mTrailingUndisplayedCharacters = undisplayed;
1357}
1358
1359Text* TextNodeCorrespondenceRecorder::NextNode() {
1360 mPreviousNode = mNodeIterator.GetCurrent();
1361 Text* next;
1362 do {
1363 next = mNodeIterator.GetNext();
1364 } while (next && next->TextLength() == 0);
1365 return next;
1366}
1367
1368void TextNodeCorrespondenceRecorder::TraverseAndRecord(nsIFrame* aFrame) {
1369 // Recursively iterate over the frame tree, for frames that correspond
1370 // to text content elements.
1371 if (IsTextContentElement(aFrame->GetContent())) {
1372 for (nsIFrame* f : aFrame->PrincipalChildList()) {
1373 TraverseAndRecord(f);
1374 }
1375 return;
1376 }
1377
1378 nsTextFrame* frame; // The current text frame.
1379 Text* node; // The text node for the current text frame.
1380 if (!GetNonEmptyTextFrameAndNode(aFrame, frame, node)) {
1381 // If this isn't an nsTextFrame, or is empty, nothing to do.
1382 return;
1383 }
1384
1385 NS_ASSERTION(frame->GetContentOffset() >= 0,do { if (!(frame->GetContentOffset() >= 0)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "don't know how to handle negative content indexes"
, "frame->GetContentOffset() >= 0", "./../../../layout/svg/SVGTextFrame.cpp"
, 1386); MOZ_PretendNoReturn(); } } while (0)
1386 "don't know how to handle negative content indexes")do { if (!(frame->GetContentOffset() >= 0)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "don't know how to handle negative content indexes"
, "frame->GetContentOffset() >= 0", "./../../../layout/svg/SVGTextFrame.cpp"
, 1386); MOZ_PretendNoReturn(); } } while (0)
;
1387
1388 uint32_t undisplayed = 0;
1389 if (!mPreviousNode) {
1390 // Must be the very first text frame.
1391 NS_ASSERTION(mNodeCharIndex == 0,do { if (!(mNodeCharIndex == 0)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "incorrect tracking of undisplayed " "characters in text nodes"
, "mNodeCharIndex == 0", "./../../../layout/svg/SVGTextFrame.cpp"
, 1393); MOZ_PretendNoReturn(); } } while (0)
1392 "incorrect tracking of undisplayed "do { if (!(mNodeCharIndex == 0)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "incorrect tracking of undisplayed " "characters in text nodes"
, "mNodeCharIndex == 0", "./../../../layout/svg/SVGTextFrame.cpp"
, 1393); MOZ_PretendNoReturn(); } } while (0)
1393 "characters in text nodes")do { if (!(mNodeCharIndex == 0)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "incorrect tracking of undisplayed " "characters in text nodes"
, "mNodeCharIndex == 0", "./../../../layout/svg/SVGTextFrame.cpp"
, 1393); MOZ_PretendNoReturn(); } } while (0)
;
1394 if (!mNodeIterator.GetCurrent()) {
1395 MOZ_ASSERT_UNREACHABLE(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"incorrect tracking of correspondence between " "text frames and text nodes"
")", "./../../../layout/svg/SVGTextFrame.cpp", 1397); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "incorrect tracking of correspondence between "
"text frames and text nodes" ")"); do { MOZ_CrashSequence(__null
, 1397); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
1396 "incorrect tracking of correspondence between "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"incorrect tracking of correspondence between " "text frames and text nodes"
")", "./../../../layout/svg/SVGTextFrame.cpp", 1397); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "incorrect tracking of correspondence between "
"text frames and text nodes" ")"); do { MOZ_CrashSequence(__null
, 1397); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
1397 "text frames and text nodes")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"incorrect tracking of correspondence between " "text frames and text nodes"
")", "./../../../layout/svg/SVGTextFrame.cpp", 1397); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "incorrect tracking of correspondence between "
"text frames and text nodes" ")"); do { MOZ_CrashSequence(__null
, 1397); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
1398 } else {
1399 // Each whole Text we find before we get to the text node for the
1400 // first text frame must be undisplayed.
1401 while (mNodeIterator.GetCurrent() != node) {
1402 undisplayed += mNodeIterator.GetCurrent()->TextLength();
1403 NextNode();
1404 }
1405 // If the first text frame starts at a non-zero content offset, then those
1406 // earlier characters are also undisplayed.
1407 undisplayed += frame->GetContentOffset();
1408 NextNode();
1409 }
1410 } else if (mPreviousNode == node) {
1411 // Same text node as last time.
1412 if (static_cast<uint32_t>(frame->GetContentOffset()) != mNodeCharIndex) {
1413 // We have some characters in the middle of the text node
1414 // that are undisplayed.
1415 NS_ASSERTION(do { if (!(mNodeCharIndex < static_cast<uint32_t>(frame
->GetContentOffset()))) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "incorrect tracking of undisplayed characters in " "text nodes"
, "mNodeCharIndex < static_cast<uint32_t>(frame->GetContentOffset())"
, "./../../../layout/svg/SVGTextFrame.cpp", 1418); MOZ_PretendNoReturn
(); } } while (0)
1416 mNodeCharIndex < static_cast<uint32_t>(frame->GetContentOffset()),do { if (!(mNodeCharIndex < static_cast<uint32_t>(frame
->GetContentOffset()))) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "incorrect tracking of undisplayed characters in " "text nodes"
, "mNodeCharIndex < static_cast<uint32_t>(frame->GetContentOffset())"
, "./../../../layout/svg/SVGTextFrame.cpp", 1418); MOZ_PretendNoReturn
(); } } while (0)
1417 "incorrect tracking of undisplayed characters in "do { if (!(mNodeCharIndex < static_cast<uint32_t>(frame
->GetContentOffset()))) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "incorrect tracking of undisplayed characters in " "text nodes"
, "mNodeCharIndex < static_cast<uint32_t>(frame->GetContentOffset())"
, "./../../../layout/svg/SVGTextFrame.cpp", 1418); MOZ_PretendNoReturn
(); } } while (0)
1418 "text nodes")do { if (!(mNodeCharIndex < static_cast<uint32_t>(frame
->GetContentOffset()))) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "incorrect tracking of undisplayed characters in " "text nodes"
, "mNodeCharIndex < static_cast<uint32_t>(frame->GetContentOffset())"
, "./../../../layout/svg/SVGTextFrame.cpp", 1418); MOZ_PretendNoReturn
(); } } while (0)
;
1419 undisplayed = frame->GetContentOffset() - mNodeCharIndex;
1420 }
1421 } else {
1422 // Different text node from last time.
1423 if (mPreviousNode->TextLength() != mNodeCharIndex) {
1424 NS_ASSERTION(mNodeCharIndex < mPreviousNode->TextLength(),do { if (!(mNodeCharIndex < mPreviousNode->TextLength()
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "incorrect tracking of undisplayed characters in "
"text nodes", "mNodeCharIndex < mPreviousNode->TextLength()"
, "./../../../layout/svg/SVGTextFrame.cpp", 1426); MOZ_PretendNoReturn
(); } } while (0)
1425 "incorrect tracking of undisplayed characters in "do { if (!(mNodeCharIndex < mPreviousNode->TextLength()
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "incorrect tracking of undisplayed characters in "
"text nodes", "mNodeCharIndex < mPreviousNode->TextLength()"
, "./../../../layout/svg/SVGTextFrame.cpp", 1426); MOZ_PretendNoReturn
(); } } while (0)
1426 "text nodes")do { if (!(mNodeCharIndex < mPreviousNode->TextLength()
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "incorrect tracking of undisplayed characters in "
"text nodes", "mNodeCharIndex < mPreviousNode->TextLength()"
, "./../../../layout/svg/SVGTextFrame.cpp", 1426); MOZ_PretendNoReturn
(); } } while (0)
;
1427 // Any trailing characters at the end of the previous Text are
1428 // undisplayed.
1429 undisplayed = mPreviousNode->TextLength() - mNodeCharIndex;
1430 }
1431 // Each whole Text we find before we get to the text node for
1432 // the current text frame must be undisplayed.
1433 while (mNodeIterator.GetCurrent() && mNodeIterator.GetCurrent() != node) {
1434 undisplayed += mNodeIterator.GetCurrent()->TextLength();
1435 NextNode();
1436 }
1437 // If the current text frame starts at a non-zero content offset, then those
1438 // earlier characters are also undisplayed.
1439 undisplayed += frame->GetContentOffset();
1440 NextNode();
1441 }
1442
1443 // Set the frame property.
1444 frame->SetProperty(TextNodeCorrespondenceProperty(),
1445 new TextNodeCorrespondence(undisplayed));
1446
1447 // Remember how far into the current Text we are.
1448 mNodeCharIndex = frame->GetContentEnd();
1449}
1450
1451// ----------------------------------------------------------------------------
1452// TextFrameIterator
1453
1454/**
1455 * An iterator class for nsTextFrames that are descendants of an
1456 * SVGTextFrame. The iterator can optionally track whether the
1457 * current nsTextFrame is for a descendant of, or past, a given subtree
1458 * content node or frame. (This functionality is used for example by the SVG
1459 * DOM text methods to get only the nsTextFrames for a particular <tspan>.)
1460 *
1461 * TextFrameIterator also tracks and exposes other information about the
1462 * current nsTextFrame:
1463 *
1464 * * how many undisplayed characters came just before it
1465 * * its position (in app units) relative to the SVGTextFrame's anonymous
1466 * block frame
1467 * * what nsInlineFrame corresponding to a <textPath> element it is a
1468 * descendant of
1469 * * what computed dominant-baseline value applies to it
1470 *
1471 * Note that any text frames that are empty -- whose ContentLength() is 0 --
1472 * will be skipped over.
1473 */
1474class MOZ_STACK_CLASS TextFrameIterator {
1475 public:
1476 /**
1477 * Constructs a TextFrameIterator for the specified SVGTextFrame
1478 * with an optional frame subtree to restrict iterated text frames to.
1479 */
1480 explicit TextFrameIterator(SVGTextFrame* aRoot,
1481 const nsIFrame* aSubtree = nullptr)
1482 : mRootFrame(aRoot), mCurrentFrame(aRoot) {
1483 Init(aSubtree);
1484 }
1485
1486 /**
1487 * Constructs a TextFrameIterator for the specified SVGTextFrame
1488 * with an optional frame content subtree to restrict iterated text frames to.
1489 */
1490 TextFrameIterator(SVGTextFrame* aRoot, const nsIContent* aSubtree)
1491 : mRootFrame(aRoot), mCurrentFrame(aRoot) {
1492 Init(aRoot && aSubtree && aSubtree != aRoot->GetContent()
1493 ? aSubtree->GetPrimaryFrame()
1494 : nullptr);
1495 }
1496
1497 /**
1498 * Returns the root SVGTextFrame this TextFrameIterator is iterating over.
1499 * (May be nullptr, if nullptr was passed to our constructor.)
1500 */
1501 SVGTextFrame* GetRoot() const { return mRootFrame; }
1502
1503 /**
1504 * Returns the current nsTextFrame, or null if the iterator has finished.
1505 */
1506 nsTextFrame* GetCurrent() const { return do_QueryFrame(mCurrentFrame); }
1507
1508 /**
1509 * Returns the number of undisplayed characters in the DOM just before the
1510 * current frame.
1511 */
1512 uint32_t UndisplayedCharacters() const;
1513
1514 /**
1515 * Returns the current frame's position, in app units, relative to the
1516 * root SVGTextFrame's anonymous block frame.
1517 */
1518 nsPoint Position() const { return mCurrentPosition; }
1519
1520 /**
1521 * Advances to the next nsTextFrame and returns it, or null if the end of
1522 * iteration has been reached.
1523 */
1524 nsTextFrame* GetNext();
1525
1526 /**
1527 * Returns whether the iterator is within the subtree.
1528 */
1529 bool IsWithinSubtree() const {
1530 return mSubtreePosition == SubtreePosition::Within;
1531 }
1532
1533 /**
1534 * Returns whether the iterator is past the subtree.
1535 */
1536 bool IsAfterSubtree() const {
1537 return mSubtreePosition == SubtreePosition::After;
1538 }
1539
1540 /**
1541 * Returns the frame corresponding to the <textPath> element, if we
1542 * are inside one.
1543 */
1544 nsIFrame* TextPathFrame() const {
1545 return mTextPathFrames.IsEmpty() ? nullptr : mTextPathFrames.LastElement();
1546 }
1547
1548 /**
1549 * Returns the current frame's computed dominant-baseline value.
1550 */
1551 StyleDominantBaseline DominantBaseline() const {
1552 return mBaselines.LastElement();
1553 }
1554
1555 /**
1556 * Finishes the iterator.
1557 */
1558 void Close() { mCurrentFrame = nullptr; }
1559
1560 private:
1561 /**
1562 * Initializes the iterator and advances to the first item.
1563 */
1564 void Init(const nsIFrame* aSubtree) {
1565 for (const nsIFrame* f = aSubtree; f; f = f->GetNextContinuation()) {
1566 mSubtreeRoot.Insert(f);
1567 }
1568 mSubtreePosition =
1569 aSubtree ? SubtreePosition::Before : SubtreePosition::Within;
1570 if (!mRootFrame) {
1571 return;
1572 }
1573
1574 mBaselines.AppendElement(mRootFrame->StyleVisibility()->mDominantBaseline);
1575 GetNext();
1576 }
1577
1578 /**
1579 * Pushes the specified frame's computed dominant-baseline value.
1580 * If the value of the property is "auto", then the parent frame's
1581 * computed value is used.
1582 */
1583 void PushBaseline(nsIFrame* aNextFrame);
1584
1585 /**
1586 * Pops the current dominant-baseline off the stack.
1587 */
1588 void PopBaseline();
1589
1590 /**
1591 * The root frame we are iterating through.
1592 */
1593 SVGTextFrame* const mRootFrame;
1594
1595 /**
1596 * The root frame of any subtree we are interested in tracking
1597 * and its following continuations, if any.
1598 */
1599 nsTHashSet<const nsIFrame*> mSubtreeRoot;
1600
1601 /**
1602 * The current value of the iterator.
1603 */
1604 nsIFrame* mCurrentFrame;
1605
1606 /**
1607 * The position, in app units, of the current frame relative to mRootFrame.
1608 */
1609 nsPoint mCurrentPosition;
1610
1611 /**
1612 * Stack of frames corresponding to <textPath> elements that are in scope
1613 * for the current frame.
1614 */
1615 AutoTArray<nsIFrame*, 1> mTextPathFrames;
1616
1617 /**
1618 * Stack of dominant-baseline values to record as we traverse through the
1619 * frame tree.
1620 */
1621 AutoTArray<StyleDominantBaseline, 8> mBaselines;
1622
1623 /**
1624 * The iterator's current position relative to the subtree.
1625 */
1626 SubtreePosition mSubtreePosition;
1627};
1628
1629uint32_t TextFrameIterator::UndisplayedCharacters() const {
1630 MOZ_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY)"
" (" "Text correspondence must be up to date" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 1632); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY)"
") (" "Text correspondence must be up to date" ")"); do { MOZ_CrashSequence
(__null, 1632); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
1631 !mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY)"
" (" "Text correspondence must be up to date" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 1632); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY)"
") (" "Text correspondence must be up to date" ")"); do { MOZ_CrashSequence
(__null, 1632); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
1632 "Text correspondence must be up to date")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY)"
" (" "Text correspondence must be up to date" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 1632); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mRootFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY)"
") (" "Text correspondence must be up to date" ")"); do { MOZ_CrashSequence
(__null, 1632); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1633
1634 if (!mCurrentFrame) {
1635 return mRootFrame->mTrailingUndisplayedCharacters;
1636 }
1637
1638 nsTextFrame* frame = do_QueryFrame(mCurrentFrame);
1639 return GetUndisplayedCharactersBeforeFrame(frame);
1640}
1641
1642nsTextFrame* TextFrameIterator::GetNext() {
1643 // Starting from mCurrentFrame, we do a non-recursive traversal to the next
1644 // nsTextFrame beneath mRoot, updating mSubtreePosition appropriately if we
1645 // encounter mSubtreeRoot.
1646 if (mCurrentFrame) {
1647 do {
1648 nsIFrame* next = IsTextContentElement(mCurrentFrame->GetContent())
1649 ? mCurrentFrame->PrincipalChildList().FirstChild()
1650 : nullptr;
1651 if (next) {
1652 // Descend into this frame, and accumulate its position.
1653 mCurrentPosition += next->GetPosition();
1654 if (next->GetContent()->IsSVGElement(nsGkAtoms::textPath)) {
1655 // Record this <textPath> frame.
1656 mTextPathFrames.AppendElement(next);
1657 }
1658 // Record the frame's baseline.
1659 PushBaseline(next);
1660 mCurrentFrame = next;
1661 if (mSubtreeRoot.Contains(mCurrentFrame)) {
1662 // If the current frame within mSubtreeRoot, we have now moved into
1663 // it.
1664 mSubtreePosition = SubtreePosition::Within;
1665 }
1666 } else {
1667 for (;;) {
1668 // We want to move past the current frame.
1669 if (mCurrentFrame == mRootFrame) {
1670 // If we've reached the root frame, we're finished.
1671 mCurrentFrame = nullptr;
1672 break;
1673 }
1674 // Remove the current frame's position.
1675 mCurrentPosition -= mCurrentFrame->GetPosition();
1676 if (mCurrentFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath)) {
1677 // Pop off the <textPath> frame if this is a <textPath>.
1678 mTextPathFrames.RemoveLastElement();
1679 }
1680 // Pop off the current baseline.
1681 PopBaseline();
1682 if (mSubtreeRoot.Contains(mCurrentFrame)) {
1683 // If this was within mSubtreeRoot, we have now moved past it.
1684 mSubtreePosition = SubtreePosition::After;
1685 }
1686 next = mCurrentFrame->GetNextSibling();
1687 if (next) {
1688 // Moving to the next sibling.
1689 mCurrentPosition += next->GetPosition();
1690 if (next->GetContent()->IsSVGElement(nsGkAtoms::textPath)) {
1691 // Record this <textPath> frame.
1692 mTextPathFrames.AppendElement(next);
1693 }
1694 // Record the frame's baseline.
1695 PushBaseline(next);
1696 mCurrentFrame = next;
1697 if (mSubtreeRoot.Contains(mCurrentFrame)) {
1698 // If the current frame is within mSubtreeRoot, we have now moved
1699 // into it.
1700 mSubtreePosition = SubtreePosition::Within;
1701 }
1702 break;
1703 }
1704 if (mSubtreeRoot.Contains(mCurrentFrame)) {
1705 // If there is no next sibling frame, and the current frame is
1706 // within mSubtreeRoot, we have now moved past it.
1707 mSubtreePosition = SubtreePosition::After;
1708 }
1709 // Ascend out of this frame.
1710 mCurrentFrame = mCurrentFrame->GetParent();
1711 }
1712 }
1713 } while (mCurrentFrame && !IsNonEmptyTextFrame(mCurrentFrame));
1714 }
1715
1716 return GetCurrent();
1717}
1718
1719void TextFrameIterator::PushBaseline(nsIFrame* aNextFrame) {
1720 StyleDominantBaseline baseline =
1721 aNextFrame->StyleVisibility()->mDominantBaseline;
1722 mBaselines.AppendElement(baseline);
1723}
1724
1725void TextFrameIterator::PopBaseline() {
1726 NS_ASSERTION(!mBaselines.IsEmpty(), "popped too many baselines")do { if (!(!mBaselines.IsEmpty())) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "popped too many baselines", "!mBaselines.IsEmpty()", "./../../../layout/svg/SVGTextFrame.cpp"
, 1726); MOZ_PretendNoReturn(); } } while (0)
;
1727 mBaselines.RemoveLastElement();
1728}
1729
1730// -----------------------------------------------------------------------------
1731// TextRenderedRunIterator
1732
1733/**
1734 * Iterator for TextRenderedRun objects for the SVGTextFrame.
1735 */
1736class TextRenderedRunIterator {
1737 public:
1738 /**
1739 * Values for the aFilter argument of the constructor, to indicate which
1740 * frames we should be limited to iterating TextRenderedRun objects for.
1741 */
1742 enum class RenderedRunFilter {
1743 // Iterate TextRenderedRuns for all nsTextFrames.
1744 AllFrames,
1745 // Iterate only TextRenderedRuns for nsTextFrames that are
1746 // visibility:visible.
1747 VisibleFrames
1748 };
1749
1750 /**
1751 * Constructs a TextRenderedRunIterator with an optional frame subtree to
1752 * restrict iterated rendered runs to.
1753 *
1754 * @param aSVGTextFrame The SVGTextFrame whose rendered runs to iterate
1755 * through.
1756 * @param aFilter Indicates whether to iterate rendered runs for non-visible
1757 * nsTextFrames.
1758 * @param aSubtree An optional frame subtree to restrict iterated rendered
1759 * runs to.
1760 */
1761 explicit TextRenderedRunIterator(
1762 SVGTextFrame* aSVGTextFrame,
1763 RenderedRunFilter aFilter = RenderedRunFilter::AllFrames,
1764 const nsIFrame* aSubtree = nullptr)
1765 : mFrameIterator(FrameIfAnonymousChildReflowed(aSVGTextFrame), aSubtree),
1766 mFilter(aFilter),
1767 mTextElementCharIndex(0),
1768 mFrameStartTextElementCharIndex(0),
1769 mFontSizeScaleFactor(aSVGTextFrame->mFontSizeScaleFactor),
1770 mCurrent(First()) {}
1771
1772 /**
1773 * Constructs a TextRenderedRunIterator with a content subtree to restrict
1774 * iterated rendered runs to.
1775 *
1776 * @param aSVGTextFrame The SVGTextFrame whose rendered runs to iterate
1777 * through.
1778 * @param aFilter Indicates whether to iterate rendered runs for non-visible
1779 * nsTextFrames.
1780 * @param aSubtree A content subtree to restrict iterated rendered runs to.
1781 */
1782 TextRenderedRunIterator(SVGTextFrame* aSVGTextFrame,
1783 RenderedRunFilter aFilter, nsIContent* aSubtree)
1784 : mFrameIterator(FrameIfAnonymousChildReflowed(aSVGTextFrame), aSubtree),
1785 mFilter(aFilter),
1786 mTextElementCharIndex(0),
1787 mFrameStartTextElementCharIndex(0),
1788 mFontSizeScaleFactor(aSVGTextFrame->mFontSizeScaleFactor),
1789 mCurrent(First()) {}
1790
1791 /**
1792 * Ensure any cached PropertyProvider is cleared at the end of the iteration.
1793 */
1794 ~TextRenderedRunIterator() {
1795 if (auto* root = mFrameIterator.GetRoot()) {
1796 root->ForgetCachedProvider();
1797 }
1798 }
1799
1800 /**
1801 * Returns the current TextRenderedRun.
1802 */
1803 TextRenderedRun Current() const { return mCurrent; }
1804
1805 /**
1806 * Advances to the next TextRenderedRun and returns it.
1807 */
1808 TextRenderedRun Next();
1809
1810 private:
1811 /**
1812 * Returns the root SVGTextFrame this iterator is for.
1813 */
1814 SVGTextFrame* GetRoot() const { return mFrameIterator.GetRoot(); }
1815
1816 /**
1817 * Advances to the first TextRenderedRun and returns it.
1818 */
1819 TextRenderedRun First();
1820
1821 /**
1822 * The frame iterator to use.
1823 */
1824 TextFrameIterator mFrameIterator;
1825
1826 /**
1827 * The filter indicating which TextRenderedRuns to return.
1828 */
1829 RenderedRunFilter mFilter;
1830
1831 /**
1832 * The character index across the entire <text> element we are currently
1833 * up to.
1834 */
1835 uint32_t mTextElementCharIndex;
1836
1837 /**
1838 * The character index across the entire <text> for the start of the current
1839 * frame.
1840 */
1841 uint32_t mFrameStartTextElementCharIndex;
1842
1843 /**
1844 * The font-size scale factor we used when constructing the nsTextFrames.
1845 */
1846 double mFontSizeScaleFactor;
1847
1848 /**
1849 * The current TextRenderedRun.
1850 */
1851 TextRenderedRun mCurrent;
1852};
1853
1854TextRenderedRun TextRenderedRunIterator::Next() {
1855 if (!mFrameIterator.GetCurrent()) {
1856 // If there are no more frames, then there are no more rendered runs to
1857 // return.
1858 mCurrent = TextRenderedRun();
1859 return mCurrent;
1860 }
1861
1862 // The values we will use to initialize the TextRenderedRun with.
1863 nsTextFrame* frame;
1864 gfxPoint pt;
1865 double rotate;
1866 nscoord baseline;
1867 uint32_t offset, length;
1868 uint32_t charIndex;
1869
1870 // We loop, because we want to skip over rendered runs that either aren't
1871 // within our subtree of interest, because they don't match the filter,
1872 // or because they are hidden due to having fallen off the end of a
1873 // <textPath>.
1874 for (;;) {
1875 if (mFrameIterator.IsAfterSubtree()) {
1876 mCurrent = TextRenderedRun();
1877 return mCurrent;
1878 }
1879
1880 frame = mFrameIterator.GetCurrent();
1881
1882 charIndex = mTextElementCharIndex;
1883
1884 // Find the end of the rendered run, by looking through the
1885 // SVGTextFrame's positions array until we find one that is recorded
1886 // as a run boundary.
1887 uint32_t runStart,
1888 runEnd; // XXX Replace runStart with mTextElementCharIndex.
1889 runStart = mTextElementCharIndex;
1890 runEnd = runStart + 1;
1891 while (runEnd < GetRoot()->mPositions.Length() &&
1892 !GetRoot()->mPositions[runEnd].mRunBoundary) {
1893 runEnd++;
1894 }
1895
1896 // Convert the global run start/end indexes into an offset/length into the
1897 // current frame's Text.
1898 offset =
1899 frame->GetContentOffset() + runStart - mFrameStartTextElementCharIndex;
1900 length = runEnd - runStart;
1901
1902 // If the end of the frame's content comes before the run boundary we found
1903 // in SVGTextFrame's position array, we need to shorten the rendered run.
1904 uint32_t contentEnd = frame->GetContentEnd();
1905 if (offset + length > contentEnd) {
1906 length = contentEnd - offset;
1907 }
1908
1909 NS_ASSERTION(offset >= uint32_t(frame->GetContentOffset()),do { if (!(offset >= uint32_t(frame->GetContentOffset()
))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "invalid offset", "offset >= uint32_t(frame->GetContentOffset())"
, "./../../../layout/svg/SVGTextFrame.cpp", 1910); MOZ_PretendNoReturn
(); } } while (0)
1910 "invalid offset")do { if (!(offset >= uint32_t(frame->GetContentOffset()
))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "invalid offset", "offset >= uint32_t(frame->GetContentOffset())"
, "./../../../layout/svg/SVGTextFrame.cpp", 1910); MOZ_PretendNoReturn
(); } } while (0)
;
1911 NS_ASSERTION(offset + length <= contentEnd, "invalid offset or length")do { if (!(offset + length <= contentEnd)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "invalid offset or length", "offset + length <= contentEnd"
, "./../../../layout/svg/SVGTextFrame.cpp", 1911); MOZ_PretendNoReturn
(); } } while (0)
;
1912
1913 // Get the frame's baseline position.
1914 frame->EnsureTextRun(nsTextFrame::eInflated);
1915 baseline = GetBaselinePosition(
1916 frame, frame->GetTextRun(nsTextFrame::eInflated),
1917 mFrameIterator.DominantBaseline(), mFontSizeScaleFactor);
1918
1919 // Trim the offset/length to remove any leading/trailing white space.
1920 uint32_t untrimmedOffset = offset;
1921 uint32_t untrimmedLength = length;
1922 nsTextFrame::TrimmedOffsets trimmedOffsets =
1923 frame->GetTrimmedOffsets(frame->CharacterDataBuffer());
1924 TrimOffsets(offset, length, trimmedOffsets);
1925 charIndex += offset - untrimmedOffset;
1926
1927 // Get the position and rotation of the character that begins this
1928 // rendered run.
1929 pt = GetRoot()->mPositions[charIndex].mPosition;
1930 rotate = GetRoot()->mPositions[charIndex].mAngle;
1931
1932 // Determine if we should skip this rendered run.
1933 bool skip = !mFrameIterator.IsWithinSubtree() ||
1934 GetRoot()->mPositions[mTextElementCharIndex].mHidden;
1935 if (mFilter == RenderedRunFilter::VisibleFrames) {
1936 skip = skip || !frame->StyleVisibility()->IsVisible();
1937 }
1938
1939 // Update our global character index to move past the characters
1940 // corresponding to this rendered run.
1941 mTextElementCharIndex += untrimmedLength;
1942
1943 // If we have moved past the end of the current frame's content, we need to
1944 // advance to the next frame.
1945 if (offset + untrimmedLength >= contentEnd) {
1946 mFrameIterator.GetNext();
1947 mTextElementCharIndex += mFrameIterator.UndisplayedCharacters();
1948 mFrameStartTextElementCharIndex = mTextElementCharIndex;
1949 }
1950
1951 if (!mFrameIterator.GetCurrent()) {
1952 if (skip) {
1953 // That was the last frame, and we skipped this rendered run. So we
1954 // have no rendered run to return.
1955 mCurrent = TextRenderedRun();
1956 return mCurrent;
1957 }
1958 break;
1959 }
1960
1961 if (length && !skip) {
1962 // Only return a rendered run if it didn't get collapsed away entirely
1963 // (due to it being all white space) and if we don't want to skip it.
1964 break;
1965 }
1966 }
1967
1968 mCurrent = TextRenderedRun(frame, GetRoot(), pt, rotate, mFontSizeScaleFactor,
1969 baseline, offset, length, charIndex);
1970 return mCurrent;
1971}
1972
1973TextRenderedRun TextRenderedRunIterator::First() {
1974 if (!mFrameIterator.GetCurrent()) {
1975 return TextRenderedRun();
1976 }
1977
1978 if (GetRoot()->mPositions.IsEmpty()) {
1979 mFrameIterator.Close();
1980 return TextRenderedRun();
1981 }
1982
1983 // Get the character index for the start of this rendered run, by skipping
1984 // any undisplayed characters.
1985 mTextElementCharIndex = mFrameIterator.UndisplayedCharacters();
1986 mFrameStartTextElementCharIndex = mTextElementCharIndex;
1987
1988 return Next();
1989}
1990
1991// -----------------------------------------------------------------------------
1992// CharIterator
1993
1994/**
1995 * Iterator for characters within an SVGTextFrame.
1996 */
1997class MOZ_STACK_CLASS CharIterator {
1998 using Range = gfxTextRun::Range;
1999
2000 public:
2001 /**
2002 * Values for the aFilter argument of the constructor, to indicate which
2003 * characters we should be iterating over.
2004 */
2005 enum class CharacterFilter {
2006 // Iterate over all original characters from the DOM that are within valid
2007 // text content elements.
2008 Original,
2009 // Iterate only over characters that are not skipped characters.
2010 Unskipped,
2011 // Iterate only over characters that are addressable by the positioning
2012 // attributes x="", y="", etc. This includes all characters after
2013 // collapsing white space as required by the value of 'white-space'.
2014 Addressable,
2015 };
2016
2017 /**
2018 * Constructs a CharIterator.
2019 *
2020 * @param aSVGTextFrame The SVGTextFrame whose characters to iterate
2021 * through.
2022 * @param aFilter Indicates which characters to iterate over.
2023 * @param aSubtree A content subtree to track whether the current character
2024 * is within.
2025 */
2026 CharIterator(SVGTextFrame* aSVGTextFrame, CharacterFilter aFilter,
2027 nsIContent* aSubtree, bool aPostReflow = true);
2028
2029 /**
2030 * Ensure any cached PropertyProvider is cleared at the end of the iteration.
2031 */
2032 ~CharIterator() {
2033 if (auto* root = mFrameIterator.GetRoot()) {
2034 root->ForgetCachedProvider();
2035 }
2036 }
2037
2038 /**
2039 * Returns whether the iterator is finished.
2040 */
2041 bool AtEnd() const { return !mFrameIterator.GetCurrent(); }
2042
2043 /**
2044 * Advances to the next matching character. Returns true if there was a
2045 * character to advance to, and false otherwise.
2046 */
2047 bool Next();
2048
2049 /**
2050 * Advances ahead aCount matching characters. Returns true if there were
2051 * enough characters to advance past, and false otherwise.
2052 */
2053 bool Next(uint32_t aCount);
2054
2055 /**
2056 * Advances ahead up to aCount matching characters.
2057 */
2058 void NextWithinSubtree(uint32_t aCount);
2059
2060 /**
2061 * Advances to the character with the specified index. The index is in the
2062 * space of original characters (i.e., all DOM characters under the <text>
2063 * that are within valid text content elements).
2064 */
2065 bool AdvanceToCharacter(uint32_t aTextElementCharIndex);
2066
2067 /**
2068 * Advances to the first matching character after the current nsTextFrame.
2069 */
2070 bool AdvancePastCurrentFrame();
2071
2072 /**
2073 * Advances to the first matching character after the frames within
2074 * the current <textPath>.
2075 */
2076 bool AdvancePastCurrentTextPathFrame();
2077
2078 /**
2079 * Advances to the first matching character of the subtree. Returns true
2080 * if we successfully advance to the subtree, or if we are already within
2081 * the subtree. Returns false if we are past the subtree.
2082 */
2083 bool AdvanceToSubtree();
2084
2085 /**
2086 * Returns the nsTextFrame for the current character, or null if the end of
2087 * iteration has been reached.
2088 */
2089 nsTextFrame* GetTextFrame() const { return mFrameIterator.GetCurrent(); }
2090
2091 /**
2092 * Returns whether the iterator is within the subtree.
2093 */
2094 bool IsWithinSubtree() const { return mFrameIterator.IsWithinSubtree(); }
2095
2096 /**
2097 * Returns whether the iterator is past the subtree.
2098 */
2099 bool IsAfterSubtree() const { return mFrameIterator.IsAfterSubtree(); }
2100
2101 /**
2102 * Returns the iterator's computed dominant-baseline value.
2103 */
2104 StyleDominantBaseline DominantBaseline() const {
2105 return mFrameIterator.DominantBaseline();
2106 }
2107
2108 /**
2109 * Returns whether the current character is a skipped character.
2110 */
2111 bool IsOriginalCharSkipped() const {
2112 return mSkipCharsIterator.IsOriginalCharSkipped();
2113 }
2114
2115 /**
2116 * Returns whether the current character is the start of a cluster and
2117 * ligature group.
2118 */
2119 bool IsClusterAndLigatureGroupStart() const {
2120 return mTextRun->IsLigatureGroupStart(
2121 mSkipCharsIterator.GetSkippedOffset()) &&
2122 mTextRun->IsClusterStart(mSkipCharsIterator.GetSkippedOffset());
2123 }
2124
2125 /**
2126 * Returns the glyph run for the current character.
2127 */
2128 const gfxTextRun::GlyphRun& GlyphRun() const {
2129 return *mTextRun->FindFirstGlyphRunContaining(
2130 mSkipCharsIterator.GetSkippedOffset());
2131 }
2132
2133 /**
2134 * Returns whether the current character is trimmed away when painting,
2135 * due to it being leading/trailing white space.
2136 */
2137 bool IsOriginalCharTrimmed() const;
2138
2139 /**
2140 * Returns whether the current character is unaddressable from the SVG glyph
2141 * positioning attributes.
2142 */
2143 bool IsOriginalCharUnaddressable() const {
2144 return IsOriginalCharSkipped() || IsOriginalCharTrimmed();
2145 }
2146
2147 /**
2148 * Returns the text run for the current character.
2149 */
2150 gfxTextRun* TextRun() const { return mTextRun; }
2151
2152 /**
2153 * Returns the current character index.
2154 */
2155 uint32_t TextElementCharIndex() const { return mTextElementCharIndex; }
2156
2157 /**
2158 * Returns the character index for the start of the cluster/ligature group it
2159 * is part of.
2160 */
2161 uint32_t GlyphStartTextElementCharIndex() const {
2162 return mGlyphStartTextElementCharIndex;
2163 }
2164
2165 /**
2166 * Gets the advance, in user units, of the current character. If the
2167 * character is a part of ligature, then the advance returned will be
2168 * a fraction of the ligature glyph's advance.
2169 *
2170 * @param aContext The context to use for unit conversions.
2171 */
2172 gfxFloat GetAdvance(nsPresContext* aContext) const;
2173
2174 /**
2175 * Returns the frame corresponding to the <textPath> that the current
2176 * character is within.
2177 */
2178 nsIFrame* TextPathFrame() const { return mFrameIterator.TextPathFrame(); }
2179
2180#ifdef DEBUG1
2181 /**
2182 * Returns the subtree we were constructed with.
2183 */
2184 nsIContent* GetSubtree() const { return mSubtree; }
2185
2186 /**
2187 * Returns the CharacterFilter mode in use.
2188 */
2189 CharacterFilter Filter() const { return mFilter; }
2190#endif
2191
2192 private:
2193 /**
2194 * Advances to the next character without checking it against the filter.
2195 * Returns true if there was a next character to advance to, or false
2196 * otherwise.
2197 */
2198 bool NextCharacter();
2199
2200 /**
2201 * Returns whether the current character matches the filter.
2202 */
2203 bool MatchesFilter() const;
2204
2205 /**
2206 * If this is the start of a glyph, record it.
2207 */
2208 void UpdateGlyphStartTextElementCharIndex() {
2209 if (!IsOriginalCharSkipped() && IsClusterAndLigatureGroupStart()) {
2210 mGlyphStartTextElementCharIndex = mTextElementCharIndex;
2211 }
2212 }
2213
2214 /**
2215 * The filter to use.
2216 */
2217 CharacterFilter mFilter;
2218
2219 /**
2220 * The iterator for text frames.
2221 */
2222 TextFrameIterator mFrameIterator;
2223
2224#ifdef DEBUG1
2225 /**
2226 * The subtree we were constructed with.
2227 */
2228 nsIContent* const mSubtree;
2229#endif
2230
2231 /**
2232 * A gfxSkipCharsIterator for the text frame the current character is
2233 * a part of.
2234 */
2235 gfxSkipCharsIterator mSkipCharsIterator;
2236
2237 // Cache for information computed by IsOriginalCharTrimmed.
2238 mutable nsTextFrame* mFrameForTrimCheck;
2239 mutable uint32_t mTrimmedOffset;
2240 mutable uint32_t mTrimmedLength;
2241
2242 /**
2243 * The text run the current character is a part of.
2244 */
2245 gfxTextRun* mTextRun;
2246
2247 /**
2248 * The current character's index.
2249 */
2250 uint32_t mTextElementCharIndex;
2251
2252 /**
2253 * The index of the character that starts the cluster/ligature group the
2254 * current character is a part of.
2255 */
2256 uint32_t mGlyphStartTextElementCharIndex;
2257
2258 /**
2259 * The scale factor to apply to glyph advances returned by
2260 * GetAdvance etc. to take into account textLength="".
2261 */
2262 float mLengthAdjustScaleFactor;
2263
2264 /**
2265 * Whether the instance of this class is being used after reflow has occurred
2266 * or not.
2267 */
2268 bool mPostReflow;
2269};
2270
2271CharIterator::CharIterator(SVGTextFrame* aSVGTextFrame,
2272 CharIterator::CharacterFilter aFilter,
2273 nsIContent* aSubtree, bool aPostReflow)
2274 : mFilter(aFilter),
2275 mFrameIterator(aSVGTextFrame, aSubtree),
2276#ifdef DEBUG1
2277 mSubtree(aSubtree),
2278#endif
2279 mFrameForTrimCheck(nullptr),
2280 mTrimmedOffset(0),
2281 mTrimmedLength(0),
2282 mTextRun(nullptr),
2283 mTextElementCharIndex(0),
2284 mGlyphStartTextElementCharIndex(0),
2285 mLengthAdjustScaleFactor(aSVGTextFrame->mLengthAdjustScaleFactor),
2286 mPostReflow(aPostReflow) {
2287 if (!AtEnd()) {
2288 mSkipCharsIterator = GetTextFrame()->EnsureTextRun(nsTextFrame::eInflated);
2289 mTextRun = GetTextFrame()->GetTextRun(nsTextFrame::eInflated);
2290 mTextElementCharIndex = mFrameIterator.UndisplayedCharacters();
2291 UpdateGlyphStartTextElementCharIndex();
2292 if (!MatchesFilter()) {
2293 Next();
2294 }
2295 }
2296}
2297
2298bool CharIterator::Next() {
2299 while (NextCharacter()) {
2300 if (MatchesFilter()) {
2301 return true;
2302 }
2303 }
2304 return false;
2305}
2306
2307bool CharIterator::Next(uint32_t aCount) {
2308 if (aCount == 0 && AtEnd()) {
2309 return false;
2310 }
2311 while (aCount) {
2312 if (!Next()) {
2313 return false;
2314 }
2315 aCount--;
2316 }
2317 return true;
2318}
2319
2320void CharIterator::NextWithinSubtree(uint32_t aCount) {
2321 while (IsWithinSubtree() && aCount) {
2322 --aCount;
2323 if (!Next()) {
2324 return;
2325 }
2326 }
2327}
2328
2329bool CharIterator::AdvanceToCharacter(uint32_t aTextElementCharIndex) {
2330 while (mTextElementCharIndex < aTextElementCharIndex) {
2331 if (!Next()) {
2332 return false;
2333 }
2334 }
2335 return true;
2336}
2337
2338bool CharIterator::AdvancePastCurrentFrame() {
2339 // XXX Can do this better than one character at a time if it matters.
2340 nsTextFrame* currentFrame = GetTextFrame();
2341 do {
2342 if (!Next()) {
2343 return false;
2344 }
2345 } while (GetTextFrame() == currentFrame);
2346 return true;
2347}
2348
2349bool CharIterator::AdvancePastCurrentTextPathFrame() {
2350 nsIFrame* currentTextPathFrame = TextPathFrame();
2351 NS_ASSERTION(currentTextPathFrame,do { if (!(currentTextPathFrame)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "expected AdvancePastCurrentTextPathFrame to be called only "
"within a text path frame", "currentTextPathFrame", "./../../../layout/svg/SVGTextFrame.cpp"
, 2353); MOZ_PretendNoReturn(); } } while (0)
2352 "expected AdvancePastCurrentTextPathFrame to be called only "do { if (!(currentTextPathFrame)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "expected AdvancePastCurrentTextPathFrame to be called only "
"within a text path frame", "currentTextPathFrame", "./../../../layout/svg/SVGTextFrame.cpp"
, 2353); MOZ_PretendNoReturn(); } } while (0)
2353 "within a text path frame")do { if (!(currentTextPathFrame)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "expected AdvancePastCurrentTextPathFrame to be called only "
"within a text path frame", "currentTextPathFrame", "./../../../layout/svg/SVGTextFrame.cpp"
, 2353); MOZ_PretendNoReturn(); } } while (0)
;
2354 do {
2355 if (!AdvancePastCurrentFrame()) {
2356 return false;
2357 }
2358 } while (TextPathFrame() == currentTextPathFrame);
2359 return true;
2360}
2361
2362bool CharIterator::AdvanceToSubtree() {
2363 while (!IsWithinSubtree()) {
2364 if (IsAfterSubtree()) {
2365 return false;
2366 }
2367 if (!AdvancePastCurrentFrame()) {
2368 return false;
2369 }
2370 }
2371 return true;
2372}
2373
2374bool CharIterator::IsOriginalCharTrimmed() const {
2375 if (mFrameForTrimCheck != GetTextFrame()) {
2376 // Since we do a lot of trim checking, we cache the trimmed offsets and
2377 // lengths while we are in the same frame.
2378 mFrameForTrimCheck = GetTextFrame();
2379 uint32_t offset = mFrameForTrimCheck->GetContentOffset();
2380 uint32_t length = mFrameForTrimCheck->GetContentLength();
2381 nsTextFrame::TrimmedOffsets trim = mFrameForTrimCheck->GetTrimmedOffsets(
2382 mFrameForTrimCheck->CharacterDataBuffer(),
2383 (mPostReflow ? nsTextFrame::TrimmedOffsetFlags::Default
2384 : nsTextFrame::TrimmedOffsetFlags::NotPostReflow));
2385 TrimOffsets(offset, length, trim);
2386 mTrimmedOffset = offset;
2387 mTrimmedLength = length;
2388 }
2389
2390 // A character is trimmed if it is outside the mTrimmedOffset/mTrimmedLength
2391 // range and it is not a significant newline character.
2392 uint32_t index = mSkipCharsIterator.GetOriginalOffset();
2393 return !(
2394 (index >= mTrimmedOffset && index < mTrimmedOffset + mTrimmedLength) ||
2395 (index >= mTrimmedOffset + mTrimmedLength &&
2396 mFrameForTrimCheck->StyleText()->NewlineIsSignificant(
2397 mFrameForTrimCheck) &&
2398 mFrameForTrimCheck->CharacterDataBuffer().CharAt(index) == '\n'));
2399}
2400
2401gfxFloat CharIterator::GetAdvance(nsPresContext* aContext) const {
2402 float cssPxPerDevPx =
2403 nsPresContext::AppUnitsToFloatCSSPixels(aContext->AppUnitsPerDevPixel());
2404
2405 auto& provider =
2406 mFrameIterator.GetRoot()->PropertyProviderFor(GetTextFrame());
2407 uint32_t offset = mSkipCharsIterator.GetSkippedOffset();
2408 gfxFloat advance =
2409 mTextRun->GetAdvanceWidth(Range(offset, offset + 1), &provider);
2410 return aContext->AppUnitsToGfxUnits(advance) * mLengthAdjustScaleFactor *
2411 cssPxPerDevPx;
2412}
2413
2414bool CharIterator::NextCharacter() {
2415 if (AtEnd()) {
2416 return false;
2417 }
2418
2419 mTextElementCharIndex++;
2420
2421 // Advance within the current text run.
2422 mSkipCharsIterator.AdvanceOriginal(1);
2423 if (mSkipCharsIterator.GetOriginalOffset() <
2424 GetTextFrame()->GetContentEnd()) {
2425 // We're still within the part of the text run for the current text frame.
2426 UpdateGlyphStartTextElementCharIndex();
2427 return true;
2428 }
2429
2430 // Advance to the next frame.
2431 mFrameIterator.GetNext();
2432
2433 // Skip any undisplayed characters.
2434 uint32_t undisplayed = mFrameIterator.UndisplayedCharacters();
2435 mTextElementCharIndex += undisplayed;
2436 if (!GetTextFrame()) {
2437 // We're at the end.
2438 mSkipCharsIterator = gfxSkipCharsIterator();
2439 return false;
2440 }
2441
2442 mSkipCharsIterator = GetTextFrame()->EnsureTextRun(nsTextFrame::eInflated);
2443 mTextRun = GetTextFrame()->GetTextRun(nsTextFrame::eInflated);
2444 UpdateGlyphStartTextElementCharIndex();
2445 return true;
2446}
2447
2448bool CharIterator::MatchesFilter() const {
2449 switch (mFilter) {
2450 case CharacterFilter::Original:
2451 return true;
2452 case CharacterFilter::Unskipped:
2453 return !IsOriginalCharSkipped();
2454 case CharacterFilter::Addressable:
2455 return !IsOriginalCharSkipped() && !IsOriginalCharUnaddressable();
2456 }
2457 MOZ_ASSERT_UNREACHABLE("Invalid mFilter value")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Invalid mFilter value" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2457); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Invalid mFilter value" ")"); do {
MOZ_CrashSequence(__null, 2457); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2458 return true;
2459}
2460
2461// -----------------------------------------------------------------------------
2462// SVGTextDrawPathCallbacks
2463
2464/**
2465 * Text frame draw callback class that paints the text and text decoration parts
2466 * of an nsTextFrame using SVG painting properties, and selection backgrounds
2467 * and decorations as they would normally.
2468 *
2469 * An instance of this class is passed to nsTextFrame::PaintText if painting
2470 * cannot be done directly (e.g. if we are using an SVG pattern fill, stroking
2471 * the text, etc.).
2472 */
2473class SVGTextDrawPathCallbacks final : public nsTextFrame::DrawPathCallbacks {
2474 using imgDrawingParams = image::imgDrawingParams;
2475
2476 public:
2477 /**
2478 * Constructs an SVGTextDrawPathCallbacks.
2479 *
2480 * @param aSVGTextFrame The ancestor text frame.
2481 * @param aContextPaint Used by context-fill and context-stroke.
2482 * @param aContext The context to use for painting.
2483 * @param aFrame The nsTextFrame to paint.
2484 * @param aCanvasTM The transformation matrix to set when painting; this
2485 * should be the FOR_OUTERSVG_TM canvas TM of the text, so that
2486 * paint servers are painted correctly.
2487 * @param aImgParams Whether we need to synchronously decode images.
2488 * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted.
2489 */
2490 SVGTextDrawPathCallbacks(SVGTextFrame* aSVGTextFrame,
2491 SVGContextPaint* aContextPaint, gfxContext& aContext,
2492 nsTextFrame* aFrame, const gfxMatrix& aCanvasTM,
2493 imgDrawingParams& aImgParams,
2494 bool aShouldPaintSVGGlyphs)
2495 : DrawPathCallbacks(aShouldPaintSVGGlyphs),
2496 mSVGTextFrame(aSVGTextFrame),
2497 mContextPaint(aContextPaint),
2498 mContext(aContext),
2499 mFrame(aFrame),
2500 mCanvasTM(aCanvasTM),
2501 mImgParams(aImgParams) {}
2502
2503 void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect,
2504 nscolor aColor,
2505 DrawTarget& aDrawTarget) override;
2506 void PaintDecorationLine(Rect aPath, bool aPaintingShadows,
2507 nscolor aColor) override;
2508 void PaintSelectionDecorationLine(Rect aPath, bool aPaintingShadows,
2509 nscolor aColor) override;
2510 void NotifyBeforeText(bool aPaintingShadows, nscolor aColor) override;
2511 void NotifyGlyphPathEmitted() override;
2512 void NotifyAfterText() override;
2513
2514 private:
2515 void SetupContext();
2516
2517 bool IsClipPathChild() const {
2518 return mSVGTextFrame->HasAnyStateBits(NS_STATE_SVG_CLIPPATH_CHILD);
2519 }
2520
2521 /**
2522 * Paints a piece of text geometry. This is called when glyphs
2523 * or text decorations have been emitted to the gfxContext.
2524 */
2525 void HandleTextGeometry();
2526
2527 /**
2528 * Sets the gfxContext paint to the appropriate color or pattern
2529 * for filling text geometry.
2530 */
2531 void MakeFillPattern(GeneralPattern* aOutPattern);
2532
2533 /**
2534 * Fills and strokes a piece of text geometry, using group opacity
2535 * if the selection style requires it.
2536 */
2537 void FillAndStrokeGeometry();
2538
2539 /**
2540 * Fills a piece of text geometry.
2541 */
2542 void FillGeometry();
2543
2544 /**
2545 * Strokes a piece of text geometry.
2546 */
2547 void StrokeGeometry();
2548
2549 /*
2550 * Takes a colour and modifies it to account for opacity properties.
2551 */
2552 void ApplyOpacity(sRGBColor& aColor, const StyleSVGPaint& aPaint,
2553 const StyleSVGOpacity& aOpacity) const;
2554
2555 SVGTextFrame* const mSVGTextFrame;
2556 SVGContextPaint* const mContextPaint;
2557 gfxContext& mContext;
2558 nsTextFrame* const mFrame;
2559 const gfxMatrix& mCanvasTM;
2560 imgDrawingParams& mImgParams;
2561
2562 /**
2563 * The color that we were last told from one of the path callback functions.
2564 * This color can be the special NS_SAME_AS_FOREGROUND_COLOR,
2565 * NS_40PERCENT_FOREGROUND_COLOR and NS_TRANSPARENT colors when we are
2566 * painting selections or IME decorations.
2567 */
2568 nscolor mColor = NS_RGBA(0, 0, 0, 0)((nscolor)(((0) << 24) | ((0) << 16) | ((0) <<
8) | (0)))
;
2569
2570 /**
2571 * Whether we're painting text shadows.
2572 */
2573 bool mPaintingShadows = false;
2574};
2575
2576void SVGTextDrawPathCallbacks::NotifySelectionBackgroundNeedsFill(
2577 const Rect& aBackgroundRect, nscolor aColor, DrawTarget& aDrawTarget) {
2578 if (IsClipPathChild()) {
2579 // Don't paint selection backgrounds when in a clip path.
2580 return;
2581 }
2582
2583 mColor = aColor; // currently needed by MakeFillPattern
2584 mPaintingShadows = false;
2585
2586 GeneralPattern fillPattern;
2587 MakeFillPattern(&fillPattern);
2588 if (fillPattern.GetPattern()) {
2589 DrawOptions drawOptions(aColor == NS_40PERCENT_FOREGROUND_COLOR ? 0.4
2590 : 1.0);
2591 aDrawTarget.FillRect(aBackgroundRect, fillPattern, drawOptions);
2592 }
2593}
2594
2595void SVGTextDrawPathCallbacks::NotifyBeforeText(bool aPaintingShadows,
2596 nscolor aColor) {
2597 mColor = aColor;
2598 mPaintingShadows = aPaintingShadows;
2599 SetupContext();
2600 mContext.NewPath();
2601}
2602
2603void SVGTextDrawPathCallbacks::NotifyGlyphPathEmitted() {
2604 HandleTextGeometry();
2605 mContext.NewPath();
2606}
2607
2608void SVGTextDrawPathCallbacks::NotifyAfterText() { mContext.Restore(); }
2609
2610void SVGTextDrawPathCallbacks::PaintDecorationLine(Rect aPath,
2611 bool aPaintingShadows,
2612 nscolor aColor) {
2613 mColor = aColor;
2614 mPaintingShadows = aPaintingShadows;
2615 AntialiasMode aaMode =
2616 SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering);
2617
2618 mContext.Save();
2619 mContext.NewPath();
2620 mContext.SetAntialiasMode(aaMode);
2621 mContext.Rectangle(ThebesRect(aPath));
2622 HandleTextGeometry();
2623 mContext.NewPath();
2624 mContext.Restore();
2625}
2626
2627void SVGTextDrawPathCallbacks::PaintSelectionDecorationLine(
2628 Rect aPath, bool aPaintingShadows, nscolor aColor) {
2629 if (IsClipPathChild()) {
2630 // Don't paint selection decorations when in a clip path.
2631 return;
2632 }
2633
2634 mColor = aColor;
2635 mPaintingShadows = aPaintingShadows;
2636
2637 mContext.Save();
2638 mContext.NewPath();
2639 mContext.Rectangle(ThebesRect(aPath));
2640 FillAndStrokeGeometry();
2641 mContext.Restore();
2642}
2643
2644void SVGTextDrawPathCallbacks::SetupContext() {
2645 mContext.Save();
2646
2647 // XXX This is copied from nsSVGGlyphFrame::Render, but cairo doesn't actually
2648 // seem to do anything with the antialias mode. So we can perhaps remove it,
2649 // or make SetAntialiasMode set cairo text antialiasing too.
2650 mContext.SetAntialiasMode(
2651 SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering));
2652}
2653
2654void SVGTextDrawPathCallbacks::HandleTextGeometry() {
2655 if (IsClipPathChild()) {
2656 RefPtr<Path> path = mContext.GetPath();
2657 ColorPattern white(
2658 DeviceColor(1.f, 1.f, 1.f, 1.f)); // for masking, so no ToDeviceColor
2659 mContext.GetDrawTarget()->Fill(path, white);
2660 } else {
2661 // Normal painting.
2662 gfxContextMatrixAutoSaveRestore saveMatrix(&mContext);
2663 mContext.SetMatrixDouble(mCanvasTM);
2664
2665 FillAndStrokeGeometry();
2666 }
2667}
2668
2669void SVGTextDrawPathCallbacks::ApplyOpacity(
2670 sRGBColor& aColor, const StyleSVGPaint& aPaint,
2671 const StyleSVGOpacity& aOpacity) const {
2672 if (aPaint.kind.tag == StyleSVGPaintKind::Tag::Color) {
2673 aColor.a *=
2674 sRGBColor::FromABGR(aPaint.kind.AsColor().CalcColor(*mFrame->Style()))
2675 .a;
2676 }
2677 aColor.a *= SVGUtils::GetOpacity(aOpacity, mContextPaint);
2678}
2679
2680void SVGTextDrawPathCallbacks::MakeFillPattern(GeneralPattern* aOutPattern) {
2681 if (mColor == NS_SAME_AS_FOREGROUND_COLOR ||
2682 mColor == NS_40PERCENT_FOREGROUND_COLOR) {
2683 SVGUtils::MakeFillPatternFor(mFrame, &mContext, aOutPattern, mImgParams,
2684 mContextPaint);
2685 return;
2686 }
2687
2688 if (mColor == NS_TRANSPARENT) {
2689 return;
2690 }
2691
2692 sRGBColor color(sRGBColor::FromABGR(mColor));
2693 if (mPaintingShadows) {
2694 ApplyOpacity(color, mFrame->StyleSVG()->mFill,
2695 mFrame->StyleSVG()->mFillOpacity);
2696 }
2697 aOutPattern->InitColorPattern(ToDeviceColor(color));
2698}
2699
2700void SVGTextDrawPathCallbacks::FillAndStrokeGeometry() {
2701 gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&mContext);
2702 if (mColor == NS_40PERCENT_FOREGROUND_COLOR) {
2703 autoGroupForBlend.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 0.4f);
2704 }
2705
2706 uint32_t paintOrder = mFrame->StyleSVG()->mPaintOrder;
2707 if (!paintOrder) {
2708 FillGeometry();
2709 StrokeGeometry();
2710 } else {
2711 while (paintOrder) {
2712 auto component = StylePaintOrder(paintOrder & kPaintOrderMask);
2713 switch (component) {
2714 case StylePaintOrder::Fill:
2715 FillGeometry();
2716 break;
2717 case StylePaintOrder::Stroke:
2718 StrokeGeometry();
2719 break;
2720 default:
2721 MOZ_FALLTHROUGH_ASSERT("Unknown paint-order value")do { do { } while (false); MOZ_ReportCrash("" "MOZ_FALLTHROUGH_ASSERT: "
"Unknown paint-order value", "./../../../layout/svg/SVGTextFrame.cpp"
, 2721); AnnotateMozCrashReason("MOZ_CRASH(" "MOZ_FALLTHROUGH_ASSERT: "
"Unknown paint-order value" ")"); do { MOZ_CrashSequence(__null
, 2721); __attribute__((nomerge)) ::abort(); } while (false);
} while (false)
;
2722 case StylePaintOrder::Markers:
2723 case StylePaintOrder::Normal:
2724 break;
2725 }
2726 paintOrder >>= kPaintOrderShift;
2727 }
2728 }
2729}
2730
2731void SVGTextDrawPathCallbacks::FillGeometry() {
2732 if (mFrame->StyleSVG()->mFill.kind.IsNone()) {
2733 return;
2734 }
2735 GeneralPattern fillPattern;
2736 MakeFillPattern(&fillPattern);
2737 if (fillPattern.GetPattern()) {
2738 RefPtr<Path> path = mContext.GetPath();
2739 FillRule fillRule = SVGUtils::ToFillRule(mFrame->StyleSVG()->mFillRule);
2740 if (fillRule != path->GetFillRule()) {
2741 Path::SetFillRule(path, fillRule);
2742 }
2743 mContext.GetDrawTarget()->Fill(path, fillPattern);
2744 }
2745}
2746
2747void SVGTextDrawPathCallbacks::StrokeGeometry() {
2748 // We don't paint the stroke when we are filling with a selection color.
2749 if (!(mColor == NS_SAME_AS_FOREGROUND_COLOR ||
2750 mColor == NS_40PERCENT_FOREGROUND_COLOR || mPaintingShadows)) {
2751 return;
2752 }
2753
2754 if (!SVGUtils::HasStroke(mFrame, mContextPaint)) {
2755 return;
2756 }
2757
2758 GeneralPattern strokePattern;
2759 if (mPaintingShadows) {
2760 sRGBColor color(sRGBColor::FromABGR(mColor));
2761 ApplyOpacity(color, mFrame->StyleSVG()->mStroke,
2762 mFrame->StyleSVG()->mStrokeOpacity);
2763 strokePattern.InitColorPattern(ToDeviceColor(color));
2764 } else {
2765 SVGUtils::MakeStrokePatternFor(mFrame, &mContext, &strokePattern,
2766 mImgParams, mContextPaint);
2767 }
2768 if (strokePattern.GetPattern()) {
2769 SVGElement* svgOwner =
2770 SVGElement::FromNode(mFrame->GetParent()->GetContent());
2771
2772 // Apply any stroke-specific transform
2773 if (Maybe<gfxMatrix> userToOuterSVG =
2774 SVGUtils::GetNonScalingStrokeTransform(mFrame)) {
2775 mContext.Multiply(userToOuterSVG->Inverse());
2776 }
2777
2778 RefPtr<Path> path = mContext.GetPath();
2779 SVGContentUtils::AutoStrokeOptions strokeOptions;
2780 SVGContentUtils::GetStrokeOptions(&strokeOptions, svgOwner, mFrame->Style(),
2781 mContextPaint);
2782 DrawOptions drawOptions;
2783 drawOptions.mAntialiasMode =
2784 SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering);
2785 mContext.GetDrawTarget()->Stroke(path, strokePattern, strokeOptions);
2786 }
2787}
2788
2789// ============================================================================
2790// SVGTextFrame
2791
2792// ----------------------------------------------------------------------------
2793// Display list item
2794
2795bool DisplaySVGText::ShouldBeActive(
2796 mozilla::wr::DisplayListBuilder& aBuilder,
2797 mozilla::wr::IpcResourceUpdateQueue& aResources,
2798 const mozilla::layers::StackingContextHelper& aSc,
2799 mozilla::layers::RenderRootStateManager* aManager,
2800 nsDisplayListBuilder* aDisplayListBuilder) {
2801 auto* frame = static_cast<SVGTextFrame*>(mFrame);
2802 return frame
2803 ->CreateWebRenderCommands(aBuilder, aResources, aSc, aManager,
2804 aDisplayListBuilder, this, /*aDryRun=*/true)
2805 .isOk();
2806}
2807
2808WebRenderCommandsResult DisplaySVGText::CreateWebRenderCommands(
2809 mozilla::wr::DisplayListBuilder& aBuilder,
2810 mozilla::wr::IpcResourceUpdateQueue& aResources,
2811 const mozilla::layers::StackingContextHelper& aSc,
2812 mozilla::layers::RenderRootStateManager* aManager,
2813 nsDisplayListBuilder* aDisplayListBuilder) {
2814 auto* frame = static_cast<SVGTextFrame*>(mFrame);
2815 return frame->CreateWebRenderCommands(aBuilder, aResources, aSc, aManager,
2816 aDisplayListBuilder, this,
2817 /*aDryRun=*/false);
2818}
2819
2820// ---------------------------------------------------------------------
2821// nsQueryFrame methods
2822
2823NS_QUERYFRAME_HEAD(SVGTextFrame)void* SVGTextFrame ::QueryFrame(FrameIID id) const { switch (
id) {
2824 NS_QUERYFRAME_ENTRY(SVGTextFrame)case SVGTextFrame ::kFrameIID: { static_assert( std::is_same_v
<SVGTextFrame, SVGTextFrame ::Has_NS_DECL_QUERYFRAME_TARGET
>, "SVGTextFrame" " must declare itself as a queryframe target"
); return const_cast<SVGTextFrame*>(static_cast<const
SVGTextFrame*>(this)); }
2825NS_QUERYFRAME_TAIL_INHERITING(SVGDisplayContainerFrame)default: break; } return SVGDisplayContainerFrame ::QueryFrame
(id); }
2826
2827} // namespace mozilla
2828
2829// ---------------------------------------------------------------------
2830// Implementation
2831
2832nsIFrame* NS_NewSVGTextFrame(mozilla::PresShell* aPresShell,
2833 mozilla::ComputedStyle* aStyle) {
2834 return new (aPresShell)
2835 mozilla::SVGTextFrame(aStyle, aPresShell->GetPresContext());
2836}
2837
2838namespace mozilla {
2839
2840NS_IMPL_FRAMEARENA_HELPERS(SVGTextFrame)void* SVGTextFrame ::operator new(size_t sz, mozilla::PresShell
* aShell) { return aShell->AllocateFrame(nsQueryFrame::SVGTextFrame_id
, sz); }
2841
2842// ---------------------------------------------------------------------
2843// nsIFrame methods
2844
2845void SVGTextFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
2846 nsIFrame* aPrevInFlow) {
2847 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::text),do { if (!(aContent->IsSVGElement(nsGkAtoms::text))) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Content is not an SVG text", "aContent->IsSVGElement(nsGkAtoms::text)"
, "./../../../layout/svg/SVGTextFrame.cpp", 2848); MOZ_PretendNoReturn
(); } } while (0)
2848 "Content is not an SVG text")do { if (!(aContent->IsSVGElement(nsGkAtoms::text))) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Content is not an SVG text", "aContent->IsSVGElement(nsGkAtoms::text)"
, "./../../../layout/svg/SVGTextFrame.cpp", 2848); MOZ_PretendNoReturn
(); } } while (0)
;
2849
2850 SVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
2851 AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
2852
2853 mMutationObserver = new MutationObserver(this);
2854
2855 if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
2856 // We're inserting a new <text> element into a non-display context.
2857 // Ensure that we get reflowed.
2858 ScheduleReflowSVGNonDisplayText(
2859 IntrinsicDirty::FrameAncestorsAndDescendants);
2860 }
2861}
2862
2863void SVGTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
2864 const nsDisplayListSet& aLists) {
2865 if (IsSubtreeDirty()) {
2866 // We can sometimes be asked to paint before reflow happens and we
2867 // have updated mPositions, etc. In this case, we just avoid
2868 // painting.
2869 return;
2870 }
2871 if (!IsVisibleForPainting() && aBuilder->IsForPainting()) {
2872 return;
2873 }
2874 DisplayOutline(aBuilder, aLists);
2875 aLists.Content()->AppendNewToTop<DisplaySVGText>(aBuilder, this);
2876}
2877
2878void SVGTextFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
2879 SVGDisplayContainerFrame::DidSetComputedStyle(aOldComputedStyle);
2880 if (StyleSVGReset()->HasNonScalingStroke() &&
2881 (!aOldComputedStyle ||
2882 !aOldComputedStyle->StyleSVGReset()->HasNonScalingStroke())) {
2883 SVGUtils::UpdateNonScalingStrokeStateBit(this);
2884 }
2885}
2886
2887nsresult SVGTextFrame::AttributeChanged(int32_t aNameSpaceID,
2888 nsAtom* aAttribute, AttrModType) {
2889 if (aNameSpaceID != kNameSpaceID_None) {
2890 return NS_OK;
2891 }
2892
2893 if (aAttribute == nsGkAtoms::transform) {
2894 // We don't invalidate for transform changes (the layers code does that).
2895 // Also note that SVGTransformableElement::GetAttributeChangeHint will
2896 // return nsChangeHint_UpdateOverflow for "transform" attribute changes
2897 // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
2898
2899 if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW) && mCanvasTM &&
2900 mCanvasTM->IsSingular()) {
2901 // We won't have calculated the glyph positions correctly.
2902 NotifyGlyphMetricsChange(false);
2903 }
2904 mCanvasTM = nullptr;
2905 } else if (IsGlyphPositioningAttribute(aAttribute) ||
2906 aAttribute == nsGkAtoms::textLength ||
2907 aAttribute == nsGkAtoms::lengthAdjust) {
2908 NotifyGlyphMetricsChange(false);
2909 }
2910
2911 return NS_OK;
2912}
2913
2914void SVGTextFrame::ReflowSVGNonDisplayText() {
2915 MOZ_ASSERT(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
" (" "only call ReflowSVGNonDisplayText when an outer SVG frame is "
"under ReflowSVG" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2917); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
") (" "only call ReflowSVGNonDisplayText when an outer SVG frame is "
"under ReflowSVG" ")"); do { MOZ_CrashSequence(__null, 2917)
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
2916 "only call ReflowSVGNonDisplayText when an outer SVG frame is "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
" (" "only call ReflowSVGNonDisplayText when an outer SVG frame is "
"under ReflowSVG" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2917); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
") (" "only call ReflowSVGNonDisplayText when an outer SVG frame is "
"under ReflowSVG" ")"); do { MOZ_CrashSequence(__null, 2917)
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
2917 "under ReflowSVG")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
" (" "only call ReflowSVGNonDisplayText when an outer SVG frame is "
"under ReflowSVG" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2917); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
") (" "only call ReflowSVGNonDisplayText when an outer SVG frame is "
"under ReflowSVG" ")"); do { MOZ_CrashSequence(__null, 2917)
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
2918 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_IS_NONDISPLAY),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(HasAnyStateBits(NS_FRAME_IS_NONDISPLAY))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)"
" (" "only call ReflowSVGNonDisplayText if the frame is " "NS_FRAME_IS_NONDISPLAY"
")", "./../../../layout/svg/SVGTextFrame.cpp", 2920); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)" ") ("
"only call ReflowSVGNonDisplayText if the frame is " "NS_FRAME_IS_NONDISPLAY"
")"); do { MOZ_CrashSequence(__null, 2920); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2919 "only call ReflowSVGNonDisplayText if the frame is "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(HasAnyStateBits(NS_FRAME_IS_NONDISPLAY))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)"
" (" "only call ReflowSVGNonDisplayText if the frame is " "NS_FRAME_IS_NONDISPLAY"
")", "./../../../layout/svg/SVGTextFrame.cpp", 2920); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)" ") ("
"only call ReflowSVGNonDisplayText if the frame is " "NS_FRAME_IS_NONDISPLAY"
")"); do { MOZ_CrashSequence(__null, 2920); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2920 "NS_FRAME_IS_NONDISPLAY")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(HasAnyStateBits(NS_FRAME_IS_NONDISPLAY))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)"
" (" "only call ReflowSVGNonDisplayText if the frame is " "NS_FRAME_IS_NONDISPLAY"
")", "./../../../layout/svg/SVGTextFrame.cpp", 2920); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)" ") ("
"only call ReflowSVGNonDisplayText if the frame is " "NS_FRAME_IS_NONDISPLAY"
")"); do { MOZ_CrashSequence(__null, 2920); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2921
2922 // We had a style change, so we mark this frame as dirty so that the next
2923 // time it is painted, we reflow the anonymous block frame.
2924 MarkSubtreeDirty();
2925
2926 // Finally, we need to actually reflow the anonymous block frame and update
2927 // mPositions, in case we are being reflowed immediately after a DOM
2928 // mutation that needs frame reconstruction.
2929 MaybeReflowAnonymousBlockChild();
2930 UpdateGlyphPositioning();
2931}
2932
2933void SVGTextFrame::ScheduleReflowSVGNonDisplayText(IntrinsicDirty aReason) {
2934 MOZ_ASSERT(!SVGUtils::OuterSVGIsCallingReflowSVG(this),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!SVGUtils::OuterSVGIsCallingReflowSVG(this))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!SVGUtils::OuterSVGIsCallingReflowSVG(this)))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!SVGUtils::OuterSVGIsCallingReflowSVG(this)"
" (" "do not call ScheduleReflowSVGNonDisplayText when the outer SVG "
"frame is under ReflowSVG" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2936); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!SVGUtils::OuterSVGIsCallingReflowSVG(this)"
") (" "do not call ScheduleReflowSVGNonDisplayText when the outer SVG "
"frame is under ReflowSVG" ")"); do { MOZ_CrashSequence(__null
, 2936); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
2935 "do not call ScheduleReflowSVGNonDisplayText when the outer SVG "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!SVGUtils::OuterSVGIsCallingReflowSVG(this))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!SVGUtils::OuterSVGIsCallingReflowSVG(this)))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!SVGUtils::OuterSVGIsCallingReflowSVG(this)"
" (" "do not call ScheduleReflowSVGNonDisplayText when the outer SVG "
"frame is under ReflowSVG" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2936); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!SVGUtils::OuterSVGIsCallingReflowSVG(this)"
") (" "do not call ScheduleReflowSVGNonDisplayText when the outer SVG "
"frame is under ReflowSVG" ")"); do { MOZ_CrashSequence(__null
, 2936); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
2936 "frame is under ReflowSVG")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!SVGUtils::OuterSVGIsCallingReflowSVG(this))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!SVGUtils::OuterSVGIsCallingReflowSVG(this)))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!SVGUtils::OuterSVGIsCallingReflowSVG(this)"
" (" "do not call ScheduleReflowSVGNonDisplayText when the outer SVG "
"frame is under ReflowSVG" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2936); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!SVGUtils::OuterSVGIsCallingReflowSVG(this)"
") (" "do not call ScheduleReflowSVGNonDisplayText when the outer SVG "
"frame is under ReflowSVG" ")"); do { MOZ_CrashSequence(__null
, 2936); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
2937 MOZ_ASSERT(!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)"
" (" "do not call ScheduleReflowSVGNonDisplayText while reflowing the "
"anonymous block child" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2939); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)"
") (" "do not call ScheduleReflowSVGNonDisplayText while reflowing the "
"anonymous block child" ")"); do { MOZ_CrashSequence(__null,
2939); __attribute__((nomerge)) ::abort(); } while (false); }
} while (false)
2938 "do not call ScheduleReflowSVGNonDisplayText while reflowing the "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)"
" (" "do not call ScheduleReflowSVGNonDisplayText while reflowing the "
"anonymous block child" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2939); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)"
") (" "do not call ScheduleReflowSVGNonDisplayText while reflowing the "
"anonymous block child" ")"); do { MOZ_CrashSequence(__null,
2939); __attribute__((nomerge)) ::abort(); } while (false); }
} while (false)
2939 "anonymous block child")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)"
" (" "do not call ScheduleReflowSVGNonDisplayText while reflowing the "
"anonymous block child" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2939); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)"
") (" "do not call ScheduleReflowSVGNonDisplayText while reflowing the "
"anonymous block child" ")"); do { MOZ_CrashSequence(__null,
2939); __attribute__((nomerge)) ::abort(); } while (false); }
} while (false)
;
2940
2941 // We need to find an ancestor frame that we can call FrameNeedsReflow
2942 // on that will cause the document to be marked as needing relayout,
2943 // and for that ancestor (or some further ancestor) to be marked as
2944 // a root to reflow. We choose the closest ancestor frame that is not
2945 // NS_FRAME_IS_NONDISPLAY and which is either an outer SVG frame or a
2946 // non-SVG frame. (We don't consider displayed SVG frame ancestors other
2947 // than SVGOuterSVGFrame, since calling FrameNeedsReflow on those other
2948 // SVG frames would do a bunch of unnecessary work on the SVG frames up to
2949 // the SVGOuterSVGFrame.)
2950
2951 nsIFrame* f = this;
2952 while (f) {
2953 if (!f->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
2954 if (f->IsSubtreeDirty()) {
2955 // This is a displayed frame, so if it is already dirty, we will be
2956 // reflowed soon anyway. No need to call FrameNeedsReflow again, then.
2957 return;
2958 }
2959 if (!f->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
2960 break;
2961 }
2962 f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
2963 }
2964 f = f->GetParent();
2965 }
2966
2967 MOZ_ASSERT(f, "should have found an ancestor frame to reflow")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(f)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(f))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("f" " (" "should have found an ancestor frame to reflow" ")"
, "./../../../layout/svg/SVGTextFrame.cpp", 2967); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "f" ") (" "should have found an ancestor frame to reflow"
")"); do { MOZ_CrashSequence(__null, 2967); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2968
2969 PresShell()->FrameNeedsReflow(f, aReason, NS_FRAME_IS_DIRTY);
2970}
2971
2972NS_IMPL_ISUPPORTS(SVGTextFrame::MutationObserver, nsIMutationObserver)MozExternalRefCountType SVGTextFrame::MutationObserver::AddRef
(void) { static_assert(!std::is_destructible_v<SVGTextFrame
::MutationObserver>, "Reference-counted class " "SVGTextFrame::MutationObserver"
" should not have a public destructor. " "Make this class's destructor non-public"
); do { static_assert( mozilla::detail::AssertionConditionType
<decltype(int32_t(mRefCnt) >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0"
" (" "illegal refcnt" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2972); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0"
") (" "illegal refcnt" ")"); do { MOZ_CrashSequence(__null, 2972
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); do { static_assert( mozilla::detail::AssertionConditionType
<decltype("SVGTextFrame::MutationObserver" != nullptr)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!("SVGTextFrame::MutationObserver" != nullptr))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("\"SVGTextFrame::MutationObserver\" != nullptr"
" (" "Must specify a name" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2972); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"SVGTextFrame::MutationObserver\" != nullptr"
") (" "Must specify a name" ")"); do { MOZ_CrashSequence(__null
, 2972); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread
.AssertOwnership("SVGTextFrame::MutationObserver" " not thread-safe"
); nsrefcnt count = ++mRefCnt; NS_LogAddRef((this), (count), (
"SVGTextFrame::MutationObserver"), (uint32_t)(sizeof(*this)))
; return count; } MozExternalRefCountType SVGTextFrame::MutationObserver
::Release(void) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(int32_t(mRefCnt) > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0"
" (" "dup release" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2972); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0"
") (" "dup release" ")"); do { MOZ_CrashSequence(__null, 2972
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); do { static_assert( mozilla::detail::AssertionConditionType
<decltype("SVGTextFrame::MutationObserver" != nullptr)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!("SVGTextFrame::MutationObserver" != nullptr))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("\"SVGTextFrame::MutationObserver\" != nullptr"
" (" "Must specify a name" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 2972); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"SVGTextFrame::MutationObserver\" != nullptr"
") (" "Must specify a name" ")"); do { MOZ_CrashSequence(__null
, 2972); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread
.AssertOwnership("SVGTextFrame::MutationObserver" " not thread-safe"
); const char* const nametmp = "SVGTextFrame::MutationObserver"
; nsrefcnt count = --mRefCnt; NS_LogRelease((this), (count), (
nametmp)); if (count == 0) { mRefCnt = 1; delete (this); return
0; } return count; } nsresult SVGTextFrame::MutationObserver
::QueryInterface(const nsIID& aIID, void** aInstancePtr) {
do { if (!(aInstancePtr)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "QueryInterface requires a non-NULL destination!", "aInstancePtr"
, "./../../../layout/svg/SVGTextFrame.cpp", 2972); MOZ_PretendNoReturn
(); } } while (0); nsresult rv = NS_ERROR_FAILURE; static_assert
(1 > 0, "Need more arguments to NS_INTERFACE_TABLE"); static
const QITableEntry table[] = { {&mozilla::detail::kImplementedIID
<SVGTextFrame::MutationObserver, nsIMutationObserver>, int32_t
( reinterpret_cast<char*>(static_cast<nsIMutationObserver
*>((SVGTextFrame::MutationObserver*)0x1000)) - reinterpret_cast
<char*>((SVGTextFrame::MutationObserver*)0x1000))}, {&
mozilla::detail::kImplementedIID<SVGTextFrame::MutationObserver
, nsISupports>, int32_t(reinterpret_cast<char*>(static_cast
<nsISupports*>( static_cast<nsIMutationObserver*>
((SVGTextFrame::MutationObserver*)0x1000))) - reinterpret_cast
<char*>((SVGTextFrame::MutationObserver*)0x1000))}, { nullptr
, 0 } } ; static_assert(std::size(table) > 1, "need at least 1 interface"
); rv = NS_TableDrivenQI(static_cast<void*>(this), aIID
, aInstancePtr, table); return rv; }
2973
2974void SVGTextFrame::MutationObserver::ContentAppended(
2975 nsIContent* aFirstNewContent, const ContentAppendInfo&) {
2976 mFrame->NotifyGlyphMetricsChange(true);
2977}
2978
2979void SVGTextFrame::MutationObserver::ContentInserted(nsIContent* aChild,
2980 const ContentInsertInfo&) {
2981 mFrame->NotifyGlyphMetricsChange(true);
2982}
2983
2984void SVGTextFrame::MutationObserver::ContentWillBeRemoved(
2985 nsIContent* aChild, const ContentRemoveInfo& aInfo) {
2986 if (aInfo.mBatchRemovalState && !aInfo.mBatchRemovalState->mIsFirst) {
2987 return;
2988 }
2989 mFrame->NotifyGlyphMetricsChange(true);
2990}
2991
2992void SVGTextFrame::MutationObserver::CharacterDataChanged(
2993 nsIContent* aContent, const CharacterDataChangeInfo&) {
2994 mFrame->NotifyGlyphMetricsChange(true);
2995}
2996
2997void SVGTextFrame::MutationObserver::AttributeChanged(
2998 Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute, AttrModType,
2999 const nsAttrValue* aOldValue) {
3000 if (!aElement->IsSVGElement()) {
3001 return;
3002 }
3003
3004 // Attribute changes on this element will be handled by
3005 // SVGTextFrame::AttributeChanged.
3006 if (aElement == mFrame->GetContent()) {
3007 return;
3008 }
3009
3010 mFrame->HandleAttributeChangeInDescendant(aElement, aNameSpaceID, aAttribute);
3011}
3012
3013void SVGTextFrame::HandleAttributeChangeInDescendant(Element* aElement,
3014 int32_t aNameSpaceID,
3015 nsAtom* aAttribute) {
3016 if (aElement->IsSVGElement(nsGkAtoms::textPath)) {
3017 if (aNameSpaceID == kNameSpaceID_None &&
3018 (aAttribute == nsGkAtoms::startOffset ||
3019 aAttribute == nsGkAtoms::path || aAttribute == nsGkAtoms::side)) {
3020 NotifyGlyphMetricsChange(false);
3021 } else if ((aNameSpaceID == kNameSpaceID_XLink4 ||
3022 aNameSpaceID == kNameSpaceID_None) &&
3023 aAttribute == nsGkAtoms::href) {
3024 // Blow away our reference, if any
3025 nsIFrame* childElementFrame = aElement->GetPrimaryFrame();
3026 if (childElementFrame) {
3027 SVGObserverUtils::RemoveTextPathObserver(childElementFrame);
3028 NotifyGlyphMetricsChange(false);
3029 }
3030 }
3031 } else {
3032 if (aNameSpaceID == kNameSpaceID_None &&
3033 IsGlyphPositioningAttribute(aAttribute)) {
3034 NotifyGlyphMetricsChange(false);
3035 }
3036 }
3037}
3038
3039void SVGTextFrame::FindCloserFrameForSelection(
3040 const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) {
3041 if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
3042 return;
3043 }
3044
3045 UpdateGlyphPositioning();
3046
3047 nsPresContext* presContext = PresContext();
3048
3049 // Find the frame that has the closest rendered run rect to aPoint.
3050 TextRenderedRunIterator it(this);
3051 for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
3052 TextRenderedRun::GeometryFlags flags(
3053 TextRenderedRun::GeometryFlag::IncludeFill,
3054 TextRenderedRun::GeometryFlag::IncludeStroke,
3055 TextRenderedRun::GeometryFlag::NoHorizontalOverflow);
3056 SVGBBox userRect = run.GetUserSpaceRect(presContext, flags);
3057 float devPxPerCSSPx = presContext->CSSPixelsToDevPixels(1.f);
3058 userRect.Scale(devPxPerCSSPx);
3059
3060 if (!userRect.IsEmpty()) {
3061 gfxMatrix m;
3062 nsRect rect =
3063 SVGUtils::ToCanvasBounds(userRect.ToThebesRect(), m, presContext);
3064
3065 if (nsLayoutUtils::PointIsCloserToRect(aPoint, rect,
3066 aCurrentBestFrame->mXDistance,
3067 aCurrentBestFrame->mYDistance)) {
3068 aCurrentBestFrame->mFrame = run.mFrame;
3069 }
3070 }
3071 }
3072}
3073
3074//----------------------------------------------------------------------
3075// ISVGDisplayableFrame methods
3076
3077void SVGTextFrame::NotifySVGChanged(ChangeFlags aFlags) {
3078 MOZ_ASSERT(aFlags.contains(ChangeFlag::TransformChanged) ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aFlags.contains(ChangeFlag::TransformChanged) || aFlags
.contains(ChangeFlag::CoordContextChanged))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aFlags.contains(ChangeFlag::
TransformChanged) || aFlags.contains(ChangeFlag::CoordContextChanged
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aFlags.contains(ChangeFlag::TransformChanged) || aFlags.contains(ChangeFlag::CoordContextChanged)"
" (" "Invalidation logic may need adjusting" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 3080); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aFlags.contains(ChangeFlag::TransformChanged) || aFlags.contains(ChangeFlag::CoordContextChanged)"
") (" "Invalidation logic may need adjusting" ")"); do { MOZ_CrashSequence
(__null, 3080); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
3079 aFlags.contains(ChangeFlag::CoordContextChanged),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aFlags.contains(ChangeFlag::TransformChanged) || aFlags
.contains(ChangeFlag::CoordContextChanged))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aFlags.contains(ChangeFlag::
TransformChanged) || aFlags.contains(ChangeFlag::CoordContextChanged
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aFlags.contains(ChangeFlag::TransformChanged) || aFlags.contains(ChangeFlag::CoordContextChanged)"
" (" "Invalidation logic may need adjusting" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 3080); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aFlags.contains(ChangeFlag::TransformChanged) || aFlags.contains(ChangeFlag::CoordContextChanged)"
") (" "Invalidation logic may need adjusting" ")"); do { MOZ_CrashSequence
(__null, 3080); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
3080 "Invalidation logic may need adjusting")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aFlags.contains(ChangeFlag::TransformChanged) || aFlags
.contains(ChangeFlag::CoordContextChanged))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aFlags.contains(ChangeFlag::
TransformChanged) || aFlags.contains(ChangeFlag::CoordContextChanged
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aFlags.contains(ChangeFlag::TransformChanged) || aFlags.contains(ChangeFlag::CoordContextChanged)"
" (" "Invalidation logic may need adjusting" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 3080); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aFlags.contains(ChangeFlag::TransformChanged) || aFlags.contains(ChangeFlag::CoordContextChanged)"
") (" "Invalidation logic may need adjusting" ")"); do { MOZ_CrashSequence
(__null, 3080); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3081
3082 bool needNewBounds = false;
3083 bool needGlyphMetricsUpdate = false;
3084 if (aFlags.contains(ChangeFlag::CoordContextChanged)) {
3085 if (HasAnyStateBits(NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES)) {
3086 needGlyphMetricsUpdate = true;
3087 }
3088 if (SVGContentUtils::HasPercentageDependentStroke(
3089 Style(), SVGContextPaint::GetContextPaint(GetContent())) ||
3090 SVGIntegrationUtils::UsingEffectsForFrame(this)) {
3091 // Stroke and effects may have percentage dependent units.
3092 needNewBounds = true;
3093 }
3094 }
3095
3096 if (aFlags.contains(ChangeFlag::TransformChanged)) {
3097 if (mCanvasTM && mCanvasTM->IsSingular()) {
3098 // We won't have calculated the glyph positions correctly.
3099 needNewBounds = true;
3100 needGlyphMetricsUpdate = true;
3101 }
3102 mCanvasTM = nullptr;
3103 if (StyleSVGReset()->HasNonScalingStroke()) {
3104 // Stroke currently contributes to our mRect, and our stroke depends on
3105 // the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|.
3106 needNewBounds = true;
3107 }
3108
3109 // If the scale at which we computed our mFontSizeScaleFactor has changed by
3110 // at least a factor of two, reflow the text. This avoids reflowing text at
3111 // every tick of a transform animation, but ensures our glyph metrics
3112 // do not get too far out of sync with the final font size on the screen.
3113 const float scale = GetContextScale(this);
3114 if (scale != mLastContextScale) {
3115 if (mLastContextScale == 0.0f) {
3116 needNewBounds = true;
3117 needGlyphMetricsUpdate = true;
3118 } else {
3119 float change = scale / mLastContextScale;
3120 if (change >= 2.0f || change <= 0.5f) {
3121 needNewBounds = true;
3122 needGlyphMetricsUpdate = true;
3123 }
3124 }
3125 }
3126 }
3127
3128 if (needNewBounds) {
3129 // Ancestor changes can't affect how we render from the perspective of
3130 // any rendering observers that we may have, so we don't need to
3131 // invalidate them. We also don't need to invalidate ourself, since our
3132 // changed ancestor will have invalidated its entire area, which includes
3133 // our area.
3134 ScheduleReflowSVG();
3135 }
3136
3137 if (needGlyphMetricsUpdate) {
3138 // If we are positioned using percentage values we need to update our
3139 // position whenever our viewport's dimensions change. But only do this if
3140 // we have been reflowed once, otherwise the glyph positioning will be
3141 // wrong. (We need to wait until bidi reordering has been done.)
3142 if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
3143 NotifyGlyphMetricsChange(false);
3144 }
3145 }
3146}
3147
3148/**
3149 * Gets the offset into a DOM node that the specified caret is positioned at.
3150 */
3151static int32_t GetCaretOffset(nsCaret* aCaret) {
3152 RefPtr<Selection> selection = aCaret->GetSelection();
3153 if (!selection) {
3154 return -1;
3155 }
3156
3157 return selection->AnchorOffset();
3158}
3159
3160/**
3161 * Returns whether the caret should be painted for a given TextRenderedRun
3162 * by checking whether the caret is in the range covered by the rendered run.
3163 *
3164 * @param aThisRun The TextRenderedRun to be painted.
3165 * @param aCaret The caret.
3166 */
3167static bool ShouldPaintCaret(const TextRenderedRun& aThisRun, nsCaret* aCaret) {
3168 int32_t caretOffset = GetCaretOffset(aCaret);
3169
3170 if (caretOffset < 0) {
3171 return false;
3172 }
3173
3174 return uint32_t(caretOffset) >= aThisRun.mTextFrameContentOffset &&
3175 uint32_t(caretOffset) < aThisRun.mTextFrameContentOffset +
3176 aThisRun.mTextFrameContentLength;
3177}
3178
3179void SVGTextFrame::PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform,
3180 imgDrawingParams& aImgParams) {
3181 DrawTarget& aDrawTarget = *aContext.GetDrawTarget();
3182 nsIFrame* kid = PrincipalChildList().FirstChild();
3183 if (!kid) {
3184 return;
3185 }
3186
3187 if (IsSubtreeDirty()) {
3188 return;
3189 }
3190
3191 if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
3192 // Text frames inside <clipPath>, <mask>, etc. will never have had
3193 // ReflowSVG called on them, so call UpdateGlyphPositioning to do this now.
3194 UpdateGlyphPositioning();
3195 }
3196
3197 const float epsilon = 0.0001;
3198 if (std::abs(mLengthAdjustScaleFactor) < epsilon) {
3199 // A zero scale factor can be caused by having forced the text length to
3200 // zero. In this situation there is nothing to show.
3201 return;
3202 }
3203
3204 if (aTransform.IsSingular()) {
3205 NS_WARNING("Can't render text element!")NS_DebugBreak(NS_DEBUG_WARNING, "Can't render text element!",
nullptr, "./../../../layout/svg/SVGTextFrame.cpp", 3205)
;
3206 return;
3207 }
3208
3209 gfxMatrix initialMatrix = aContext.CurrentMatrixDouble();
3210
3211 gfxMatrix matrixForPaintServers = aTransform * initialMatrix;
3212
3213 // SVG frames' PaintSVG methods paint in CSS px, but normally frames paint in
3214 // dev pixels. Here we multiply a CSS-px-to-dev-pixel factor onto aTransform
3215 // so our non-SVG nsTextFrame children paint correctly.
3216 nsPresContext* presContext = PresContext();
3217 auto auPerDevPx = presContext->AppUnitsPerDevPixel();
3218 float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(auPerDevPx);
3219 gfxMatrix canvasTMForChildren = aTransform;
3220 canvasTMForChildren.PreScale(cssPxPerDevPx, cssPxPerDevPx);
3221 initialMatrix.PreScale(1 / cssPxPerDevPx, 1 / cssPxPerDevPx);
3222
3223 gfxContextMatrixAutoSaveRestore matSR(&aContext);
3224 aContext.NewPath();
3225 aContext.Multiply(canvasTMForChildren);
3226 gfxMatrix currentMatrix = aContext.CurrentMatrixDouble();
3227
3228 RefPtr<nsCaret> caret = presContext->PresShell()->GetActiveCaret();
3229 nsIFrame* caretFrame = caret->GetPaintGeometry();
3230
3231 gfxContextAutoSaveRestore ctxSR;
3232 TextRenderedRunIterator it(
3233 this, TextRenderedRunIterator::RenderedRunFilter::VisibleFrames);
3234 TextRenderedRun run = it.Current();
3235
3236 SVGContextPaint* outerContextPaint =
3237 SVGContextPaint::GetContextPaint(GetContent());
3238
3239 while (run.mFrame) {
3240 nsTextFrame* frame = run.mFrame;
3241
3242 auto contextPaint = MakeRefPtr<SVGContextPaint>(
3243 &aDrawTarget, initialMatrix, frame, outerContextPaint, aImgParams);
3244 DrawMode drawMode = contextPaint->GetDrawMode();
3245 if (drawMode & DrawMode::GLYPH_STROKE) {
3246 ctxSR.EnsureSaved(&aContext);
3247 // This may change the gfxContext's transform (for non-scaling stroke),
3248 // in which case this needs to happen before we call SetMatrix() below.
3249 SVGUtils::SetupStrokeGeometry(frame->GetParent(), &aContext,
3250 outerContextPaint);
3251 }
3252
3253 nscoord startEdge, endEdge;
3254 run.GetClipEdges(startEdge, endEdge);
3255
3256 // Set up the transform for painting the text frame for the substring
3257 // indicated by the run.
3258 gfxMatrix runTransform = run.GetTransformFromUserSpaceForPainting(
3259 presContext, startEdge, endEdge) *
3260 currentMatrix;
3261 aContext.SetMatrixDouble(runTransform);
3262
3263 if (drawMode != DrawMode(0)) {
3264 bool paintSVGGlyphs;
3265 nsTextFrame::PaintTextParams params(&aContext);
3266 params.framePt = Point();
3267 params.dirtyRect =
3268 LayoutDevicePixel::FromAppUnits(frame->InkOverflowRect(), auPerDevPx);
3269 params.contextPaint = contextPaint;
3270 bool isSelected;
3271 if (HasAnyStateBits(NS_STATE_SVG_CLIPPATH_CHILD)) {
3272 params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
3273 isSelected = false;
3274 } else {
3275 isSelected = frame->IsSelected();
3276 }
3277 gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&aContext);
3278 float opacity = 1.0f;
3279 nsIFrame* ancestor = frame->GetParent();
3280 while (ancestor != this) {
3281 opacity *= ancestor->StyleEffects()->mOpacity;
3282 ancestor = ancestor->GetParent();
3283 }
3284 if (opacity < 1.0f) {
3285 autoGroupForBlend.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
3286 opacity);
3287 }
3288
3289 if (ShouldRenderAsPath(frame, outerContextPaint, paintSVGGlyphs)) {
3290 SVGTextDrawPathCallbacks callbacks(this, outerContextPaint, aContext,
3291 frame, matrixForPaintServers,
3292 aImgParams, paintSVGGlyphs);
3293 params.callbacks = &callbacks;
3294 frame->PaintText(params, startEdge, endEdge, nsPoint(), isSelected,
3295 aImgParams);
3296 } else {
3297 frame->PaintText(params, startEdge, endEdge, nsPoint(), isSelected,
3298 aImgParams);
3299 }
3300 }
3301
3302 if (frame == caretFrame && ShouldPaintCaret(run, caret)) {
3303 // XXX Should we be looking at the fill/stroke colours to paint the
3304 // caret with, rather than using the color property?
3305 caret->PaintCaret(aDrawTarget, frame, nsPoint());
3306 aContext.NewPath();
3307 }
3308
3309 run = it.Next();
3310 }
3311}
3312
3313WebRenderCommandsResult SVGTextFrame::CreateWebRenderCommands(
3314 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
3315 const layers::StackingContextHelper& aSc,
3316 layers::RenderRootStateManager* aManager,
3317 nsDisplayListBuilder* aDisplayListBuilder, DisplaySVGText* aItem,
3318 bool aDryRun) {
3319 if (!StaticPrefs::gfx_webrender_svg_text()) {
3320 return Err("WebRender SVG text is disabled");
3321 }
3322 if (!PrincipalChildList().FirstChild() || IsSubtreeDirty()) {
3323 return Err("text is not laid out");
3324 }
3325 if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY | NS_STATE_SVG_CLIPPATH_CHILD)) {
3326 return Err("non-display text");
3327 }
3328 const float epsilon = 0.0001;
3329 if (std::abs(mLengthAdjustScaleFactor) < epsilon) {
3330 return Err("zero length adjust scale");
3331 }
3332 if (GetWritingMode().IsVertical() &&
3333 std::abs(mLengthAdjustScaleFactor - 1.0f) > epsilon) {
3334 // lengthAdjust: spacingAndGlyphs scales only the inline axis, so the run
3335 // transform is a non-uniform scale. Sideways vertical glyphs reach
3336 // WebRender pre-transposed through the glyph flags, and WebRender applies
3337 // the run transform after that transpose, which puts the scale on the
3338 // glyph's cross axis.
3339 return Err("length adjust scale on vertical text");
3340 }
3341
3342 nsPresContext* presContext = PresContext();
3343 const int32_t auPerDevPx = presContext->AppUnitsPerDevPixel();
3344
3345 // Same setup as PaintSVG: the text frames paint in device pixels, offset by
3346 // the item's position relative to the reference frame.
3347 nsPoint offset = aItem->ToReferenceFrame() - GetPosition();
3348 gfxMatrix currentMatrix = gfxMatrix::Translation(
3349 nsLayoutUtils::PointToGfxPoint(offset, auPerDevPx));
3350
3351 RefPtr<nsCaret> caret = presContext->PresShell()->GetActiveCaret();
3352 nsIFrame* caretFrame = caret->GetPaintGeometry();
3353
3354 SVGContextPaint* outerContextPaint =
3355 SVGContextPaint::GetContextPaint(GetContent());
3356
3357 // The font size WebRender rasterizes at is the CSS font size scaled by the
3358 // run transform and by the stacking context's scale.
3359 constexpr float kWebRenderFontSizeLimit = 320.0f;
3360 const gfx::MatrixScales inheritedScale = aSc.GetInheritedScale();
3361 const float scScale = std::max(inheritedScale.xScale, inheritedScale.yScale);
3362
3363 // First pass: static checks that do not depend on the fonts, so that the
3364 // common failure cases do not pay for a text painting pass.
3365 TextRenderedRunIterator checkIt(
3366 this, TextRenderedRunIterator::RenderedRunFilter::VisibleFrames);
3367 for (TextRenderedRun run = checkIt.Current(); run.mFrame;
3368 run = checkIt.Next()) {
3369 nsTextFrame* frame = run.mFrame;
3370 const nsStyleSVG* style = frame->StyleSVG();
3371
3372 if (!style->mFill.kind.IsNone() && !style->mFill.kind.IsColor()) {
3373 return Err("text fill is not a plain color");
3374 }
3375 bool paintSVGGlyphs;
3376 if (ShouldRenderAsPath(frame, outerContextPaint, paintSVGGlyphs)) {
3377 // ShouldRenderAsPath rejects translucent fills because Skia would
3378 // double blend overlapping glyphs. WebRender text has the same behavior
3379 // as HTML text with a translucent color, which is accepted here.
3380 // Decorations are excluded: nsTextFrame paints them with the fill color
3381 // but without fill-opacity.
3382 bool onlyTranslucentFill =
3383 StaticPrefs::gfx_webrender_svg_text_fill_opacity() &&
3384 style->mFill.kind.IsColor() && !style->HasStroke() &&
3385 !frame->StyleText()->HasTextShadow() &&
3386 !frame->Style()->HasTextDecorationLines();
3387 if (!onlyTranslucentFill) {
3388 return Err("text is rendered as a path");
3389 }
3390 }
3391 if (frame->StyleText()->HasTextShadow()) {
3392 return Err("text-shadow is not supported");
3393 }
3394 if (frame->StyleText()->mTextRendering ==
3395 StyleTextRendering::Geometricprecision) {
3396 return Err("text-rendering: geometricPrecision is not supported");
3397 }
3398 if (frame == caretFrame) {
3399 return Err("caret painting is not supported");
3400 }
3401 if (frame->IsSelected()) {
3402 return Err("selected text is not supported");
3403 }
3404 // Opacity on <tspan> and other intermediate ancestors is applied by
3405 // grouping in the blob path, which has no WebRender equivalent here.
3406 for (nsIFrame* ancestor = frame->GetParent(); ancestor != this;
3407 ancestor = ancestor->GetParent()) {
3408 if (ancestor->StyleEffects()->mOpacity < 1.0f) {
3409 return Err("opacity on an intermediate ancestor is not supported");
3410 }
3411 }
3412
3413 float fontSize = frame->StyleFont()->mFont.size.ToCSSPixels();
3414 float devPxPerCSSPx = presContext->CSSToDevPixelScale().scale;
3415 float runScale = std::max(1.0f, std::abs(run.mLengthAdjustScaleFactor));
3416 if (fontSize * devPxPerCSSPx * runScale * scScale >
3417 kWebRenderFontSizeLimit) {
3418 return Err("font size exceeds the WebRender glyph rasterization limit");
3419 }
3420 }
3421
3422 // Everything from here on emits WebRender commands. They are recorded under
3423 // a save point so that a failure reported by the text drawer, or a dry run,
3424 // can discard them.
3425 aBuilder.Save();
3426 bool ok = true;
3427
3428 {
3429 bool snap;
3430 nsRect itemBounds = aItem->GetBounds(aDisplayListBuilder, &snap);
3431 RefPtr<layout::TextDrawTarget> textDrawer = new layout::TextDrawTarget(
3432 aBuilder, aResources, aSc, aManager, aItem, itemBounds,
3433 /* aCallerDoesSaveRestore = */ true);
3434 if (!textDrawer->IsValid()) {
3435 aBuilder.Restore();
3436 return Err("text draw target is invalid");
3437 }
3438 gfxContext context(textDrawer);
3439 imgDrawingParams imgParams(aDisplayListBuilder->GetImageDecodeFlags());
3440 const float inheritedOpacity = aBuilder.GetInheritedOpacity();
3441
3442 // initialMatrix maps device pixels back to CSS pixels for paint servers;
3443 // only solid colors are accepted here so it only needs to be invertible.
3444 gfxMatrix initialMatrix;
3445
3446 TextRenderedRunIterator it(
3447 this, TextRenderedRunIterator::RenderedRunFilter::VisibleFrames);
3448 for (TextRenderedRun run = it.Current(); run.mFrame && ok;
3449 run = it.Next()) {
3450 nsTextFrame* frame = run.mFrame;
3451
3452 auto contextPaint = MakeRefPtr<SVGContextPaint>(
3453 textDrawer, initialMatrix, frame, outerContextPaint, imgParams);
3454 DrawMode drawMode = contextPaint->GetDrawMode();
3455 if (drawMode == DrawMode(0)) {
3456 continue;
3457 }
3458 // GLYPH_STROKE can still be set here: it covers any stroke paint other
3459 // than "none", including a stroke that paints nothing because its width
3460 // or its opacity is zero. Those are the only strokes that get this far,
3461 // since ShouldRenderAsPath sends the rest down the path rendering route
3462 // which the checks above reject. nsTextFrame strokes glyphs only for
3463 // -webkit-text-stroke, so an SVG stroke is ignored below, exactly as it
3464 // is when the blob image path paints a zero width stroke.
3465
3466 nscoord startEdge, endEdge;
3467 run.GetClipEdges(startEdge, endEdge);
3468
3469 gfxMatrix runTransform = run.GetTransformFromUserSpaceForPainting(
3470 presContext, startEdge, endEdge) *
3471 currentMatrix;
3472 if (runTransform.IsSingular()) {
3473 continue;
3474 }
3475
3476 // The text frame is painted at the origin of a reference frame that
3477 // carries the run transform, so glyph positions reach WebRender
3478 // untransformed and WebRender applies the scale and rotation.
3479 wr::WrTransformInfo transformInfo;
3480 transformInfo.transform =
3481 wr::ToLayoutTransform(gfx::Matrix4x4::From2D(ToMatrix(runTransform)));
3482 wr::StackingContextParams params;
3483 params.mTransformPtr = &transformInfo;
3484 params.reference_frame_kind = wr::WrReferenceFrameKind::Transform;
3485 params.clip =
3486 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId());
3487 params.prim_flags = aItem->BackfaceIsHidden()
3488 ? wr::PrimitiveFlags{0}
3489 : wr::PrimitiveFlags::IS_BACKFACE_VISIBLE;
3490
3491 LayoutDeviceRect runBounds =
3492 LayoutDeviceRect::FromAppUnits(frame->InkOverflowRect(), auPerDevPx);
3493 runBounds.Inflate(1);
3494
3495 Maybe<wr::WrSpatialId> spatialId = aBuilder.PushStackingContext(
3496 params, wr::ToLayoutRect(LayoutDeviceRect()),
3497 wr::RasterSpace::Screen());
3498 {
3499 Maybe<wr::SpaceAndClipChainHelper> spaceHelper;
3500 if (spatialId) {
3501 spaceHelper.emplace(aBuilder, *spatialId);
3502 }
3503 textDrawer->SetBounds(runBounds);
3504 context.SetMatrix(gfx::Matrix());
3505
3506 nsTextFrame::PaintTextParams textParams(&context);
3507 textParams.framePt = Point();
3508 textParams.dirtyRect = runBounds;
3509 textParams.contextPaint = contextPaint;
3510 textParams.state = nsTextFrame::PaintTextParams::PaintText;
3511 frame->PaintText(textParams, startEdge, endEdge, nsPoint(),
3512 /* aIsSelected = */ false, imgParams,
3513 inheritedOpacity);
3514 textDrawer->TerminateShadows();
3515 }
3516 aBuilder.PopStackingContext(spatialId.isSome());
3517 }
3518
3519 ok = !textDrawer->CheckHasUnsupportedFeatures();
3520 }
3521
3522 if (!ok) {
3523 aBuilder.Restore();
3524 return Err("text drawer could not emit all glyph commands");
3525 }
3526 if (aDryRun) {
3527 aBuilder.Restore();
3528 return Ok();
3529 }
3530
3531 aBuilder.ClearSave();
3532 return Ok();
3533}
3534
3535nsIFrame* SVGTextFrame::GetFrameForPoint(const gfxPoint& aPoint) {
3536 NS_ASSERTION(PrincipalChildList().FirstChild(), "must have a child frame")do { if (!(PrincipalChildList().FirstChild())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "must have a child frame", "PrincipalChildList().FirstChild()"
, "./../../../layout/svg/SVGTextFrame.cpp", 3536); MOZ_PretendNoReturn
(); } } while (0)
;
3537
3538 if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
3539 // Text frames inside <clipPath> will never have had ReflowSVG called on
3540 // them, so call UpdateGlyphPositioning to do this now. (Text frames
3541 // inside <mask> and other non-display containers will never need to
3542 // be hit tested.)
3543 UpdateGlyphPositioning();
3544 } else {
3545 NS_ASSERTION(!IsSubtreeDirty(), "reflow should have happened")do { if (!(!IsSubtreeDirty())) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "reflow should have happened", "!IsSubtreeDirty()", "./../../../layout/svg/SVGTextFrame.cpp"
, 3545); MOZ_PretendNoReturn(); } } while (0)
;
3546 }
3547
3548 // Hit-testing any clip-path will typically be a lot quicker than the
3549 // hit-testing of our text frames in the loop below, so we do the former up
3550 // front to avoid unnecessarily wasting cycles on the latter.
3551 if (!SVGUtils::HitTestClip(this, aPoint)) {
3552 return nullptr;
3553 }
3554
3555 nsPresContext* presContext = PresContext();
3556
3557 // Ideally we'd iterate backwards so that we can just return the first frame
3558 // that is under aPoint. In practice this will rarely matter though since it
3559 // is rare for text in/under an SVG <text> element to overlap (i.e. the first
3560 // text frame that is hit will likely be the only text frame that is hit).
3561
3562 TextRenderedRunIterator it(this);
3563 nsIFrame* hit = nullptr;
3564 for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
3565 if (SVGUtils::GetGeometryHitTestFlags(run.mFrame).isEmpty()) {
3566 continue;
3567 }
3568
3569 gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext);
3570 if (!m.Invert()) {
3571 return nullptr;
3572 }
3573
3574 gfxPoint pointInRunUserSpace = m.TransformPoint(aPoint);
3575 gfxRect frameRect =
3576 run.GetRunUserSpaceRect({TextRenderedRun::GeometryFlag::IncludeFill,
3577 TextRenderedRun::GeometryFlag::IncludeStroke})
3578 .ToThebesRect();
3579
3580 if (frameRect.Contains(pointInRunUserSpace)) {
3581 hit = run.mFrame;
3582 }
3583 }
3584 return hit;
3585}
3586
3587void SVGTextFrame::ReflowSVG() {
3588 MOZ_ASSERT(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
" (" "This call is probaby a wasteful mistake" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 3589); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
") (" "This call is probaby a wasteful mistake" ")"); do { MOZ_CrashSequence
(__null, 3589); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
3589 "This call is probaby a wasteful mistake")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
" (" "This call is probaby a wasteful mistake" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 3589); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
") (" "This call is probaby a wasteful mistake" ")"); do { MOZ_CrashSequence
(__null, 3589); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3590
3591 MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)"
" (" "ReflowSVG mechanism not designed for this" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 3592); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)"
") (" "ReflowSVG mechanism not designed for this" ")"); do {
MOZ_CrashSequence(__null, 3592); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
3592 "ReflowSVG mechanism not designed for this")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)"
" (" "ReflowSVG mechanism not designed for this" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 3592); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)"
") (" "ReflowSVG mechanism not designed for this" ")"); do {
MOZ_CrashSequence(__null, 3592); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
3593
3594 if (!SVGUtils::NeedsReflowSVG(this)) {
3595 MOZ_ASSERT(!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
| NS_STATE_SVG_POSITIONING_DIRTY))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
| NS_STATE_SVG_POSITIONING_DIRTY)))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY | NS_STATE_SVG_POSITIONING_DIRTY)"
" (" "How did this happen?" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 3597); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY | NS_STATE_SVG_POSITIONING_DIRTY)"
") (" "How did this happen?" ")"); do { MOZ_CrashSequence(__null
, 3597); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
3596 NS_STATE_SVG_POSITIONING_DIRTY),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
| NS_STATE_SVG_POSITIONING_DIRTY))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
| NS_STATE_SVG_POSITIONING_DIRTY)))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY | NS_STATE_SVG_POSITIONING_DIRTY)"
" (" "How did this happen?" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 3597); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY | NS_STATE_SVG_POSITIONING_DIRTY)"
") (" "How did this happen?" ")"); do { MOZ_CrashSequence(__null
, 3597); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
3597 "How did this happen?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
| NS_STATE_SVG_POSITIONING_DIRTY))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY
| NS_STATE_SVG_POSITIONING_DIRTY)))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY | NS_STATE_SVG_POSITIONING_DIRTY)"
" (" "How did this happen?" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 3597); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY | NS_STATE_SVG_POSITIONING_DIRTY)"
") (" "How did this happen?" ")"); do { MOZ_CrashSequence(__null
, 3597); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
3598 return;
3599 }
3600
3601 MaybeReflowAnonymousBlockChild();
3602 UpdateGlyphPositioning();
3603
3604 nsPresContext* presContext = PresContext();
3605
3606 SVGBBox r;
3607 TextRenderedRunIterator it(
3608 this, TextRenderedRunIterator::RenderedRunFilter::AllFrames);
3609 for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
3610 TextRenderedRun::GeometryFlags runFlags{
3611 TextRenderedRun::GeometryFlag::IncludeFill};
3612 if (!run.mFrame->StyleSVG()->mStroke.kind.IsNone()) {
3613 runFlags += TextRenderedRun::GeometryFlag::IncludeStroke;
3614 }
3615 r.UnionEdges(run.GetUserSpaceRect(presContext, runFlags));
3616 }
3617
3618 if (r.IsEmpty()) {
3619 mRect.SetEmpty();
3620 } else {
3621 mRect = nsLayoutUtils::RoundGfxRectToAppRect((const Rect&)r,
3622 AppUnitsPerCSSPixel());
3623
3624 // Due to rounding issues when we have a transform applied, we sometimes
3625 // don't include an additional row of pixels. For now, just inflate our
3626 // covered region.
3627 if (mLastContextScale != 0.0f) {
3628 mRect.Inflate(
3629 ceil(presContext->AppUnitsPerDevPixel() / mLastContextScale));
3630 }
3631 }
3632
3633 if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
3634 // Make sure we have our filter property (if any) before calling
3635 // FinishAndStoreOverflow (subsequent filter changes are handled off
3636 // nsChangeHint_UpdateEffects):
3637 SVGObserverUtils::UpdateEffects(this);
3638 }
3639
3640 // Now unset the various reflow bits. Do this before calling
3641 // FinishAndStoreOverflow since FinishAndStoreOverflow can require glyph
3642 // positions (to resolve transform-origin).
3643 RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
3644 NS_FRAME_HAS_DIRTY_CHILDREN);
3645
3646 nsRect overflow = nsRect(nsPoint(0, 0), mRect.Size());
3647 OverflowAreas overflowAreas(overflow, overflow);
3648 FinishAndStoreOverflow(overflowAreas, mRect.Size());
3649}
3650
3651/**
3652 * Converts SVGUtils::eBBox* flags into TextRenderedRun flags appropriate
3653 * for the specified rendered run.
3654 */
3655static TextRenderedRun::GeometryFlags TextRenderedRunFlagsForBBoxContribution(
3656 const TextRenderedRun& aRun, SVGBBoxFlags aBBoxFlags) {
3657 TextRenderedRun::GeometryFlags flags;
3658 if (aBBoxFlags.contains(SVGBBoxFlag::IncludeFillGeometry)) {
3659 flags += TextRenderedRun::GeometryFlag::IncludeFill;
3660 }
3661 if (aBBoxFlags.contains(SVGBBoxFlag::IncludeStrokeGeometry) ||
3662 (aBBoxFlags.contains(SVGBBoxFlag::IncludeStroke) &&
3663 SVGUtils::HasStroke(aRun.mFrame))) {
3664 flags += TextRenderedRun::GeometryFlag::IncludeStroke;
3665 }
3666 return flags;
3667}
3668
3669SVGBBox SVGTextFrame::GetBBoxContribution(const Matrix& aToBBoxUserspace,
3670 SVGBBoxFlags aFlags) {
3671 NS_ASSERTION(PrincipalChildList().FirstChild(), "must have a child frame")do { if (!(PrincipalChildList().FirstChild())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "must have a child frame", "PrincipalChildList().FirstChild()"
, "./../../../layout/svg/SVGTextFrame.cpp", 3671); MOZ_PretendNoReturn
(); } } while (0)
;
3672 SVGBBox bbox;
3673
3674 if (aFlags.contains(SVGBBoxFlag::ForGetClientRects)) {
3675 if (!mRect.IsEmpty()) {
3676 Rect rect = NSRectToRect(mRect, AppUnitsPerCSSPixel());
3677 bbox = aToBBoxUserspace.TransformBounds(rect);
3678 }
3679 return bbox;
3680 }
3681
3682 nsIFrame* kid = PrincipalChildList().FirstChild();
3683 if (kid && kid->IsSubtreeDirty()) {
3684 // Return an empty bbox if our kid's subtree is dirty. This may be called
3685 // in that situation, e.g. when we're building a display list after an
3686 // interrupted reflow. This can also be called during reflow before we've
3687 // been reflowed, e.g. if an earlier sibling is calling
3688 // FinishAndStoreOverflow and needs our parent's perspective matrix, which
3689 // depends on the SVG bbox contribution of this frame. In the latter
3690 // situation, when all siblings have been reflowed, the parent will compute
3691 // its perspective and rerun FinishAndStoreOverflow for all its children.
3692 return bbox;
3693 }
3694
3695 return GetSubtreeBBox(nullptr, aToBBoxUserspace, aFlags);
3696}
3697
3698SVGBBox SVGTextFrame::GetSubtreeBBox(const nsIFrame* aSubtree,
3699 const Matrix& aToBBoxUserspace,
3700 SVGBBoxFlags aFlags) {
3701 UpdateGlyphPositioning();
3702
3703 nsPresContext* presContext = PresContext();
3704
3705 SVGBBox bbox;
3706 TextRenderedRunIterator it(
3707 this, TextRenderedRunIterator::RenderedRunFilter::AllFrames, aSubtree);
3708 for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
3709 TextRenderedRun::GeometryFlags flags =
3710 TextRenderedRunFlagsForBBoxContribution(run, aFlags);
3711 gfxMatrix m = ThebesMatrix(aToBBoxUserspace);
3712 SVGBBox bboxForRun = run.GetUserSpaceRect(presContext, flags, &m);
3713 if (aFlags.contains(SVGBBoxFlag::DisregardCSSZoom)) {
3714 bboxForRun.Scale(1 / run.mFrame->Style()->EffectiveZoom().ToFloat());
3715 }
3716
3717 bbox.UnionEdges(bboxForRun);
3718 }
3719
3720 return bbox;
3721}
3722
3723//----------------------------------------------------------------------
3724// SVGTextFrame SVG DOM methods
3725
3726/**
3727 * Returns whether the specified node has any non-empty Text
3728 * beneath it.
3729 */
3730static bool HasTextContent(nsIContent* aContent) {
3731 NS_ASSERTION(aContent, "expected non-null aContent")do { if (!(aContent)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "expected non-null aContent"
, "aContent", "./../../../layout/svg/SVGTextFrame.cpp", 3731)
; MOZ_PretendNoReturn(); } } while (0)
;
3732
3733 TextNodeIterator it(aContent);
3734 for (Text* text = it.GetCurrent(); text; text = it.GetNext()) {
3735 if (text->TextLength() != 0) {
3736 return true;
3737 }
3738 }
3739 return false;
3740}
3741
3742/**
3743 * Returns the number of DOM characters beneath the specified node.
3744 */
3745static uint32_t GetTextContentLength(nsIContent* aContent) {
3746 NS_ASSERTION(aContent, "expected non-null aContent")do { if (!(aContent)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "expected non-null aContent"
, "aContent", "./../../../layout/svg/SVGTextFrame.cpp", 3746)
; MOZ_PretendNoReturn(); } } while (0)
;
3747
3748 uint32_t length = 0;
3749 TextNodeIterator it(aContent);
3750 for (Text* text = it.GetCurrent(); text; text = it.GetNext()) {
3751 length += text->TextLength();
3752 }
3753 return length;
3754}
3755
3756int32_t SVGTextFrame::ConvertTextElementCharIndexToAddressableIndex(
3757 int32_t aIndex, dom::SVGTextContentElement* aElement) {
3758 CharIterator it(this, CharIterator::CharacterFilter::Original, aElement);
3759 if (!it.AdvanceToSubtree()) {
3760 return -1;
3761 }
3762 int32_t result = 0;
3763 int32_t textElementCharIndex;
3764 while (!it.AtEnd() && it.IsWithinSubtree()) {
3765 bool addressable = !it.IsOriginalCharUnaddressable();
3766 textElementCharIndex = it.TextElementCharIndex();
3767 it.Next();
3768 uint32_t delta = it.TextElementCharIndex() - textElementCharIndex;
3769 aIndex -= delta;
3770 if (addressable) {
3771 if (aIndex < 0) {
3772 return result;
3773 }
3774 result += delta;
3775 }
3776 }
3777 return -1;
3778}
3779
3780/**
3781 * Implements the SVG DOM GetNumberOfChars method for the specified
3782 * text content element.
3783 */
3784uint32_t SVGTextFrame::GetNumberOfChars(dom::SVGTextContentElement* aElement) {
3785 nsIFrame* kid = PrincipalChildList().FirstChild();
3786 if (kid->IsSubtreeDirty()) {
3787 // We're never reflowed if we're under a non-SVG element that is
3788 // never reflowed (such as the HTML 'caption' element).
3789 return 0;
3790 }
3791
3792 UpdateGlyphPositioning();
3793
3794 uint32_t n = 0;
3795 CharIterator it(this, CharIterator::CharacterFilter::Addressable, aElement);
3796 if (it.AdvanceToSubtree()) {
3797 while (!it.AtEnd() && it.IsWithinSubtree()) {
3798 n++;
3799 it.Next();
3800 }
3801 }
3802 return n;
3803}
3804
3805/**
3806 * Implements the SVG DOM GetComputedTextLength method for the specified
3807 * text child element.
3808 */
3809float SVGTextFrame::GetComputedTextLength(
3810 dom::SVGTextContentElement* aElement) {
3811 nsIFrame* kid = PrincipalChildList().FirstChild();
3812 if (kid->IsSubtreeDirty()) {
3813 // We're never reflowed if we're under a non-SVG element that is
3814 // never reflowed (such as the HTML 'caption' element).
3815 //
3816 // If we ever decide that we need to return accurate values here,
3817 // we could do similar work to GetSubStringLength.
3818 return 0;
3819 }
3820
3821 UpdateGlyphPositioning();
3822
3823 float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
3824 PresContext()->AppUnitsPerDevPixel());
3825
3826 nscoord length = 0;
3827 TextRenderedRunIterator it(
3828 this, TextRenderedRunIterator::RenderedRunFilter::AllFrames, aElement);
3829 for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
3830 length +=
3831 run.GetAdvanceWidth() / run.mFrame->Style()->EffectiveZoom().ToFloat();
3832 }
3833
3834 return PresContext()->AppUnitsToGfxUnits(length) * cssPxPerDevPx *
3835 mLengthAdjustScaleFactor / mFontSizeScaleFactor;
3836}
3837
3838/**
3839 * Implements the SVG DOM SelectSubString method for the specified
3840 * text content element.
3841 */
3842void SVGTextFrame::SelectSubString(dom::SVGTextContentElement* aElement,
3843 uint32_t charnum, uint32_t nchars,
3844 ErrorResult& aRv) {
3845 nsIFrame* kid = PrincipalChildList().FirstChild();
3846 if (kid->IsSubtreeDirty()) {
3847 // We're never reflowed if we're under a non-SVG element that is
3848 // never reflowed (such as the HTML 'caption' element).
3849 // XXXbz Should this just return without throwing like the no-frame case?
3850 aRv.ThrowInvalidStateError("No layout information available for SVG text");
3851 return;
3852 }
3853
3854 UpdateGlyphPositioning();
3855
3856 RefPtr<nsIContent> content;
3857
3858 // Ensure the destructor of CharIterator runs before calling HandleClick.
3859 {
3860 // Convert charnum/nchars from addressable characters relative to
3861 // aElement to global character indices.
3862 CharIterator chit(this, CharIterator::CharacterFilter::Addressable,
3863 aElement);
3864 if (!chit.AdvanceToSubtree() || !chit.Next(charnum) ||
3865 chit.IsAfterSubtree()) {
3866 aRv.ThrowIndexSizeError("Character index out of range");
3867 return;
3868 }
3869 charnum = chit.TextElementCharIndex();
3870 content = chit.GetTextFrame()->GetContent();
3871 chit.NextWithinSubtree(nchars);
3872 nchars = chit.TextElementCharIndex() - charnum;
3873 }
3874
3875 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
3876
3877 frameSelection->HandleClick(content, charnum, charnum + nchars,
3878 nsFrameSelection::FocusMode::kCollapseToNewPoint,
3879 CaretAssociationHint::Before);
3880}
3881
3882/**
3883 * For some content we cannot (or currently cannot) compute the length
3884 * without reflowing. In those cases we need to fall back to using
3885 * GetSubStringLengthSlowFallback.
3886 *
3887 * We fall back for textPath since we need glyph positioning in order to
3888 * tell if any characters should be ignored due to having fallen off the
3889 * end of the textPath.
3890 *
3891 * We fall back for bidi because GetTrimmedOffsets does not produce the
3892 * correct results for bidi continuations when passed aPostReflow = false.
3893 * XXX It may be possible to determine which continuations to trim from (and
3894 * which sides), but currently we don't do that. It would require us to
3895 * identify the visual (rather than logical) start and end of the line, to
3896 * avoid trimming at line-internal frame boundaries. Maybe nsBidiPresUtils
3897 * methods like GetFrameToRightOf and GetFrameToLeftOf would help?
3898 *
3899 */
3900bool SVGTextFrame::RequiresSlowFallbackForSubStringLength() {
3901 TextFrameIterator frameIter(this);
3902 for (nsTextFrame* frame = frameIter.GetCurrent(); frame;
3903 frame = frameIter.GetNext()) {
3904 if (frameIter.TextPathFrame() || frame->GetNextContinuation()) {
3905 return true;
3906 }
3907 }
3908 return false;
3909}
3910
3911/**
3912 * Implements the SVG DOM GetSubStringLength method for the specified
3913 * text content element.
3914 */
3915float SVGTextFrame::GetSubStringLengthFastPath(
3916 dom::SVGTextContentElement* aElement, uint32_t charnum, uint32_t nchars,
3917 ErrorResult& aRv) {
3918 MOZ_ASSERT(!RequiresSlowFallbackForSubStringLength())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!RequiresSlowFallbackForSubStringLength())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!RequiresSlowFallbackForSubStringLength()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!RequiresSlowFallbackForSubStringLength()"
, "./../../../layout/svg/SVGTextFrame.cpp", 3918); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!RequiresSlowFallbackForSubStringLength()"
")"); do { MOZ_CrashSequence(__null, 3918); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3919
3920 // We only need our text correspondence to be up to date (no need to call
3921 // UpdateGlyphPositioning).
3922 TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
3923
3924 // Convert charnum/nchars from addressable characters relative to
3925 // aElement to global character indices.
3926 CharIterator chit(this, CharIterator::CharacterFilter::Addressable, aElement,
3927 /* aPostReflow */ false);
3928 if (!chit.AdvanceToSubtree() || !chit.Next(charnum) ||
3929 chit.IsAfterSubtree()) {
3930 aRv.ThrowIndexSizeError("Character index out of range");
3931 return 0;
3932 }
3933
3934 // We do this after the ThrowIndexSizeError() bit so JS calls correctly throw
3935 // when necessary.
3936 if (nchars == 0) {
3937 return 0.0f;
3938 }
3939
3940 charnum = chit.TextElementCharIndex();
3941 chit.NextWithinSubtree(nchars);
3942 nchars = chit.TextElementCharIndex() - charnum;
3943
3944 // Sum of the substring advances.
3945 nscoord textLength = 0;
3946
3947 TextFrameIterator frit(this); // aSubtree = nullptr
3948
3949 // Index of the first non-skipped char in the frame, and of a subsequent char
3950 // that we're interested in. Both are relative to the index of the first
3951 // non-skipped char in the ancestor <text> element.
3952 uint32_t frameStartTextElementCharIndex = 0;
3953 uint32_t textElementCharIndex;
3954
3955 for (nsTextFrame* frame = frit.GetCurrent(); frame; frame = frit.GetNext()) {
3956 frameStartTextElementCharIndex += frit.UndisplayedCharacters();
3957 textElementCharIndex = frameStartTextElementCharIndex;
3958
3959 // Offset into frame's Text:
3960 const uint32_t untrimmedOffset = frame->GetContentOffset();
3961 const uint32_t untrimmedLength = frame->GetContentEnd() - untrimmedOffset;
3962
3963 // Trim the offset/length to remove any leading/trailing white space.
3964 uint32_t trimmedOffset = untrimmedOffset;
3965 uint32_t trimmedLength = untrimmedLength;
3966 nsTextFrame::TrimmedOffsets trimmedOffsets = frame->GetTrimmedOffsets(
3967 frame->CharacterDataBuffer(),
3968 nsTextFrame::TrimmedOffsetFlags::NotPostReflow);
3969 TrimOffsets(trimmedOffset, trimmedLength, trimmedOffsets);
3970
3971 textElementCharIndex += trimmedOffset - untrimmedOffset;
3972
3973 if (textElementCharIndex >= charnum + nchars) {
3974 break; // we're past the end of the substring
3975 }
3976
3977 uint32_t offset = textElementCharIndex;
3978
3979 // Intersect the substring we are interested in with the range covered by
3980 // the nsTextFrame.
3981 IntersectInterval(offset, trimmedLength, charnum, nchars);
3982
3983 if (trimmedLength != 0) {
3984 // Convert offset into an index into the frame.
3985 offset += trimmedOffset - textElementCharIndex;
3986
3987 gfxSkipCharsIterator it = frame->EnsureTextRun(nsTextFrame::eInflated);
3988 gfxTextRun* textRun = frame->GetTextRun(nsTextFrame::eInflated);
3989 auto& provider = PropertyProviderFor(frame);
3990
3991 Range range = ConvertOriginalToSkipped(it, offset, trimmedLength);
3992
3993 // Accumulate the advance.
3994 textLength += textRun->GetAdvanceWidth(range, &provider) /
3995 frame->Style()->EffectiveZoom().ToFloat();
3996 }
3997
3998 // Advance, ready for next call:
3999 frameStartTextElementCharIndex += untrimmedLength;
4000 }
4001
4002 nsPresContext* presContext = PresContext();
4003 float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
4004 presContext->AppUnitsPerDevPixel());
4005
4006 return presContext->AppUnitsToGfxUnits(textLength) * cssPxPerDevPx /
4007 mFontSizeScaleFactor;
4008}
4009
4010float SVGTextFrame::GetSubStringLengthSlowFallback(
4011 dom::SVGTextContentElement* aElement, uint32_t charnum, uint32_t nchars,
4012 ErrorResult& aRv) {
4013 UpdateGlyphPositioning();
4014
4015 // Convert charnum/nchars from addressable characters relative to
4016 // aElement to global character indices.
4017 CharIterator chit(this, CharIterator::CharacterFilter::Addressable, aElement);
4018 if (!chit.AdvanceToSubtree() || !chit.Next(charnum) ||
4019 chit.IsAfterSubtree()) {
4020 aRv.ThrowIndexSizeError("Character index out of range");
4021 return 0;
4022 }
4023
4024 if (nchars == 0) {
4025 return 0.0f;
4026 }
4027
4028 charnum = chit.TextElementCharIndex();
4029 chit.NextWithinSubtree(nchars);
4030 nchars = chit.TextElementCharIndex() - charnum;
4031
4032 // Find each rendered run that intersects with the range defined
4033 // by charnum/nchars.
4034 nscoord textLength = 0;
4035 TextRenderedRunIterator runIter(
4036 this, TextRenderedRunIterator::RenderedRunFilter::AllFrames);
4037 TextRenderedRun run = runIter.Current();
4038 while (run.mFrame) {
4039 // If this rendered run is past the substring we are interested in, we
4040 // are done.
4041 uint32_t offset = run.mTextElementCharIndex;
4042 if (offset >= charnum + nchars) {
4043 break;
4044 }
4045
4046 // Intersect the substring we are interested in with the range covered by
4047 // the rendered run.
4048 uint32_t length = run.mTextFrameContentLength;
4049 IntersectInterval(offset, length, charnum, nchars);
4050
4051 if (length != 0) {
4052 // Convert offset into an index into the frame.
4053 offset += run.mTextFrameContentOffset - run.mTextElementCharIndex;
4054
4055 gfxSkipCharsIterator it =
4056 run.mFrame->EnsureTextRun(nsTextFrame::eInflated);
4057 gfxTextRun* textRun = run.mFrame->GetTextRun(nsTextFrame::eInflated);
4058 auto& provider = PropertyProviderFor(run.mFrame);
4059
4060 Range range = ConvertOriginalToSkipped(it, offset, length);
4061
4062 // Accumulate the advance.
4063 textLength += textRun->GetAdvanceWidth(range, &provider) /
4064 run.mFrame->Style()->EffectiveZoom().ToFloat();
4065 }
4066
4067 run = runIter.Next();
4068 }
4069
4070 nsPresContext* presContext = PresContext();
4071 float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
4072 presContext->AppUnitsPerDevPixel());
4073
4074 return presContext->AppUnitsToGfxUnits(textLength) * cssPxPerDevPx /
4075 mFontSizeScaleFactor;
4076}
4077
4078/**
4079 * Implements the SVG DOM GetCharNumAtPosition method for the specified
4080 * text content element.
4081 */
4082int32_t SVGTextFrame::GetCharNumAtPosition(dom::SVGTextContentElement* aElement,
4083 const gfx::Point& aPoint) {
4084 nsIFrame* kid = PrincipalChildList().FirstChild();
4085 if (kid->IsSubtreeDirty()) {
4086 // We're never reflowed if we're under a non-SVG element that is
4087 // never reflowed (such as the HTML 'caption' element).
4088 return -1;
4089 }
4090
4091 UpdateGlyphPositioning();
4092
4093 nsPresContext* context = PresContext();
4094
4095 gfxPoint p = ThebesPoint(aPoint) * dom::UserSpaceMetrics::GetZoom(aElement);
4096
4097 int32_t result = -1;
4098
4099 TextRenderedRunIterator it(
4100 this, TextRenderedRunIterator::RenderedRunFilter::AllFrames, aElement);
4101 for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
4102 // Hit test this rendered run. Later runs will override earlier ones.
4103 int32_t index = run.GetCharNumAtPosition(context, p);
4104 if (index != -1) {
4105 result = index + run.mTextElementCharIndex;
4106 }
4107 }
4108
4109 if (result == -1) {
4110 return result;
4111 }
4112
4113 return ConvertTextElementCharIndexToAddressableIndex(result, aElement);
4114}
4115
4116/**
4117 * Implements the SVG DOM GetStartPositionOfChar method for the specified
4118 * text content element.
4119 */
4120already_AddRefed<DOMSVGPoint> SVGTextFrame::GetStartPositionOfChar(
4121 dom::SVGTextContentElement* aElement, uint32_t aCharNum, ErrorResult& aRv) {
4122 nsIFrame* kid = PrincipalChildList().FirstChild();
4123 if (kid->IsSubtreeDirty()) {
4124 // We're never reflowed if we're under a non-SVG element that is
4125 // never reflowed (such as the HTML 'caption' element).
4126 aRv.ThrowInvalidStateError("No layout information available for SVG text");
4127 return nullptr;
4128 }
4129
4130 UpdateGlyphPositioning();
4131
4132 CharIterator it(this, CharIterator::CharacterFilter::Addressable, aElement);
4133 if (!it.AdvanceToSubtree() || !it.Next(aCharNum) || it.IsAfterSubtree()) {
4134 aRv.ThrowIndexSizeError("Character index out of range");
4135 return nullptr;
4136 }
4137
4138 // We need to return the start position of the whole glyph.
4139 uint32_t startIndex = it.GlyphStartTextElementCharIndex();
4140
4141 return MakeAndAddRef<DOMSVGPoint>(
4142 ToPoint(mPositions[startIndex].mPosition) /
4143 it.GetTextFrame()->Style()->EffectiveZoom().ToFloat());
4144}
4145
4146/**
4147 * Returns the advance of the entire glyph whose starting character is at
4148 * aTextElementCharIndex.
4149 *
4150 * aIterator, if provided, must be a CharIterator that already points to
4151 * aTextElementCharIndex that is restricted to aContent and is using
4152 * filter mode CharacterFilter::Addressable.
4153 */
4154static gfxFloat GetGlyphAdvance(SVGTextFrame* aFrame,
4155 dom::SVGTextContentElement* aElement,
4156 uint32_t aTextElementCharIndex,
4157 CharIterator* aIterator) {
4158 MOZ_ASSERT(!aIterator || (aIterator->Filter() ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aIterator || (aIterator->Filter() == CharIterator
::CharacterFilter::Addressable && aIterator->GetSubtree
() == aElement && aIterator->GlyphStartTextElementCharIndex
() == aTextElementCharIndex))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aIterator || (aIterator->
Filter() == CharIterator::CharacterFilter::Addressable &&
aIterator->GetSubtree() == aElement && aIterator->
GlyphStartTextElementCharIndex() == aTextElementCharIndex))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
" (" "Invalid aIterator" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 4163); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
") (" "Invalid aIterator" ")"); do { MOZ_CrashSequence(__null
, 4163); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
4159 CharIterator::CharacterFilter::Addressable &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aIterator || (aIterator->Filter() == CharIterator
::CharacterFilter::Addressable && aIterator->GetSubtree
() == aElement && aIterator->GlyphStartTextElementCharIndex
() == aTextElementCharIndex))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aIterator || (aIterator->
Filter() == CharIterator::CharacterFilter::Addressable &&
aIterator->GetSubtree() == aElement && aIterator->
GlyphStartTextElementCharIndex() == aTextElementCharIndex))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
" (" "Invalid aIterator" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 4163); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
") (" "Invalid aIterator" ")"); do { MOZ_CrashSequence(__null
, 4163); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
4160 aIterator->GetSubtree() == aElement &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aIterator || (aIterator->Filter() == CharIterator
::CharacterFilter::Addressable && aIterator->GetSubtree
() == aElement && aIterator->GlyphStartTextElementCharIndex
() == aTextElementCharIndex))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aIterator || (aIterator->
Filter() == CharIterator::CharacterFilter::Addressable &&
aIterator->GetSubtree() == aElement && aIterator->
GlyphStartTextElementCharIndex() == aTextElementCharIndex))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
" (" "Invalid aIterator" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 4163); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
") (" "Invalid aIterator" ")"); do { MOZ_CrashSequence(__null
, 4163); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
4161 aIterator->GlyphStartTextElementCharIndex() ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aIterator || (aIterator->Filter() == CharIterator
::CharacterFilter::Addressable && aIterator->GetSubtree
() == aElement && aIterator->GlyphStartTextElementCharIndex
() == aTextElementCharIndex))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aIterator || (aIterator->
Filter() == CharIterator::CharacterFilter::Addressable &&
aIterator->GetSubtree() == aElement && aIterator->
GlyphStartTextElementCharIndex() == aTextElementCharIndex))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
" (" "Invalid aIterator" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 4163); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
") (" "Invalid aIterator" ")"); do { MOZ_CrashSequence(__null
, 4163); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
4162 aTextElementCharIndex),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aIterator || (aIterator->Filter() == CharIterator
::CharacterFilter::Addressable && aIterator->GetSubtree
() == aElement && aIterator->GlyphStartTextElementCharIndex
() == aTextElementCharIndex))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aIterator || (aIterator->
Filter() == CharIterator::CharacterFilter::Addressable &&
aIterator->GetSubtree() == aElement && aIterator->
GlyphStartTextElementCharIndex() == aTextElementCharIndex))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
" (" "Invalid aIterator" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 4163); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
") (" "Invalid aIterator" ")"); do { MOZ_CrashSequence(__null
, 4163); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
4163 "Invalid aIterator")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aIterator || (aIterator->Filter() == CharIterator
::CharacterFilter::Addressable && aIterator->GetSubtree
() == aElement && aIterator->GlyphStartTextElementCharIndex
() == aTextElementCharIndex))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aIterator || (aIterator->
Filter() == CharIterator::CharacterFilter::Addressable &&
aIterator->GetSubtree() == aElement && aIterator->
GlyphStartTextElementCharIndex() == aTextElementCharIndex))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
" (" "Invalid aIterator" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 4163); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aIterator || (aIterator->Filter() == CharIterator::CharacterFilter::Addressable && aIterator->GetSubtree() == aElement && aIterator->GlyphStartTextElementCharIndex() == aTextElementCharIndex)"
") (" "Invalid aIterator" ")"); do { MOZ_CrashSequence(__null
, 4163); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
4164
4165 Maybe<CharIterator> newIterator;
4166 CharIterator* it = aIterator;
4167 if (!it) {
4168 newIterator.emplace(aFrame, CharIterator::CharacterFilter::Addressable,
4169 aElement);
4170 if (!newIterator->AdvanceToSubtree()) {
4171 MOZ_ASSERT_UNREACHABLE("Invalid aElement")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Invalid aElement" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 4171); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Invalid aElement" ")"); do { MOZ_CrashSequence
(__null, 4171); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4172 return 0.0;
4173 }
4174 it = newIterator.ptr();
4175 }
4176
4177 while (it->GlyphStartTextElementCharIndex() != aTextElementCharIndex) {
4178 if (!it->Next()) {
4179 MOZ_ASSERT_UNREACHABLE("Invalid aTextElementCharIndex")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Invalid aTextElementCharIndex" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 4179); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Invalid aTextElementCharIndex" ")"
); do { MOZ_CrashSequence(__null, 4179); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4180 return 0.0;
4181 }
4182 }
4183
4184 if (it->AtEnd()) {
4185 MOZ_ASSERT_UNREACHABLE("Invalid aTextElementCharIndex")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Invalid aTextElementCharIndex" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 4185); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Invalid aTextElementCharIndex" ")"
); do { MOZ_CrashSequence(__null, 4185); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4186 return 0.0;
4187 }
4188
4189 nsPresContext* presContext = aFrame->PresContext();
4190 gfxFloat advance = 0.0;
4191
4192 for (;;) {
4193 advance += it->GetAdvance(presContext);
4194 if (!it->Next() ||
4195 it->GlyphStartTextElementCharIndex() != aTextElementCharIndex) {
4196 break;
4197 }
4198 }
4199
4200 return advance;
4201}
4202
4203/**
4204 * Implements the SVG DOM GetEndPositionOfChar method for the specified
4205 * text content element.
4206 */
4207already_AddRefed<DOMSVGPoint> SVGTextFrame::GetEndPositionOfChar(
4208 dom::SVGTextContentElement* aElement, uint32_t aCharNum, ErrorResult& aRv) {
4209 nsIFrame* kid = PrincipalChildList().FirstChild();
4210 if (kid->IsSubtreeDirty()) {
4211 // We're never reflowed if we're under a non-SVG element that is
4212 // never reflowed (such as the HTML 'caption' element).
4213 aRv.ThrowInvalidStateError("No layout information available for SVG text");
4214 return nullptr;
4215 }
4216
4217 UpdateGlyphPositioning();
4218
4219 CharIterator it(this, CharIterator::CharacterFilter::Addressable, aElement);
4220 if (!it.AdvanceToSubtree() || !it.Next(aCharNum) || it.IsAfterSubtree()) {
4221 aRv.ThrowIndexSizeError("Character index out of range");
4222 return nullptr;
4223 }
4224
4225 RefPtr<const gfxTextRun> textRun = it.TextRun();
4226
4227 // We need to return the end position of the whole glyph.
4228 uint32_t startIndex = it.GlyphStartTextElementCharIndex();
4229 float zoom = it.GetTextFrame()->Style()->EffectiveZoom().ToFloat();
4230
4231 // Get the advance of the glyph.
4232 gfxFloat advance =
4233 GetGlyphAdvance(this, aElement, startIndex,
4234 it.IsClusterAndLigatureGroupStart() ? &it : nullptr) /
4235 mFontSizeScaleFactor;
4236 if (textRun->IsInlineReversed()) {
4237 advance = -advance;
4238 }
4239 Point p = textRun->IsVertical() ? Point(0, advance) : Point(advance, 0);
4240
4241 // The end position is the start position plus the advance in the direction
4242 // of the glyph's rotation.
4243 Matrix m = Matrix::Rotation(mPositions[startIndex].mAngle) *
4244 Matrix::Translation(ToPoint(mPositions[startIndex].mPosition));
4245
4246 return MakeAndAddRef<DOMSVGPoint>(m.TransformPoint(p) / zoom);
4247}
4248
4249/**
4250 * Implements the SVG DOM GetExtentOfChar method for the specified
4251 * text content element.
4252 */
4253already_AddRefed<SVGRect> SVGTextFrame::GetExtentOfChar(
4254 dom::SVGTextContentElement* aElement, uint32_t aCharNum, ErrorResult& aRv) {
4255 nsIFrame* kid = PrincipalChildList().FirstChild();
4256 if (kid->IsSubtreeDirty()) {
4257 // We're never reflowed if we're under a non-SVG element that is
4258 // never reflowed (such as the HTML 'caption' element).
4259 aRv.ThrowInvalidStateError("No layout information available for SVG text");
4260 return nullptr;
4261 }
4262
4263 UpdateGlyphPositioning();
4264
4265 // Search for the character whose addressable index is aCharNum.
4266 CharIterator it(this, CharIterator::CharacterFilter::Addressable, aElement);
4267 if (!it.AdvanceToSubtree() || !it.Next(aCharNum) || it.IsAfterSubtree()) {
4268 aRv.ThrowIndexSizeError("Character index out of range");
4269 return nullptr;
4270 }
4271
4272 nsPresContext* presContext = PresContext();
4273 float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
4274 presContext->AppUnitsPerDevPixel());
4275
4276 nsTextFrame* textFrame = it.GetTextFrame();
4277 uint32_t startIndex = it.GlyphStartTextElementCharIndex();
4278 RefPtr<const gfxTextRun> textRun = it.TextRun();
4279
4280 // Get the glyph advance.
4281 gfxFloat advance =
4282 GetGlyphAdvance(this, aElement, startIndex,
4283 it.IsClusterAndLigatureGroupStart() ? &it : nullptr);
4284 gfxFloat x = textRun->IsInlineReversed() ? -advance : 0.0;
4285
4286 // The ascent and descent gives the height of the glyph.
4287 gfxFloat ascent, descent;
4288 GetAscentAndDescentInAppUnits(textFrame, ascent, descent);
4289
4290 // The horizontal extent is the origin of the glyph plus the advance
4291 // in the direction of the glyph's rotation.
4292 gfxMatrix m;
4293 m.PreTranslate(mPositions[startIndex].mPosition);
4294 m.PreRotate(mPositions[startIndex].mAngle);
4295 m.PreScale(1 / mFontSizeScaleFactor, 1 / mFontSizeScaleFactor);
4296
4297 nscoord baseline = GetBaselinePosition(
4298 textFrame, textRun, it.DominantBaseline(), mFontSizeScaleFactor);
4299
4300 gfxRect glyphRect;
4301 if (textRun->IsVertical()) {
4302 glyphRect = gfxRect(
4303 -presContext->AppUnitsToGfxUnits(baseline) * cssPxPerDevPx, x,
4304 presContext->AppUnitsToGfxUnits(ascent + descent) * cssPxPerDevPx,
4305 advance);
4306 } else {
4307 glyphRect = gfxRect(
4308 x, -presContext->AppUnitsToGfxUnits(baseline) * cssPxPerDevPx, advance,
4309 presContext->AppUnitsToGfxUnits(ascent + descent) * cssPxPerDevPx);
4310 }
4311
4312 // Transform the glyph's rect into user space.
4313 gfxRect r = m.TransformBounds(glyphRect);
4314 r.Scale(1 / textFrame->Style()->EffectiveZoom().ToFloat());
4315
4316 return MakeAndAddRef<SVGRect>(aElement, ToRect(r));
4317}
4318
4319/**
4320 * Implements the SVG DOM GetRotationOfChar method for the specified
4321 * text content element.
4322 */
4323float SVGTextFrame::GetRotationOfChar(dom::SVGTextContentElement* aElement,
4324 uint32_t aCharNum, ErrorResult& aRv) {
4325 nsIFrame* kid = PrincipalChildList().FirstChild();
4326 if (kid->IsSubtreeDirty()) {
4327 // We're never reflowed if we're under a non-SVG element that is
4328 // never reflowed (such as the HTML 'caption' element).
4329 aRv.ThrowInvalidStateError("No layout information available for SVG text");
4330 return 0;
4331 }
4332
4333 UpdateGlyphPositioning();
4334
4335 CharIterator it(this, CharIterator::CharacterFilter::Addressable, aElement);
4336 if (!it.AdvanceToSubtree() || !it.Next(aCharNum) || it.IsAfterSubtree()) {
4337 aRv.ThrowIndexSizeError("Character index out of range");
4338 return 0;
4339 }
4340
4341 // We need to account for the glyph's underlying orientation.
4342 const gfxTextRun::GlyphRun& glyphRun = it.GlyphRun();
4343 int32_t glyphOrientation =
4344 90 * (glyphRun.IsSidewaysRight() - glyphRun.IsSidewaysLeft());
4345
4346 return mPositions[it.TextElementCharIndex()].mAngle * 180.0 /
4347 std::numbers::pi +
4348 glyphOrientation;
4349}
4350
4351//----------------------------------------------------------------------
4352// SVGTextFrame text layout methods
4353
4354/**
4355 * Given the character position array before values have been filled in
4356 * to any unspecified positions, and an array of dx/dy values, returns whether
4357 * a character at a given index should start a new rendered run.
4358 *
4359 * @param aPositions The array of character positions before unspecified
4360 * positions have been filled in and dx/dy values have been added to them.
4361 * @param aDeltas The array of dx/dy values.
4362 * @param aIndex The character index in question.
4363 */
4364static bool ShouldStartRunAtIndex(const nsTArray<CharPosition>& aPositions,
4365 const nsTArray<gfxPoint>& aDeltas,
4366 uint32_t aIndex) {
4367 if (aIndex == 0) {
4368 return true;
4369 }
4370
4371 if (aIndex < aPositions.Length()) {
4372 // If an explicit x or y value was given, start a new run.
4373 if (aPositions[aIndex].IsXSpecified() ||
4374 aPositions[aIndex].IsYSpecified()) {
4375 return true;
4376 }
4377
4378 // If a non-zero rotation was given, or the previous character had a non-
4379 // zero rotation, start a new run.
4380 if ((aPositions[aIndex].IsAngleSpecified() &&
4381 aPositions[aIndex].mAngle != 0.0f) ||
4382 (aPositions[aIndex - 1].IsAngleSpecified() &&
4383 (aPositions[aIndex - 1].mAngle != 0.0f))) {
4384 return true;
4385 }
4386 }
4387
4388 if (aIndex < aDeltas.Length()) {
4389 // If a non-zero dx or dy value was given, start a new run.
4390 if (aDeltas[aIndex].x != 0.0 || aDeltas[aIndex].y != 0.0) {
4391 return true;
4392 }
4393 }
4394
4395 return false;
4396}
4397
4398bool SVGTextFrame::ResolvePositionsForNode(nsIContent* aContent,
4399 uint32_t& aIndex, bool aInTextPath,
4400 bool& aForceStartOfChunk,
4401 nsTArray<gfxPoint>& aDeltas) {
4402 if (aContent->IsText()) {
4403 // We found a text node.
4404 uint32_t length = aContent->AsText()->TextLength();
4405 if (length) {
4406 uint32_t end = aIndex + length;
4407 if (MOZ_UNLIKELY(end > mPositions.Length())(__builtin_expect(!!(end > mPositions.Length()), 0))) {
4408 MOZ_ASSERT_UNREACHABLE(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"length of mPositions does not match characters " "found by iterating content"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4410); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "length of mPositions does not match characters "
"found by iterating content" ")"); do { MOZ_CrashSequence(__null
, 4410); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
4409 "length of mPositions does not match characters "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"length of mPositions does not match characters " "found by iterating content"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4410); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "length of mPositions does not match characters "
"found by iterating content" ")"); do { MOZ_CrashSequence(__null
, 4410); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
4410 "found by iterating content")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"length of mPositions does not match characters " "found by iterating content"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4410); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "length of mPositions does not match characters "
"found by iterating content" ")"); do { MOZ_CrashSequence(__null
, 4410); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
4411 return false;
4412 }
4413 if (aForceStartOfChunk) {
4414 // Note this character as starting a new anchored chunk.
4415 mPositions[aIndex].mStartOfChunk = true;
4416 aForceStartOfChunk = false;
4417 }
4418 while (aIndex < end) {
4419 // Record whether each of these characters should start a new rendered
4420 // run. That is always the case for characters on a text path.
4421 //
4422 // Run boundaries due to rotate="" values are handled in
4423 // DoGlyphPositioning.
4424 if (aInTextPath || ShouldStartRunAtIndex(mPositions, aDeltas, aIndex)) {
4425 mPositions[aIndex].mRunBoundary = true;
4426 }
4427 aIndex++;
4428 }
4429 }
4430 return true;
4431 }
4432
4433 // Skip past elements that aren't text content elements.
4434 if (!IsTextContentElement(aContent)) {
4435 return true;
4436 }
4437
4438 if (aContent->IsSVGElement(nsGkAtoms::textPath)) {
4439 // Any ‘y’ attributes on horizontal <textPath> elements are ignored.
4440 // Similarly, for vertical <texPath>s x attributes are ignored.
4441 // <textPath> elements behave as if they have x="0" y="0" on them, but only
4442 // if there is not a value for the non-ignored coordinate that got inherited
4443 // from a parent. We skip this if there is no text content, so that empty
4444 // <textPath>s don't interrupt the layout of text in the parent element.
4445 if (HasTextContent(aContent)) {
4446 if (MOZ_UNLIKELY(aIndex >= mPositions.Length())(__builtin_expect(!!(aIndex >= mPositions.Length()), 0))) {
4447 MOZ_ASSERT_UNREACHABLE(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"length of mPositions does not match characters " "found by iterating content"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4449); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "length of mPositions does not match characters "
"found by iterating content" ")"); do { MOZ_CrashSequence(__null
, 4449); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
4448 "length of mPositions does not match characters "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"length of mPositions does not match characters " "found by iterating content"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4449); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "length of mPositions does not match characters "
"found by iterating content" ")"); do { MOZ_CrashSequence(__null
, 4449); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
4449 "found by iterating content")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"length of mPositions does not match characters " "found by iterating content"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4449); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "length of mPositions does not match characters "
"found by iterating content" ")"); do { MOZ_CrashSequence(__null
, 4449); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
4450 return false;
4451 }
4452 bool vertical = GetWritingMode().IsVertical();
4453 if (vertical || !mPositions[aIndex].IsXSpecified()) {
4454 mPositions[aIndex].mPosition.x = 0.0;
4455 }
4456 if (!vertical || !mPositions[aIndex].IsYSpecified()) {
4457 mPositions[aIndex].mPosition.y = 0.0;
4458 }
4459 mPositions[aIndex].mStartOfChunk = true;
4460 }
4461 } else if (!aContent->IsSVGElement(nsGkAtoms::a)) {
4462 MOZ_ASSERT(aContent->IsSVGElement())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContent->IsSVGElement())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aContent->IsSVGElement())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("aContent->IsSVGElement()"
, "./../../../layout/svg/SVGTextFrame.cpp", 4462); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aContent->IsSVGElement()" ")"); do { MOZ_CrashSequence
(__null, 4462); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4463
4464 // We have a text content element that can have x/y/dx/dy/rotate attributes.
4465 SVGElement* element = static_cast<SVGElement*>(aContent);
4466
4467 // Get x, y, dx, dy.
4468 SVGUserUnitList x, y, dx, dy;
4469 element->GetAnimatedLengthListValues(&x, &y, &dx, &dy, nullptr);
4470
4471 // Get rotate.
4472 const SVGNumberList* rotate = nullptr;
4473 SVGAnimatedNumberList* animatedRotate =
4474 element->GetAnimatedNumberList(nsGkAtoms::rotate);
4475 if (animatedRotate) {
4476 rotate = &animatedRotate->GetAnimValue();
4477 }
4478
4479 bool percentages = false;
4480 uint32_t count = GetTextContentLength(aContent);
4481
4482 if (MOZ_UNLIKELY(aIndex + count > mPositions.Length())(__builtin_expect(!!(aIndex + count > mPositions.Length())
, 0))
) {
4483 MOZ_ASSERT_UNREACHABLE(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"length of mPositions does not match characters " "found by iterating content"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4485); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "length of mPositions does not match characters "
"found by iterating content" ")"); do { MOZ_CrashSequence(__null
, 4485); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
4484 "length of mPositions does not match characters "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"length of mPositions does not match characters " "found by iterating content"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4485); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "length of mPositions does not match characters "
"found by iterating content" ")"); do { MOZ_CrashSequence(__null
, 4485); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
4485 "found by iterating content")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"length of mPositions does not match characters " "found by iterating content"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4485); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "length of mPositions does not match characters "
"found by iterating content" ")"); do { MOZ_CrashSequence(__null
, 4485); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
4486 return false;
4487 }
4488
4489 // New text anchoring chunks start at each character assigned a position
4490 // with x="" or y="", or if we forced one with aForceStartOfChunk due to
4491 // being just after a <textPath>.
4492 uint32_t newChunkCount = std::max(x.Length(), y.Length());
4493 if (!newChunkCount && aForceStartOfChunk) {
4494 newChunkCount = 1;
4495 }
4496 for (uint32_t i = 0, j = 0; i < newChunkCount && j < count; j++) {
4497 if (!mPositions[aIndex + j].mUnaddressable) {
4498 mPositions[aIndex + j].mStartOfChunk = true;
4499 i++;
4500 }
4501 }
4502
4503 // Copy dx="" and dy="" values into aDeltas.
4504 if (!dx.IsEmpty() || !dy.IsEmpty()) {
4505 // Any unspecified deltas when we grow the array just get left as 0s.
4506 aDeltas.EnsureLengthAtLeast(aIndex + count);
4507 for (uint32_t i = 0, j = 0; i < dx.Length() && j < count; j++) {
4508 if (!mPositions[aIndex + j].mUnaddressable) {
4509 aDeltas[aIndex + j].x = dx[i];
4510 percentages = percentages || dx.HasPercentageValueAt(i);
4511 i++;
4512 }
4513 }
4514 for (uint32_t i = 0, j = 0; i < dy.Length() && j < count; j++) {
4515 if (!mPositions[aIndex + j].mUnaddressable) {
4516 aDeltas[aIndex + j].y = dy[i];
4517 percentages = percentages || dy.HasPercentageValueAt(i);
4518 i++;
4519 }
4520 }
4521 }
4522
4523 // Copy x="" and y="" values.
4524 for (uint32_t i = 0, j = 0; i < x.Length() && j < count; j++) {
4525 if (!mPositions[aIndex + j].mUnaddressable) {
4526 mPositions[aIndex + j].mPosition.x = x[i];
4527 percentages = percentages || x.HasPercentageValueAt(i);
4528 i++;
4529 }
4530 }
4531 for (uint32_t i = 0, j = 0; i < y.Length() && j < count; j++) {
4532 if (!mPositions[aIndex + j].mUnaddressable) {
4533 mPositions[aIndex + j].mPosition.y = y[i];
4534 percentages = percentages || y.HasPercentageValueAt(i);
4535 i++;
4536 }
4537 }
4538
4539 // Copy rotate="" values.
4540 if (rotate && !rotate->IsEmpty()) {
4541 uint32_t i = 0, j = 0;
4542 while (i < rotate->Length() && j < count) {
4543 if (!mPositions[aIndex + j].mUnaddressable) {
4544 mPositions[aIndex + j].mAngle =
4545 std::numbers::pi * (*rotate)[i] / 180.0;
4546 i++;
4547 }
4548 j++;
4549 }
4550 // Propagate final rotate="" value to the end of this element.
4551 while (j < count) {
4552 mPositions[aIndex + j].mAngle = mPositions[aIndex + j - 1].mAngle;
4553 j++;
4554 }
4555 }
4556
4557 if (percentages) {
4558 AddStateBits(NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES);
4559 }
4560 }
4561
4562 // Recurse to children.
4563 bool inTextPath = aInTextPath || aContent->IsSVGElement(nsGkAtoms::textPath);
4564 for (nsIContent* child = aContent->GetFirstChild(); child;
4565 child = child->GetNextSibling()) {
4566 bool ok = ResolvePositionsForNode(child, aIndex, inTextPath,
4567 aForceStartOfChunk, aDeltas);
4568 if (!ok) {
4569 return false;
4570 }
4571 }
4572
4573 if (aContent->IsSVGElement(nsGkAtoms::textPath)) {
4574 // Force a new anchored chunk just after a <textPath>.
4575 aForceStartOfChunk = true;
4576 }
4577
4578 return true;
4579}
4580
4581bool SVGTextFrame::ResolvePositions(nsTArray<gfxPoint>& aDeltas,
4582 bool aRunPerGlyph) {
4583 NS_ASSERTION(mPositions.IsEmpty(), "expected mPositions to be empty")do { if (!(mPositions.IsEmpty())) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "expected mPositions to be empty", "mPositions.IsEmpty()", "./../../../layout/svg/SVGTextFrame.cpp"
, 4583); MOZ_PretendNoReturn(); } } while (0)
;
4584 RemoveStateBits(NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES);
4585
4586 CharIterator it(this, CharIterator::CharacterFilter::Original,
4587 /* aSubtree */ nullptr);
4588 if (it.AtEnd()) {
4589 return false;
4590 }
4591
4592 // We assume the first character position is (0,0) unless we later see
4593 // otherwise, and note it as unaddressable if it is.
4594 bool firstCharUnaddressable = it.IsOriginalCharUnaddressable();
4595 mPositions.AppendElement(CharPosition::Unspecified(firstCharUnaddressable));
4596
4597 // Fill in unspecified positions for all remaining characters, noting
4598 // them as unaddressable if they are.
4599 uint32_t index = 0;
4600 while (it.Next()) {
4601 while (++index < it.TextElementCharIndex()) {
4602 mPositions.AppendElement(CharPosition::Unspecified(false));
4603 }
4604 mPositions.AppendElement(
4605 CharPosition::Unspecified(it.IsOriginalCharUnaddressable()));
4606 }
4607 while (++index < it.TextElementCharIndex()) {
4608 mPositions.AppendElement(CharPosition::Unspecified(false));
4609 }
4610
4611 // Recurse over the content and fill in character positions as we go.
4612 bool forceStartOfChunk = false;
4613 index = 0;
4614 bool ok = ResolvePositionsForNode(mContent, index, aRunPerGlyph,
4615 forceStartOfChunk, aDeltas);
4616 return ok && index > 0;
4617}
4618
4619void SVGTextFrame::DetermineCharPositions(nsTArray<nsPoint>& aPositions) {
4620 NS_ASSERTION(aPositions.IsEmpty(), "expected aPositions to be empty")do { if (!(aPositions.IsEmpty())) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "expected aPositions to be empty", "aPositions.IsEmpty()", "./../../../layout/svg/SVGTextFrame.cpp"
, 4620); MOZ_PretendNoReturn(); } } while (0)
;
4621
4622 nsPoint position;
4623
4624 TextFrameIterator frit(this);
4625 for (nsTextFrame* frame = frit.GetCurrent(); frame; frame = frit.GetNext()) {
4626 gfxSkipCharsIterator it = frame->EnsureTextRun(nsTextFrame::eInflated);
4627 gfxTextRun* textRun = frame->GetTextRun(nsTextFrame::eInflated);
4628 auto& provider = PropertyProviderFor(frame);
4629
4630 // Reset the position to the new frame's position.
4631 position = frit.Position();
4632 if (textRun->IsVertical()) {
4633 if (textRun->IsInlineReversed()) {
4634 position.y += frame->GetRect().height;
4635 }
4636 position.x += GetBaselinePosition(frame, textRun, frit.DominantBaseline(),
4637 mFontSizeScaleFactor);
4638 } else {
4639 if (textRun->IsInlineReversed()) {
4640 position.x += frame->GetRect().width;
4641 }
4642 position.y += GetBaselinePosition(frame, textRun, frit.DominantBaseline(),
4643 mFontSizeScaleFactor);
4644 }
4645
4646 // Any characters not in a frame, e.g. when display:none.
4647 for (uint32_t i = 0; i < frit.UndisplayedCharacters(); i++) {
4648 aPositions.AppendElement(position);
4649 }
4650
4651 // Any white space characters trimmed at the start of the line of text.
4652 nsTextFrame::TrimmedOffsets trimmedOffsets =
4653 frame->GetTrimmedOffsets(frame->CharacterDataBuffer());
4654 while (it.GetOriginalOffset() < trimmedOffsets.mStart) {
4655 aPositions.AppendElement(position);
4656 it.AdvanceOriginal(1);
4657 }
4658
4659 // Visible characters in the text frame.
4660 while (it.GetOriginalOffset() < frame->GetContentEnd()) {
4661 aPositions.AppendElement(position);
4662 if (!it.IsOriginalCharSkipped()) {
4663 // Accumulate partial ligature advance into position. (We must get
4664 // partial advances rather than get the advance of the whole ligature
4665 // group / cluster at once, since the group may span text frames, and
4666 // the PropertyProvider only has spacing information for the current
4667 // text frame.)
4668 uint32_t offset = it.GetSkippedOffset();
4669 nscoord advance =
4670 textRun->GetAdvanceWidth(Range(offset, offset + 1), &provider);
4671 (textRun->IsVertical() ? position.y : position.x) +=
4672 textRun->IsInlineReversed() ? -advance : advance;
4673 }
4674 it.AdvanceOriginal(1);
4675 }
4676 }
4677
4678 // Finally any characters at the end that are not in a frame.
4679 for (uint32_t i = 0; i < frit.UndisplayedCharacters(); i++) {
4680 aPositions.AppendElement(position);
4681 }
4682
4683 // Clear any cached PropertyProvider, to avoid risk of re-using it after
4684 // style changes or other mutations may have invalidated it.
4685 ForgetCachedProvider();
4686}
4687
4688/**
4689 * Physical text-anchor values.
4690 */
4691enum class TextAnchorSide { Left, Middle, Right };
4692
4693/**
4694 * Converts a logical text-anchor value to its physical value, based on whether
4695 * it is for an RTL frame.
4696 */
4697static TextAnchorSide ConvertLogicalTextAnchorToPhysical(
4698 StyleTextAnchor aTextAnchor, bool aIsRightToLeft) {
4699 NS_ASSERTION(uint8_t(aTextAnchor) <= 3, "unexpected value for aTextAnchor")do { if (!(uint8_t(aTextAnchor) <= 3)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "unexpected value for aTextAnchor", "uint8_t(aTextAnchor) <= 3"
, "./../../../layout/svg/SVGTextFrame.cpp", 4699); MOZ_PretendNoReturn
(); } } while (0)
;
4700 if (!aIsRightToLeft) {
4701 return TextAnchorSide(uint8_t(aTextAnchor));
4702 }
4703 return TextAnchorSide(2 - uint8_t(aTextAnchor));
4704}
4705
4706/**
4707 * Shifts the recorded character positions for an anchored chunk.
4708 *
4709 * @param aCharPositions The recorded character positions.
4710 * @param aChunkStart The character index the starts the anchored chunk. This
4711 * character's initial position is the anchor point.
4712 * @param aChunkEnd The character index just after the end of the anchored
4713 * chunk.
4714 * @param aVisIStartEdge The left/top-most edge of any of the glyphs within the
4715 * anchored chunk.
4716 * @param aVisIEndEdge The right/bottom-most edge of any of the glyphs within
4717 * the anchored chunk.
4718 * @param aAnchorSide The direction to anchor.
4719 */
4720static void ShiftAnchoredChunk(nsTArray<CharPosition>& aCharPositions,
4721 uint32_t aChunkStart, uint32_t aChunkEnd,
4722 gfxFloat aVisIStartEdge, gfxFloat aVisIEndEdge,
4723 TextAnchorSide aAnchorSide, bool aVertical) {
4724 NS_ASSERTION(aVisIStartEdge <= aVisIEndEdge,do { if (!(aVisIStartEdge <= aVisIEndEdge)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "unexpected anchored chunk edges", "aVisIStartEdge <= aVisIEndEdge"
, "./../../../layout/svg/SVGTextFrame.cpp", 4725); MOZ_PretendNoReturn
(); } } while (0)
4725 "unexpected anchored chunk edges")do { if (!(aVisIStartEdge <= aVisIEndEdge)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "unexpected anchored chunk edges", "aVisIStartEdge <= aVisIEndEdge"
, "./../../../layout/svg/SVGTextFrame.cpp", 4725); MOZ_PretendNoReturn
(); } } while (0)
;
4726 NS_ASSERTION(aChunkStart < aChunkEnd,do { if (!(aChunkStart < aChunkEnd)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "unexpected values for aChunkStart and aChunkEnd", "aChunkStart < aChunkEnd"
, "./../../../layout/svg/SVGTextFrame.cpp", 4727); MOZ_PretendNoReturn
(); } } while (0)
4727 "unexpected values for aChunkStart and aChunkEnd")do { if (!(aChunkStart < aChunkEnd)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "unexpected values for aChunkStart and aChunkEnd", "aChunkStart < aChunkEnd"
, "./../../../layout/svg/SVGTextFrame.cpp", 4727); MOZ_PretendNoReturn
(); } } while (0)
;
4728
4729 gfxFloat shift = aVertical ? aCharPositions[aChunkStart].mPosition.y
4730 : aCharPositions[aChunkStart].mPosition.x;
4731 switch (aAnchorSide) {
4732 case TextAnchorSide::Left:
4733 shift -= aVisIStartEdge;
4734 break;
4735 case TextAnchorSide::Middle:
4736 shift -= std::midpoint(aVisIStartEdge, aVisIEndEdge);
4737 break;
4738 case TextAnchorSide::Right:
4739 shift -= aVisIEndEdge;
4740 break;
4741 }
4742
4743 if (shift != 0.0) {
4744 if (aVertical) {
4745 for (uint32_t i = aChunkStart; i < aChunkEnd; i++) {
4746 aCharPositions[i].mPosition.y += shift;
4747 }
4748 } else {
4749 for (uint32_t i = aChunkStart; i < aChunkEnd; i++) {
4750 aCharPositions[i].mPosition.x += shift;
4751 }
4752 }
4753 }
4754}
4755
4756void SVGTextFrame::AdjustChunksForLineBreaks() {
4757 nsBlockFrame* block = do_QueryFrame(PrincipalChildList().FirstChild());
4758 NS_ASSERTION(block, "expected block frame")do { if (!(block)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "expected block frame"
, "block", "./../../../layout/svg/SVGTextFrame.cpp", 4758); MOZ_PretendNoReturn
(); } } while (0)
;
4759
4760 nsBlockFrame::LineIterator line = block->LinesBegin();
4761
4762 CharIterator it(this, CharIterator::CharacterFilter::Original,
4763 /* aSubtree */ nullptr);
4764 while (!it.AtEnd() && line != block->LinesEnd()) {
4765 if (it.GetTextFrame() == line->mFirstChild) {
4766 mPositions[it.TextElementCharIndex()].mStartOfChunk = true;
4767 line++;
4768 }
4769 it.AdvancePastCurrentFrame();
4770 }
4771}
4772
4773void SVGTextFrame::AdjustPositionsForClusters() {
4774 nsPresContext* presContext = PresContext();
4775
4776 // Find all of the characters that are in the middle of a cluster or
4777 // ligature group, and adjust their positions and rotations to match
4778 // the first character of the cluster/group.
4779 //
4780 // Also move the boundaries of text rendered runs and anchored chunks to
4781 // not lie in the middle of cluster/group.
4782
4783 // The partial advance of the current cluster or ligature group that we
4784 // have accumulated.
4785 gfxFloat partialAdvance = 0.0;
4786
4787 CharIterator it(this, CharIterator::CharacterFilter::Unskipped,
4788 /* aSubtree */ nullptr);
4789 bool isFirst = true;
4790 while (!it.AtEnd()) {
4791 if (it.IsClusterAndLigatureGroupStart() || isFirst) {
4792 // If we're at the start of a new cluster or ligature group, reset our
4793 // accumulated partial advance. Also treat the beginning of the text as
4794 // an anchor, even if it is a combining character and therefore was
4795 // marked as being a Unicode cluster continuation.
4796 partialAdvance = 0.0;
4797 isFirst = false;
4798 } else {
4799 // Otherwise, we're in the middle of a cluster or ligature group, and
4800 // we need to use the currently accumulated partial advance to adjust
4801 // the character's position and rotation.
4802
4803 // Find the start of the cluster/ligature group.
4804 uint32_t charIndex = it.TextElementCharIndex();
4805 uint32_t startIndex = it.GlyphStartTextElementCharIndex();
4806 MOZ_ASSERT(charIndex != startIndex,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(charIndex != startIndex)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(charIndex != startIndex))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("charIndex != startIndex"
" (" "If the current character is in the middle of a cluster or "
"ligature group, then charIndex must be different from " "startIndex"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4809); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "charIndex != startIndex" ") (" "If the current character is in the middle of a cluster or "
"ligature group, then charIndex must be different from " "startIndex"
")"); do { MOZ_CrashSequence(__null, 4809); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4807 "If the current character is in the middle of a cluster or "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(charIndex != startIndex)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(charIndex != startIndex))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("charIndex != startIndex"
" (" "If the current character is in the middle of a cluster or "
"ligature group, then charIndex must be different from " "startIndex"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4809); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "charIndex != startIndex" ") (" "If the current character is in the middle of a cluster or "
"ligature group, then charIndex must be different from " "startIndex"
")"); do { MOZ_CrashSequence(__null, 4809); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4808 "ligature group, then charIndex must be different from "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(charIndex != startIndex)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(charIndex != startIndex))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("charIndex != startIndex"
" (" "If the current character is in the middle of a cluster or "
"ligature group, then charIndex must be different from " "startIndex"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4809); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "charIndex != startIndex" ") (" "If the current character is in the middle of a cluster or "
"ligature group, then charIndex must be different from " "startIndex"
")"); do { MOZ_CrashSequence(__null, 4809); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4809 "startIndex")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(charIndex != startIndex)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(charIndex != startIndex))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("charIndex != startIndex"
" (" "If the current character is in the middle of a cluster or "
"ligature group, then charIndex must be different from " "startIndex"
")", "./../../../layout/svg/SVGTextFrame.cpp", 4809); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "charIndex != startIndex" ") (" "If the current character is in the middle of a cluster or "
"ligature group, then charIndex must be different from " "startIndex"
")"); do { MOZ_CrashSequence(__null, 4809); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4810
4811 mPositions[charIndex].mClusterOrLigatureGroupMiddle = true;
4812
4813 // Don't allow different rotations on ligature parts.
4814 bool rotationAdjusted = false;
4815 double angle = mPositions[startIndex].mAngle;
4816 if (mPositions[charIndex].mAngle != angle) {
4817 mPositions[charIndex].mAngle = angle;
4818 rotationAdjusted = true;
4819 }
4820
4821 // Update the character position.
4822 gfxFloat advance = partialAdvance / mFontSizeScaleFactor;
4823 const gfxTextRun* textRun = it.TextRun();
4824 gfxPoint direction = gfxPoint(cos(angle), sin(angle)) *
4825 (textRun->IsInlineReversed() ? -1.0 : 1.0);
4826 if (textRun->IsVertical()) {
4827 std::swap(direction.x, direction.y);
4828 }
4829 mPositions[charIndex].mPosition =
4830 mPositions[startIndex].mPosition + direction * advance;
4831
4832 // Ensure any runs that would end in the middle of a ligature now end just
4833 // after the ligature.
4834 if (mPositions[charIndex].mRunBoundary) {
4835 mPositions[charIndex].mRunBoundary = false;
4836 if (charIndex + 1 < mPositions.Length()) {
4837 mPositions[charIndex + 1].mRunBoundary = true;
4838 }
4839 } else if (rotationAdjusted) {
4840 if (charIndex + 1 < mPositions.Length()) {
4841 mPositions[charIndex + 1].mRunBoundary = true;
4842 }
4843 }
4844
4845 // Ensure any anchored chunks that would begin in the middle of a ligature
4846 // now begin just after the ligature.
4847 if (mPositions[charIndex].mStartOfChunk) {
4848 mPositions[charIndex].mStartOfChunk = false;
4849 if (charIndex + 1 < mPositions.Length()) {
4850 mPositions[charIndex + 1].mStartOfChunk = true;
4851 }
4852 }
4853 }
4854
4855 // Accumulate the current character's partial advance.
4856 partialAdvance += it.GetAdvance(presContext);
4857
4858 it.Next();
4859 }
4860}
4861
4862already_AddRefed<Path> SVGTextFrame::GetTextPath(nsIFrame* aTextPathFrame) {
4863 nsIContent* content = aTextPathFrame->GetContent();
4864 SVGTextPathElement* tp = static_cast<SVGTextPathElement*>(content);
4865 if (tp->mPath.IsRendered()) {
4866 // This is just an attribute so there's no transform that can apply
4867 // so we can just return the path directly.
4868 return tp->mPath.GetAnimValue().BuildPathForMeasuring(
4869 aTextPathFrame->Style()->EffectiveZoom().ToFloat());
4870 }
4871
4872 SVGGeometryElement* geomElement =
4873 SVGObserverUtils::GetAndObserveTextPathsPath(aTextPathFrame);
4874 if (!geomElement) {
4875 return nullptr;
4876 }
4877
4878 RefPtr<Path> path = geomElement->GetOrBuildPathForMeasuring();
4879 if (!path) {
4880 return nullptr;
4881 }
4882
4883 // Apply the geometry element's transform if appropriate.
4884 auto matrix = geomElement->LocalTransform();
4885 if (!matrix.IsIdentity()) {
4886 Path::Transform(path, matrix);
4887 }
4888
4889 return path.forget();
4890}
4891
4892gfxFloat SVGTextFrame::GetOffsetScale(nsIFrame* aTextPathFrame) {
4893 nsIContent* content = aTextPathFrame->GetContent();
4894 SVGTextPathElement* tp = static_cast<SVGTextPathElement*>(content);
4895 if (tp->mPath.IsRendered()) {
4896 // A path attribute has no pathLength or transform
4897 // so we return a unit scale.
4898 return 1.0;
4899 }
4900
4901 SVGGeometryElement* geomElement =
4902 SVGObserverUtils::GetAndObserveTextPathsPath(aTextPathFrame);
4903 if (!geomElement) {
4904 return 1.0;
4905 }
4906 return geomElement->GetPathLengthScale(
4907 SVGGeometryElement::PathLengthScaleUsageType::TextPath);
4908}
4909
4910gfxFloat SVGTextFrame::GetStartOffset(nsIFrame* aTextPathFrame) {
4911 SVGTextPathElement* tp =
4912 static_cast<SVGTextPathElement*>(aTextPathFrame->GetContent());
4913 SVGAnimatedLength* length =
4914 &tp->mLengthAttributes[SVGTextPathElement::STARTOFFSET];
4915
4916 if (length->IsPercentage()) {
4917 if (!std::isfinite(GetOffsetScale(aTextPathFrame))) {
4918 // Either pathLength="0" for this path or the path has 0 length.
4919 return 0.0;
4920 }
4921 RefPtr<Path> data = GetTextPath(aTextPathFrame);
4922 return data ? length->GetAnimValInSpecifiedUnits() * data->ComputeLength() /
4923 100.0
4924 : 0.0;
4925 }
4926 float lengthValue = length->GetAnimValueWithZoom(tp);
4927 // If offsetScale is infinity we want to return 0 not NaN
4928 return lengthValue == 0 ? 0.0 : lengthValue * GetOffsetScale(aTextPathFrame);
4929}
4930
4931void SVGTextFrame::DoTextPathLayout() {
4932 nsPresContext* context = PresContext();
4933
4934 CharIterator it(this, CharIterator::CharacterFilter::Original,
4935 /* aSubtree */ nullptr);
4936 while (!it.AtEnd()) {
4937 nsIFrame* textPathFrame = it.TextPathFrame();
4938 if (!textPathFrame) {
4939 // Skip past this frame if we're not in a text path.
4940 it.AdvancePastCurrentFrame();
4941 continue;
4942 }
4943
4944 // Get the path itself.
4945 RefPtr<Path> path = GetTextPath(textPathFrame);
4946 if (!path) {
4947 uint32_t start = it.TextElementCharIndex();
4948 it.AdvancePastCurrentTextPathFrame();
4949 uint32_t end = it.TextElementCharIndex();
4950 for (uint32_t i = start; i < end; i++) {
4951 mPositions[i].mHidden = true;
4952 }
4953 continue;
4954 }
4955
4956 SVGTextPathElement* textPath =
4957 static_cast<SVGTextPathElement*>(textPathFrame->GetContent());
4958 uint16_t side =
4959 textPath->EnumAttributes()[SVGTextPathElement::SIDE].GetAnimValue();
4960
4961 gfxFloat offset = GetStartOffset(textPathFrame);
4962 Float pathLength = path->ComputeLength();
4963
4964 // If the first character within the text path is in the middle of a
4965 // cluster or ligature group, just skip it and don't apply text path
4966 // positioning.
4967 while (!it.AtEnd()) {
4968 if (it.IsOriginalCharSkipped()) {
4969 it.Next();
4970 continue;
4971 }
4972 if (it.IsClusterAndLigatureGroupStart()) {
4973 break;
4974 }
4975 it.Next();
4976 }
4977
4978 bool skippedEndOfTextPath = false;
4979
4980 // Loop for each character in the text path.
4981 while (!it.AtEnd() && it.TextPathFrame() &&
4982 it.TextPathFrame()->GetContent() == textPath) {
4983 // The index of the cluster or ligature group's first character.
4984 uint32_t i = it.TextElementCharIndex();
4985
4986 // The index of the next character of the cluster or ligature.
4987 // We track this as we loop over the characters below so that we
4988 // can detect undisplayed characters and append entries into
4989 // partialAdvances for them.
4990 uint32_t j = i + 1;
4991
4992 MOZ_ASSERT(!mPositions[i].mClusterOrLigatureGroupMiddle)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mPositions[i].mClusterOrLigatureGroupMiddle)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!mPositions[i].mClusterOrLigatureGroupMiddle))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!mPositions[i].mClusterOrLigatureGroupMiddle"
, "./../../../layout/svg/SVGTextFrame.cpp", 4992); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mPositions[i].mClusterOrLigatureGroupMiddle"
")"); do { MOZ_CrashSequence(__null, 4992); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4993
4994 RefPtr<const gfxTextRun> textRun = it.TextRun();
4995 bool vertical = textRun->IsVertical();
4996
4997 // Compute cumulative advances for each character of the cluster or
4998 // ligature group.
4999 AutoTArray<gfxFloat, 4> partialAdvances;
5000 gfxFloat partialAdvance = it.GetAdvance(context);
5001 partialAdvances.AppendElement(partialAdvance);
5002 while (it.Next()) {
5003 // Append entries for any undisplayed characters the CharIterator
5004 // skipped over.
5005 MOZ_ASSERT(j <= it.TextElementCharIndex())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(j <= it.TextElementCharIndex())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(j <= it.TextElementCharIndex
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("j <= it.TextElementCharIndex()", "./../../../layout/svg/SVGTextFrame.cpp"
, 5005); AnnotateMozCrashReason("MOZ_ASSERT" "(" "j <= it.TextElementCharIndex()"
")"); do { MOZ_CrashSequence(__null, 5005); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5006 while (j < it.TextElementCharIndex()) {
5007 partialAdvances.AppendElement(partialAdvance);
5008 ++j;
5009 }
5010 // This loop may end up outside of the current text path, but
5011 // that's OK; we'll consider any complete cluster or ligature
5012 // group that begins inside the text path as being affected
5013 // by it.
5014 if (it.IsOriginalCharSkipped()) {
5015 if (!it.TextPathFrame()) {
5016 skippedEndOfTextPath = true;
5017 break;
5018 }
5019 // Leave partialAdvance unchanged.
5020 } else if (it.IsClusterAndLigatureGroupStart()) {
5021 break;
5022 } else {
5023 partialAdvance += it.GetAdvance(context);
5024 }
5025 partialAdvances.AppendElement(partialAdvance);
5026 }
5027
5028 if (!skippedEndOfTextPath) {
5029 // Any final undisplayed characters the CharIterator skipped over.
5030 MOZ_ASSERT(j <= it.TextElementCharIndex())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(j <= it.TextElementCharIndex())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(j <= it.TextElementCharIndex
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("j <= it.TextElementCharIndex()", "./../../../layout/svg/SVGTextFrame.cpp"
, 5030); AnnotateMozCrashReason("MOZ_ASSERT" "(" "j <= it.TextElementCharIndex()"
")"); do { MOZ_CrashSequence(__null, 5030); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5031 while (j < it.TextElementCharIndex()) {
5032 partialAdvances.AppendElement(partialAdvance);
5033 ++j;
5034 }
5035 }
5036
5037 gfxFloat halfAdvance =
5038 partialAdvances.LastElement() / mFontSizeScaleFactor / 2.0;
5039 if (textRun->IsInlineReversed()) {
5040 halfAdvance = -halfAdvance;
5041 }
5042 gfxFloat midx =
5043 (vertical ? mPositions[i].mPosition.y : mPositions[i].mPosition.x) +
5044 halfAdvance + offset;
5045
5046 // Hide the character if it falls off the end of the path.
5047 mPositions[i].mHidden = midx < 0 || midx > pathLength;
5048
5049 // Position the character on the path at the right angle.
5050 Point tangent; // Unit vector tangent to the point we find.
5051 Point pt;
5052 if (side == dom::SVGTextPathElement_Binding::TEXTPATH_SIDETYPE_RIGHT) {
5053 pt = path->ComputePointAtLength(Float(pathLength - midx), &tangent);
5054 tangent = -tangent;
5055 } else {
5056 pt = path->ComputePointAtLength(Float(midx), &tangent);
5057 }
5058 Float rotation = vertical ? atan2f(-tangent.x, tangent.y)
5059 : atan2f(tangent.y, tangent.x);
5060 Point normal(-tangent.y, tangent.x); // Unit vector normal to the point.
5061 Point offsetFromPath = normal * (vertical ? -mPositions[i].mPosition.x
5062 : mPositions[i].mPosition.y);
5063 pt += offsetFromPath;
5064 mPositions[i].mPosition =
5065 ThebesPoint(pt) - ThebesPoint(tangent) * halfAdvance;
5066 mPositions[i].mAngle += rotation;
5067 Point direction = textRun->IsInlineReversed() ? -tangent : tangent;
5068
5069 // Position any characters for a partial ligature.
5070 for (uint32_t k = i + 1; k < j; k++) {
5071 gfxPoint partialAdvance = ThebesPoint(direction) *
5072 partialAdvances[k - i] / mFontSizeScaleFactor;
5073 mPositions[k].mPosition = mPositions[i].mPosition + partialAdvance;
5074 mPositions[k].mAngle = mPositions[i].mAngle;
5075 mPositions[k].mHidden = mPositions[i].mHidden;
5076 }
5077 }
5078 }
5079}
5080
5081void SVGTextFrame::DoAnchoring() {
5082 nsPresContext* presContext = PresContext();
5083
5084 CharIterator it(this, CharIterator::CharacterFilter::Original,
5085 /* aSubtree */ nullptr);
5086
5087 // Don't need to worry about skipped or trimmed characters.
5088 while (!it.AtEnd() &&
5089 (it.IsOriginalCharSkipped() || it.IsOriginalCharTrimmed())) {
5090 it.Next();
5091 }
5092
5093 bool vertical = GetWritingMode().IsVertical();
5094 for (uint32_t start = it.TextElementCharIndex(); start < mPositions.Length();
5095 start = it.TextElementCharIndex()) {
5096 it.AdvanceToCharacter(start);
5097 nsTextFrame* chunkFrame = it.GetTextFrame();
5098
5099 // Measure characters in this chunk to find the left-most and right-most
5100 // edges of all glyphs within the chunk.
5101 uint32_t index = it.TextElementCharIndex();
5102 uint32_t end = start;
Value stored to 'end' during its initialization is never read
5103 gfxFloat left = std::numeric_limits<gfxFloat>::infinity();
5104 gfxFloat right = -std::numeric_limits<gfxFloat>::infinity();
5105 do {
5106 if (!it.IsOriginalCharSkipped() && !it.IsOriginalCharTrimmed()) {
5107 gfxFloat advance = it.GetAdvance(presContext) / mFontSizeScaleFactor;
5108 const gfxTextRun* textRun = it.TextRun();
5109 gfxFloat pos = textRun->IsVertical() ? mPositions[index].mPosition.y
5110 : mPositions[index].mPosition.x;
5111 if (textRun->IsInlineReversed()) {
5112 left = std::min(left, pos - advance);
5113 right = std::max(right, pos);
5114 } else {
5115 left = std::min(left, pos);
5116 right = std::max(right, pos + advance);
5117 }
5118 }
5119 it.Next();
5120 index = end = it.TextElementCharIndex();
5121 } while (!it.AtEnd() && !mPositions[end].mStartOfChunk);
5122
5123 if (left != std::numeric_limits<gfxFloat>::infinity()) {
5124 bool isRTL =
5125 chunkFrame->StyleVisibility()->mDirection == StyleDirection::Rtl;
5126 TextAnchorSide anchor = ConvertLogicalTextAnchorToPhysical(
5127 chunkFrame->StyleSVG()->mTextAnchor, isRTL);
5128
5129 ShiftAnchoredChunk(mPositions, start, end, left, right, anchor, vertical);
5130 }
5131 }
5132}
5133
5134void SVGTextFrame::DoGlyphPositioning() {
5135 mPositions.Clear();
5136 RemoveStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
5137
5138 nsIFrame* kid = PrincipalChildList().FirstChild();
5139 if (kid && kid->IsSubtreeDirty()) {
5140 MOZ_ASSERT(false, "should have already reflowed the kid")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "should have already reflowed the kid"
")", "./../../../layout/svg/SVGTextFrame.cpp", 5140); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "should have already reflowed the kid"
")"); do { MOZ_CrashSequence(__null, 5140); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5141 return;
5142 }
5143
5144 // Since we can be called directly via GetBBoxContribution, our correspondence
5145 // may not be up to date.
5146 TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
5147
5148 // Determine the positions of each character in app units.
5149 AutoTArray<nsPoint, 64> charPositions;
5150 DetermineCharPositions(charPositions);
5151
5152 if (charPositions.IsEmpty()) {
5153 // No characters, so nothing to do.
5154 return;
5155 }
5156
5157 // If the textLength="" attribute was specified, then we need ResolvePositions
5158 // to record that a new run starts with each glyph.
5159 SVGTextContentElement* element =
5160 static_cast<SVGTextContentElement*>(GetContent());
5161 SVGAnimatedLength* textLengthAttr =
5162 element->GetAnimatedLength(nsGkAtoms::textLength);
5163 uint16_t lengthAdjust =
5164 element->EnumAttributes()[SVGTextContentElement::LENGTHADJUST]
5165 .GetAnimValue();
5166 bool adjustingTextLength = textLengthAttr->IsExplicitlySet();
5167 float expectedTextLength = textLengthAttr->GetAnimValueWithZoom(element);
5168
5169 if (adjustingTextLength &&
5170 (expectedTextLength < 0.0f || lengthAdjust == LENGTHADJUST_UNKNOWN)) {
5171 // If textLength="" is less than zero or lengthAdjust is unknown, ignore it.
5172 adjustingTextLength = false;
5173 }
5174
5175 // Get the x, y, dx, dy, rotate values for the subtree.
5176 AutoTArray<gfxPoint, 16> deltas;
5177 if (!ResolvePositions(deltas, adjustingTextLength)) {
5178 // If ResolvePositions returned false, it means either there were some
5179 // characters in the DOM but none of them are displayed, or there was
5180 // an error in processing mPositions. Clear out mPositions so that we don't
5181 // attempt to do any painting later.
5182 mPositions.Clear();
5183 return;
5184 }
5185
5186 // XXX We might be able to do less work when there is at most a single
5187 // x/y/dx/dy position.
5188
5189 // Truncate the positioning arrays to the actual number of characters present.
5190 TruncateTo(deltas, charPositions);
5191 TruncateTo(mPositions, charPositions);
5192
5193 // Fill in an unspecified position for the first addressable character.
5194 uint32_t first = 0;
5195 while (first + 1 < mPositions.Length() && mPositions[first].mUnaddressable) {
5196 ++first;
5197 }
5198 if (!mPositions[first].IsXSpecified()) {
5199 mPositions[first].mPosition.x = 0.0;
5200 }
5201 if (!mPositions[first].IsYSpecified()) {
5202 mPositions[first].mPosition.y = 0.0;
5203 }
5204 if (!mPositions[first].IsAngleSpecified()) {
5205 mPositions[first].mAngle = 0.0;
5206 }
5207
5208 nsPresContext* presContext = PresContext();
5209 bool vertical = GetWritingMode().IsVertical();
5210
5211 float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
5212 presContext->AppUnitsPerDevPixel());
5213 double factor = cssPxPerDevPx / mFontSizeScaleFactor;
5214
5215 // Determine how much to compress or expand glyph positions due to
5216 // textLength="" and lengthAdjust="".
5217 double adjustment = 0.0;
5218 mLengthAdjustScaleFactor = 1.0f;
5219 if (adjustingTextLength) {
5220 nscoord frameLength =
5221 vertical ? PrincipalChildList().FirstChild()->GetRect().height
5222 : PrincipalChildList().FirstChild()->GetRect().width;
5223 float actualTextLength = static_cast<float>(
5224 presContext->AppUnitsToGfxUnits(frameLength) * factor);
5225
5226 switch (lengthAdjust) {
5227 case LENGTHADJUST_SPACINGANDGLYPHS:
5228 // Scale the glyphs and their positions.
5229 if (actualTextLength > 0) {
5230 mLengthAdjustScaleFactor = expectedTextLength / actualTextLength;
5231 }
5232 break;
5233
5234 default:
5235 MOZ_ASSERT(lengthAdjust == LENGTHADJUST_SPACING)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lengthAdjust == LENGTHADJUST_SPACING)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lengthAdjust == LENGTHADJUST_SPACING
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lengthAdjust == LENGTHADJUST_SPACING", "./../../../layout/svg/SVGTextFrame.cpp"
, 5235); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lengthAdjust == LENGTHADJUST_SPACING"
")"); do { MOZ_CrashSequence(__null, 5235); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5236 // Just add space between each glyph.
5237 int32_t adjustableSpaces = 0;
5238 for (uint32_t i = 1; i < mPositions.Length(); i++) {
5239 if (!mPositions[i].mUnaddressable) {
5240 adjustableSpaces++;
5241 }
5242 }
5243 if (adjustableSpaces) {
5244 adjustment =
5245 (expectedTextLength - actualTextLength) / adjustableSpaces;
5246 }
5247 break;
5248 }
5249 }
5250
5251 // Fill in any unspecified character positions based on the positions recorded
5252 // in charPositions, and also add in the dx/dy values.
5253 if (!deltas.IsEmpty()) {
5254 mPositions[0].mPosition += deltas[0];
5255 }
5256
5257 gfxFloat xLengthAdjustFactor = vertical ? 1.0 : mLengthAdjustScaleFactor;
5258 gfxFloat yLengthAdjustFactor = vertical ? mLengthAdjustScaleFactor : 1.0;
5259 for (uint32_t i = 1; i < mPositions.Length(); i++) {
5260 // Fill in unspecified x position.
5261 if (!mPositions[i].IsXSpecified()) {
5262 nscoord d = charPositions[i].x - charPositions[i - 1].x;
5263 mPositions[i].mPosition.x =
5264 mPositions[i - 1].mPosition.x +
5265 presContext->AppUnitsToGfxUnits(d) * factor * xLengthAdjustFactor;
5266 if (!vertical && !mPositions[i].mUnaddressable) {
5267 mPositions[i].mPosition.x += adjustment;
5268 }
5269 }
5270 // Fill in unspecified y position.
5271 if (!mPositions[i].IsYSpecified()) {
5272 nscoord d = charPositions[i].y - charPositions[i - 1].y;
5273 mPositions[i].mPosition.y =
5274 mPositions[i - 1].mPosition.y +
5275 presContext->AppUnitsToGfxUnits(d) * factor * yLengthAdjustFactor;
5276 if (vertical && !mPositions[i].mUnaddressable) {
5277 mPositions[i].mPosition.y += adjustment;
5278 }
5279 }
5280 // Add in dx/dy.
5281 if (i < deltas.Length()) {
5282 mPositions[i].mPosition += deltas[i];
5283 }
5284 // Fill in unspecified rotation values.
5285 if (!mPositions[i].IsAngleSpecified()) {
5286 mPositions[i].mAngle = 0.0f;
5287 }
5288 }
5289
5290 MOZ_ASSERT(mPositions.Length() == charPositions.Length())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mPositions.Length() == charPositions.Length())>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mPositions.Length() == charPositions.Length()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("mPositions.Length() == charPositions.Length()"
, "./../../../layout/svg/SVGTextFrame.cpp", 5290); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mPositions.Length() == charPositions.Length()"
")"); do { MOZ_CrashSequence(__null, 5290); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5291
5292 AdjustChunksForLineBreaks();
5293 AdjustPositionsForClusters();
5294 DoAnchoring();
5295 DoTextPathLayout();
5296}
5297
5298bool SVGTextFrame::ShouldRenderAsPath(nsTextFrame* aFrame,
5299 SVGContextPaint* aContextPaint,
5300 bool& aShouldPaintSVGGlyphs) {
5301 // Rendering to a clip path.
5302 if (HasAnyStateBits(NS_STATE_SVG_CLIPPATH_CHILD)) {
5303 aShouldPaintSVGGlyphs = false;
5304 return true;
5305 }
5306
5307 aShouldPaintSVGGlyphs = true;
5308
5309 const nsStyleSVG* style = aFrame->StyleSVG();
5310
5311 // Fill is a non-solid paint or is not opaque.
5312 if (!(style->mFill.kind.IsNone() ||
5313 (style->mFill.kind.IsColor() &&
5314 SVGUtils::GetOpacity(style->mFillOpacity, aContextPaint) == 1.0f))) {
5315 return true;
5316 }
5317
5318 // If we're going to need to draw a non-opaque shadow.
5319 // It's possible nsTextFrame will support non-opaque shadows in the future,
5320 // in which case this test can be removed.
5321 if (style->mFill.kind.IsColor() && aFrame->StyleText()->HasTextShadow() &&
5322 NS_GET_A(style->mFill.kind.AsColor().CalcColor(*aFrame->Style()))((uint8_t)(((style->mFill.kind.AsColor().CalcColor(*aFrame
->Style())) >> 24) & 0xff))
!=
5323 0xFF) {
5324 return true;
5325 }
5326
5327 // Text has a stroke.
5328 if (style->HasStroke()) {
5329 if (style->mStrokeWidth.IsContextValue()) {
5330 return true;
5331 }
5332 if (SVGContentUtils::CoordToFloat(
5333 static_cast<SVGElement*>(GetContent()),
5334 style->mStrokeWidth.AsLengthPercentage()) > 0) {
5335 return true;
5336 }
5337 }
5338
5339 return false;
5340}
5341
5342void SVGTextFrame::ScheduleReflowSVG() {
5343 if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
5344 ScheduleReflowSVGNonDisplayText(
5345 IntrinsicDirty::FrameAncestorsAndDescendants);
5346 } else {
5347 SVGUtils::ScheduleReflowSVG(this);
5348 }
5349}
5350
5351void SVGTextFrame::NotifyGlyphMetricsChange(bool aUpdateTextCorrespondence) {
5352 if (aUpdateTextCorrespondence) {
5353 AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY);
5354 }
5355 AddStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
5356 nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), RestyleHint{0},
5357 nsChangeHint_InvalidateRenderingObservers);
5358 ScheduleReflowSVG();
5359}
5360
5361void SVGTextFrame::UpdateGlyphPositioning() {
5362 nsIFrame* kid = PrincipalChildList().FirstChild();
5363 if (!kid) {
5364 return;
5365 }
5366
5367 if (HasAnyStateBits(NS_STATE_SVG_POSITIONING_DIRTY)) {
5368 DoGlyphPositioning();
5369 }
5370}
5371
5372void SVGTextFrame::MaybeResolveBidiForAnonymousBlockChild() {
5373 nsIFrame* kid = PrincipalChildList().FirstChild();
5374
5375 if (kid && kid->HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION) &&
5376 PresContext()->BidiEnabled()) {
5377 MOZ_ASSERT(static_cast<nsBlockFrame*>(do_QueryFrame(kid)),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(static_cast<nsBlockFrame*>(do_QueryFrame(kid))
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(static_cast<nsBlockFrame*>(do_QueryFrame(kid))
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"static_cast<nsBlockFrame*>(do_QueryFrame(kid))" " (" "Expect anonymous child to be an nsBlockFrame"
")", "./../../../layout/svg/SVGTextFrame.cpp", 5378); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "static_cast<nsBlockFrame*>(do_QueryFrame(kid))"
") (" "Expect anonymous child to be an nsBlockFrame" ")"); do
{ MOZ_CrashSequence(__null, 5378); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
5378 "Expect anonymous child to be an nsBlockFrame")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(static_cast<nsBlockFrame*>(do_QueryFrame(kid))
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(static_cast<nsBlockFrame*>(do_QueryFrame(kid))
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"static_cast<nsBlockFrame*>(do_QueryFrame(kid))" " (" "Expect anonymous child to be an nsBlockFrame"
")", "./../../../layout/svg/SVGTextFrame.cpp", 5378); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "static_cast<nsBlockFrame*>(do_QueryFrame(kid))"
") (" "Expect anonymous child to be an nsBlockFrame" ")"); do
{ MOZ_CrashSequence(__null, 5378); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
5379 nsBidiPresUtils::Resolve(static_cast<nsBlockFrame*>(kid));
5380 }
5381}
5382
5383void SVGTextFrame::MaybeReflowAnonymousBlockChild() {
5384 nsIFrame* kid = PrincipalChildList().FirstChild();
5385 if (!kid) {
5386 return;
5387 }
5388
5389 MOZ_ASSERT(!kid->HasAnyStateBits(NS_FRAME_IN_REFLOW),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!kid->HasAnyStateBits(NS_FRAME_IN_REFLOW))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!kid->HasAnyStateBits(NS_FRAME_IN_REFLOW)))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!kid->HasAnyStateBits(NS_FRAME_IN_REFLOW)"
" (" "should not be in reflow when about to reflow again" ")"
, "./../../../layout/svg/SVGTextFrame.cpp", 5390); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!kid->HasAnyStateBits(NS_FRAME_IN_REFLOW)"
") (" "should not be in reflow when about to reflow again" ")"
); do { MOZ_CrashSequence(__null, 5390); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
5390 "should not be in reflow when about to reflow again")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!kid->HasAnyStateBits(NS_FRAME_IN_REFLOW))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!kid->HasAnyStateBits(NS_FRAME_IN_REFLOW)))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!kid->HasAnyStateBits(NS_FRAME_IN_REFLOW)"
" (" "should not be in reflow when about to reflow again" ")"
, "./../../../layout/svg/SVGTextFrame.cpp", 5390); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!kid->HasAnyStateBits(NS_FRAME_IN_REFLOW)"
") (" "should not be in reflow when about to reflow again" ")"
); do { MOZ_CrashSequence(__null, 5390); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5391
5392 if (IsSubtreeDirty()) {
5393 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
5394 // If we require a full reflow, ensure our kid is marked fully dirty.
5395 // (Note that our anonymous nsBlockFrame is not an ISVGDisplayableFrame,
5396 // so even when we are called via our ReflowSVG this will not be done for
5397 // us by SVGDisplayContainerFrame::ReflowSVG.)
5398 kid->MarkSubtreeDirty();
5399 }
5400
5401 // The RecordCorrespondence and DoReflow calls can result in new text frames
5402 // being created (due to bidi resolution or reflow). We set this bit to
5403 // guard against unnecessarily calling back in to
5404 // ScheduleReflowSVGNonDisplayText from nsIFrame::DidSetComputedStyle on
5405 // those new text frames.
5406 AddStateBits(NS_STATE_SVG_TEXT_IN_REFLOW);
5407
5408 TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
5409
5410 MOZ_ASSERT(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
" (" "should be under ReflowSVG" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 5411); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
") (" "should be under ReflowSVG" ")"); do { MOZ_CrashSequence
(__null, 5411); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
5411 "should be under ReflowSVG")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
" (" "should be under ReflowSVG" ")", "./../../../layout/svg/SVGTextFrame.cpp"
, 5411); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SVGUtils::AnyOuterSVGIsCallingReflowSVG(this)"
") (" "should be under ReflowSVG" ")"); do { MOZ_CrashSequence
(__null, 5411); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5412 nsPresContext::InterruptPreventer noInterrupts(PresContext());
5413 DoReflow();
5414
5415 RemoveStateBits(NS_STATE_SVG_TEXT_IN_REFLOW);
5416 }
5417}
5418
5419void SVGTextFrame::DoReflow() {
5420 MOZ_ASSERT(HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)"
, "./../../../layout/svg/SVGTextFrame.cpp", 5420); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)"
")"); do { MOZ_CrashSequence(__null, 5420); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5421
5422 // Since we are going to reflow the anonymous block frame, we will
5423 // need to update mPositions.
5424 // We also mark our text correspondence as dirty since we can end up needing
5425 // reflow in ways that do not set NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY.
5426 // (We'd then fail the "expected a TextNodeCorrespondenceProperty" assertion
5427 // when UpdateGlyphPositioning() is called after we return.)
5428 AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |
5429 NS_STATE_SVG_POSITIONING_DIRTY);
5430
5431 if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
5432 // Normally, these dirty flags would be cleared in ReflowSVG(), but that
5433 // doesn't get called for non-display frames. We don't want to reflow our
5434 // descendants every time SVGTextFrame::PaintSVG makes sure that we have
5435 // valid positions by calling UpdateGlyphPositioning(), so we need to clear
5436 // these dirty bits. Note that this also breaks an invalidation loop where
5437 // our descendants invalidate as they reflow, which invalidates rendering
5438 // observers, which reschedules the frame that is currently painting by
5439 // referencing us to paint again. See bug 839958 comment 7. Hopefully we
5440 // will break that loop more convincingly at some point.
5441 RemoveStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
5442 }
5443
5444 // Forget any cached measurements of one of our children.
5445 mFrameForCachedRanges = nullptr;
5446
5447 nsPresContext* presContext = PresContext();
5448 nsIFrame* kid = PrincipalChildList().FirstChild();
5449 if (!kid) {
5450 return;
5451 }
5452
5453 std::unique_ptr<gfxContext> renderingContext =
5454 presContext->PresShell()->CreateReferenceRenderingContext();
5455
5456 if (UpdateFontSizeScaleFactor()) {
5457 // If the font size scale factor changed, we need the block to report
5458 // an updated preferred width.
5459 kid->MarkIntrinsicISizesDirty();
5460 }
5461
5462 const IntrinsicSizeInput input(renderingContext.get(), Nothing(), Nothing());
5463 nscoord inlineSize = kid->GetPrefISize(input);
5464 WritingMode wm = kid->GetWritingMode();
5465 ReflowInput reflowInput(presContext, kid, renderingContext.get(),
5466 LogicalSize(wm, inlineSize, NS_UNCONSTRAINEDSIZE));
5467 ReflowOutput desiredSize(reflowInput);
5468 nsReflowStatus status;
5469
5470 NS_ASSERTION(do { if (!(reflowInput.ComputedPhysicalBorderPadding() == nsMargin
(0, 0, 0, 0) && reflowInput.ComputedPhysicalMargin() ==
nsMargin(0, 0, 0, 0))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "style system should ensure that :-moz-svg-text "
"does not get styled", "reflowInput.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) && reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0)"
, "./../../../layout/svg/SVGTextFrame.cpp", 5474); MOZ_PretendNoReturn
(); } } while (0)
5471 reflowInput.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) &&do { if (!(reflowInput.ComputedPhysicalBorderPadding() == nsMargin
(0, 0, 0, 0) && reflowInput.ComputedPhysicalMargin() ==
nsMargin(0, 0, 0, 0))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "style system should ensure that :-moz-svg-text "
"does not get styled", "reflowInput.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) && reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0)"
, "./../../../layout/svg/SVGTextFrame.cpp", 5474); MOZ_PretendNoReturn
(); } } while (0)
5472 reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),do { if (!(reflowInput.ComputedPhysicalBorderPadding() == nsMargin
(0, 0, 0, 0) && reflowInput.ComputedPhysicalMargin() ==
nsMargin(0, 0, 0, 0))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "style system should ensure that :-moz-svg-text "
"does not get styled", "reflowInput.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) && reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0)"
, "./../../../layout/svg/SVGTextFrame.cpp", 5474); MOZ_PretendNoReturn
(); } } while (0)
5473 "style system should ensure that :-moz-svg-text "do { if (!(reflowInput.ComputedPhysicalBorderPadding() == nsMargin
(0, 0, 0, 0) && reflowInput.ComputedPhysicalMargin() ==
nsMargin(0, 0, 0, 0))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "style system should ensure that :-moz-svg-text "
"does not get styled", "reflowInput.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) && reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0)"
, "./../../../layout/svg/SVGTextFrame.cpp", 5474); MOZ_PretendNoReturn
(); } } while (0)
5474 "does not get styled")do { if (!(reflowInput.ComputedPhysicalBorderPadding() == nsMargin
(0, 0, 0, 0) && reflowInput.ComputedPhysicalMargin() ==
nsMargin(0, 0, 0, 0))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "style system should ensure that :-moz-svg-text "
"does not get styled", "reflowInput.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) && reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0)"
, "./../../../layout/svg/SVGTextFrame.cpp", 5474); MOZ_PretendNoReturn
(); } } while (0)
;
5475
5476 kid->Reflow(presContext, desiredSize, reflowInput, status);
5477 kid->DidReflow(presContext, &reflowInput);
5478 kid->SetSize(wm, desiredSize.Size(wm));
5479}
5480
5481// Usable font size range in devpixels / user-units
5482#define CLAMP_MIN_SIZE8.0 8.0
5483#define CLAMP_MAX_SIZE200.0 200.0
5484#define PRECISE_SIZE200.0 200.0
5485
5486bool SVGTextFrame::UpdateFontSizeScaleFactor() {
5487 float contextScale = GetContextScale(this);
5488 mLastContextScale = contextScale;
5489
5490 double oldFontSizeScaleFactor = mFontSizeScaleFactor;
5491
5492 bool geometricPrecision = false;
5493 // We may need to invert a matrix with these values later.
5494 CSSCoord min = std::sqrt(std::numeric_limits<float>::max());
5495 CSSCoord max = std::sqrt(std::numeric_limits<float>::min());
5496 bool anyText = false;
5497
5498 // Find the minimum and maximum font sizes used over all the
5499 // nsTextFrames.
5500 TextFrameIterator it(this);
5501 nsTextFrame* f = it.GetCurrent();
5502 while (f) {
5503 if (!geometricPrecision) {
5504 // Unfortunately we can't treat text-rendering:geometricPrecision
5505 // separately for each text frame.
5506 geometricPrecision = f->StyleText()->mTextRendering ==
5507 StyleTextRendering::Geometricprecision;
5508 }
5509 const auto& fontSize = f->StyleFont()->mFont.size;
5510 if (!fontSize.IsZero()) {
5511 min = std::min(min, fontSize.ToCSSPixels());
5512 max = std::max(max, fontSize.ToCSSPixels());
5513 anyText = true;
5514 }
5515 f = it.GetNext();
5516 }
5517
5518 if (!anyText) {
5519 // No text, so no need for scaling.
5520 mFontSizeScaleFactor = 1.0;
5521 return mFontSizeScaleFactor != oldFontSizeScaleFactor;
5522 }
5523
5524 if (geometricPrecision) {
5525 // We want to ensure minSize is scaled to PRECISE_SIZE.
5526 mFontSizeScaleFactor = PRECISE_SIZE200.0 / min;
5527 return mFontSizeScaleFactor != oldFontSizeScaleFactor;
5528 }
5529
5530 double minTextRunSize = min * contextScale;
5531 double maxTextRunSize = max * contextScale;
5532
5533 if (minTextRunSize >= CLAMP_MIN_SIZE8.0 && maxTextRunSize <= CLAMP_MAX_SIZE200.0) {
5534 // We are already in the ideal font size range for all text frames,
5535 // so we only have to take into account the contextScale.
5536 mFontSizeScaleFactor = contextScale;
5537 } else if (max / min > CLAMP_MAX_SIZE200.0 / CLAMP_MIN_SIZE8.0) {
5538 // We can't scale the font sizes so that all of the text frames lie
5539 // within our ideal font size range.
5540 // Heuristically, if the maxTextRunSize is within the CLAMP_MAX_SIZE
5541 // as a reasonable value, it's likely to be the user's intent to
5542 // get a valid font for the maxTextRunSize one, we should honor it.
5543 // The same for minTextRunSize.
5544 if (maxTextRunSize <= CLAMP_MAX_SIZE200.0) {
5545 mFontSizeScaleFactor = CLAMP_MAX_SIZE200.0 / max;
5546 } else if (minTextRunSize >= CLAMP_MIN_SIZE8.0) {
5547 mFontSizeScaleFactor = CLAMP_MIN_SIZE8.0 / min;
5548 } else {
5549 // So maxTextRunSize is too big, minTextRunSize is too small,
5550 // we can't really do anything for this case, just leave it as is.
5551 mFontSizeScaleFactor = contextScale;
5552 }
5553 } else if (minTextRunSize < CLAMP_MIN_SIZE8.0) {
5554 mFontSizeScaleFactor = CLAMP_MIN_SIZE8.0 / min;
5555 } else {
5556 mFontSizeScaleFactor = CLAMP_MAX_SIZE200.0 / max;
5557 }
5558
5559 return mFontSizeScaleFactor != oldFontSizeScaleFactor;
5560}
5561
5562double SVGTextFrame::GetFontSizeScaleFactor() const {
5563 return mFontSizeScaleFactor;
5564}
5565
5566/**
5567 * Take aPoint, which is in the <text> element's user space, and convert
5568 * it to the appropriate frame user space of aChildFrame according to
5569 * which rendered run the point hits.
5570 */
5571Point SVGTextFrame::TransformFramePointToTextChild(
5572 const Point& aPoint, const nsIFrame* aChildFrame) {
5573 NS_ASSERTION(aChildFrame && nsLayoutUtils::GetClosestFrameOfType(do { if (!(aChildFrame && nsLayoutUtils::GetClosestFrameOfType
( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "aChildFrame must be a descendant of this frame"
, "aChildFrame && nsLayoutUtils::GetClosestFrameOfType( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this"
, "./../../../layout/svg/SVGTextFrame.cpp", 5576); MOZ_PretendNoReturn
(); } } while (0)
5574 aChildFrame->GetParent(),do { if (!(aChildFrame && nsLayoutUtils::GetClosestFrameOfType
( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "aChildFrame must be a descendant of this frame"
, "aChildFrame && nsLayoutUtils::GetClosestFrameOfType( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this"
, "./../../../layout/svg/SVGTextFrame.cpp", 5576); MOZ_PretendNoReturn
(); } } while (0)
5575 LayoutFrameType::SVGText) == this,do { if (!(aChildFrame && nsLayoutUtils::GetClosestFrameOfType
( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "aChildFrame must be a descendant of this frame"
, "aChildFrame && nsLayoutUtils::GetClosestFrameOfType( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this"
, "./../../../layout/svg/SVGTextFrame.cpp", 5576); MOZ_PretendNoReturn
(); } } while (0)
5576 "aChildFrame must be a descendant of this frame")do { if (!(aChildFrame && nsLayoutUtils::GetClosestFrameOfType
( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "aChildFrame must be a descendant of this frame"
, "aChildFrame && nsLayoutUtils::GetClosestFrameOfType( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this"
, "./../../../layout/svg/SVGTextFrame.cpp", 5576); MOZ_PretendNoReturn
(); } } while (0)
;
5577
5578 UpdateGlyphPositioning();
5579
5580 nsPresContext* presContext = PresContext();
5581
5582 // Add in the mRect offset to aPoint, as that will have been taken into
5583 // account when transforming the point from the ancestor frame down
5584 // to this one.
5585 float cssPxPerDevPx = nsPresContext::AppUnitsToFloatCSSPixels(
5586 presContext->AppUnitsPerDevPixel());
5587 float factor = AppUnitsPerCSSPixel();
5588 Point framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
5589 NSAppUnitsToFloatPixels(mRect.y, factor));
5590 Point pointInUserSpace = aPoint * cssPxPerDevPx + framePosition;
5591
5592 // Find the closest rendered run for the text frames beneath aChildFrame.
5593 TextRenderedRunIterator it(
5594 this, TextRenderedRunIterator::RenderedRunFilter::AllFrames, aChildFrame);
5595 TextRenderedRun hit;
5596 gfxPoint pointInRun;
5597 nscoord dx = nscoord_MAX;
5598 nscoord dy = nscoord_MAX;
5599 for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
5600 TextRenderedRun::GeometryFlags flags(
5601 TextRenderedRun::GeometryFlag::IncludeFill,
5602 TextRenderedRun::GeometryFlag::IncludeStroke,
5603 TextRenderedRun::GeometryFlag::NoHorizontalOverflow);
5604 gfxRect runRect = run.GetRunUserSpaceRect(flags).ToThebesRect();
5605
5606 gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext);
5607 if (!m.Invert()) {
5608 return aPoint;
5609 }
5610 gfxPoint pointInRunUserSpace =
5611 m.TransformPoint(ThebesPoint(pointInUserSpace));
5612
5613 if (runRect.Contains(pointInRunUserSpace)) {
5614 // The point was inside the rendered run's rect, so we choose it.
5615 dx = 0;
5616 dy = 0;
5617 pointInRun = pointInRunUserSpace;
5618 hit = run;
5619 } else if (nsLayoutUtils::PointIsCloserToRect(pointInRunUserSpace, runRect,
5620 dx, dy)) {
5621 // The point was closer to this rendered run's rect than any others
5622 // we've seen so far.
5623 pointInRun.x =
5624 std::clamp(pointInRunUserSpace.x.value, runRect.X(), runRect.XMost());
5625 pointInRun.y =
5626 std::clamp(pointInRunUserSpace.y.value, runRect.Y(), runRect.YMost());
5627 hit = run;
5628 }
5629 }
5630
5631 if (!hit.mFrame) {
5632 // We didn't find any rendered runs for the frame.
5633 return aPoint;
5634 }
5635
5636 // Return the point in user units relative to the nsTextFrame,
5637 // but taking into account mFontSizeScaleFactor.
5638 gfxMatrix m = hit.GetTransformFromRunUserSpaceToFrameUserSpace(presContext);
5639 m.PreScale(mFontSizeScaleFactor, mFontSizeScaleFactor);
5640 return ToPoint(m.TransformPoint(pointInRun) / cssPxPerDevPx);
5641}
5642
5643/**
5644 * For each rendered run beneath aChildFrame, translate aRect from
5645 * aChildFrame to the run's text frame, transform it then into
5646 * the run's frame user space, intersect it with the run's
5647 * frame user space rect, then transform it up to user space.
5648 * The result is the union of all of these.
5649 */
5650gfxRect SVGTextFrame::TransformFrameRectFromTextChild(
5651 const nsRect& aRect, const nsIFrame* aChildFrame) {
5652 NS_ASSERTION(aChildFrame && nsLayoutUtils::GetClosestFrameOfType(do { if (!(aChildFrame && nsLayoutUtils::GetClosestFrameOfType
( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "aChildFrame must be a descendant of this frame"
, "aChildFrame && nsLayoutUtils::GetClosestFrameOfType( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this"
, "./../../../layout/svg/SVGTextFrame.cpp", 5655); MOZ_PretendNoReturn
(); } } while (0)
5653 aChildFrame->GetParent(),do { if (!(aChildFrame && nsLayoutUtils::GetClosestFrameOfType
( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "aChildFrame must be a descendant of this frame"
, "aChildFrame && nsLayoutUtils::GetClosestFrameOfType( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this"
, "./../../../layout/svg/SVGTextFrame.cpp", 5655); MOZ_PretendNoReturn
(); } } while (0)
5654 LayoutFrameType::SVGText) == this,do { if (!(aChildFrame && nsLayoutUtils::GetClosestFrameOfType
( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "aChildFrame must be a descendant of this frame"
, "aChildFrame && nsLayoutUtils::GetClosestFrameOfType( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this"
, "./../../../layout/svg/SVGTextFrame.cpp", 5655); MOZ_PretendNoReturn
(); } } while (0)
5655 "aChildFrame must be a descendant of this frame")do { if (!(aChildFrame && nsLayoutUtils::GetClosestFrameOfType
( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "aChildFrame must be a descendant of this frame"
, "aChildFrame && nsLayoutUtils::GetClosestFrameOfType( aChildFrame->GetParent(), LayoutFrameType::SVGText) == this"
, "./../../../layout/svg/SVGTextFrame.cpp", 5655); MOZ_PretendNoReturn
(); } } while (0)
;
5656
5657 UpdateGlyphPositioning();
5658
5659 nsPresContext* presContext = PresContext();
5660
5661 gfxRect result;
5662 TextRenderedRunIterator it(
5663 this, TextRenderedRunIterator::RenderedRunFilter::AllFrames, aChildFrame);
5664 for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
5665 // First, translate aRect from aChildFrame to this run's frame.
5666 nsRect rectInTextFrame = aRect + aChildFrame->GetOffsetTo(run.mFrame);
5667
5668 // Scale it into frame user space.
5669 gfxRect rectInFrameUserSpace = AppUnitsToFloatCSSPixels(rectInTextFrame);
5670
5671 // Take into account any font size scaling
5672 rectInFrameUserSpace.Scale(1.0 / mFontSizeScaleFactor);
5673
5674 // Intersect it with the run.
5675 TextRenderedRun::GeometryFlags flags(
5676 TextRenderedRun::GeometryFlag::IncludeFill,
5677 TextRenderedRun::GeometryFlag::IncludeStroke);
5678
5679 if (rectInFrameUserSpace.IntersectRect(
5680 rectInFrameUserSpace,
5681 run.GetFrameUserSpaceRect(presContext, flags).ToThebesRect())) {
5682 // Transform it up to user space of the <text>
5683 gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext);
5684 gfxRect rectInUserSpace = m.TransformRect(rectInFrameUserSpace);
5685
5686 // Union it into the result.
5687 result.UnionRect(result, rectInUserSpace);
5688 }
5689 }
5690
5691 // Subtract the mRect offset from the result, as our user space for
5692 // this frame is relative to the top-left of mRect.
5693 float factor = AppUnitsPerCSSPixel();
5694 gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
5695 NSAppUnitsToFloatPixels(mRect.y, factor));
5696
5697 return result - framePosition;
5698}
5699
5700Rect SVGTextFrame::TransformFrameRectFromTextChild(
5701 const Rect& aRect, const nsIFrame* aChildFrame) {
5702 nscoord appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
5703 nsRect r = LayoutDevicePixel::ToAppUnits(
5704 LayoutDeviceRect::FromUnknownRect(aRect), appUnitsPerDevPixel);
5705 gfxRect resultCssUnits = TransformFrameRectFromTextChild(r, aChildFrame);
5706 float devPixelPerCSSPixel =
5707 float(AppUnitsPerCSSPixel()) / appUnitsPerDevPixel;
5708 resultCssUnits.Scale(devPixelPerCSSPixel);
5709 return ToRect(resultCssUnits);
5710}
5711
5712Point SVGTextFrame::TransformFramePointFromTextChild(
5713 const Point& aPoint, const nsIFrame* aChildFrame) {
5714 return TransformFrameRectFromTextChild(Rect(aPoint, Size(1, 1)), aChildFrame)
5715 .TopLeft();
5716}
5717
5718void SVGTextFrame::AppendDirectlyOwnedAnonBoxes(
5719 nsTArray<OwnedAnonBox>& aResult) {
5720 MOZ_ASSERT(PrincipalChildList().FirstChild(), "Must have our anon box")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(PrincipalChildList().FirstChild())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(PrincipalChildList().FirstChild
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("PrincipalChildList().FirstChild()" " (" "Must have our anon box"
")", "./../../../layout/svg/SVGTextFrame.cpp", 5720); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "PrincipalChildList().FirstChild()" ") (" "Must have our anon box"
")"); do { MOZ_CrashSequence(__null, 5720); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5721 aResult.AppendElement(OwnedAnonBox(PrincipalChildList().FirstChild()));
5722}
5723
5724} // namespace mozilla