SVGTextContentElement.cpp (6885B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/SVGTextContentElement.h" 8 9 #include "DOMSVGPoint.h" 10 #include "SVGTextFrame.h" 11 #include "mozilla/dom/CharacterDataBuffer.h" 12 #include "mozilla/dom/SVGLengthBinding.h" 13 #include "mozilla/dom/SVGRect.h" 14 #include "mozilla/dom/SVGTextContentElementBinding.h" 15 #include "nsBidiUtils.h" 16 #include "nsLayoutUtils.h" 17 #include "nsTextFrameUtils.h" 18 #include "nsTextNode.h" 19 20 namespace mozilla::dom { 21 22 using namespace SVGTextContentElement_Binding; 23 24 SVGEnumMapping SVGTextContentElement::sLengthAdjustMap[] = { 25 {nsGkAtoms::spacing, LENGTHADJUST_SPACING}, 26 {nsGkAtoms::spacingAndGlyphs, LENGTHADJUST_SPACINGANDGLYPHS}, 27 {nullptr, 0}}; 28 29 SVGElement::EnumInfo SVGTextContentElement::sEnumInfo[1] = { 30 {nsGkAtoms::lengthAdjust, sLengthAdjustMap, LENGTHADJUST_SPACING}}; 31 32 SVGElement::LengthInfo SVGTextContentElement::sLengthInfo[1] = { 33 {nsGkAtoms::textLength, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, 34 SVGContentUtils::XY}}; 35 36 SVGTextFrame* SVGTextContentElement::GetSVGTextFrame() { 37 nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); 38 nsIFrame* textFrame = 39 nsLayoutUtils::GetClosestFrameOfType(frame, LayoutFrameType::SVGText); 40 return static_cast<SVGTextFrame*>(textFrame); 41 } 42 43 SVGTextFrame* 44 SVGTextContentElement::GetSVGTextFrameForNonLayoutDependentQuery() { 45 nsIFrame* frame = GetPrimaryFrame(FlushType::Frames); 46 nsIFrame* textFrame = 47 nsLayoutUtils::GetClosestFrameOfType(frame, LayoutFrameType::SVGText); 48 return static_cast<SVGTextFrame*>(textFrame); 49 } 50 51 already_AddRefed<DOMSVGAnimatedLength> SVGTextContentElement::TextLength() { 52 return LengthAttributes()[TEXTLENGTH].ToDOMAnimatedLength(this); 53 } 54 55 already_AddRefed<DOMSVGAnimatedEnumeration> 56 SVGTextContentElement::LengthAdjust() { 57 return EnumAttributes()[LENGTHADJUST].ToDOMAnimatedEnum(this); 58 } 59 60 //---------------------------------------------------------------------- 61 62 template <typename T> 63 static bool FragmentHasSkippableCharacter(const T* aBuffer, uint32_t aLength) { 64 for (uint32_t i = 0; i < aLength; i++) { 65 if (nsTextFrameUtils::IsSkippableCharacterForTransformText(aBuffer[i])) { 66 return true; 67 } 68 } 69 return false; 70 } 71 72 Maybe<int32_t> SVGTextContentElement::GetNonLayoutDependentNumberOfChars() { 73 SVGTextFrame* frame = GetSVGTextFrameForNonLayoutDependentQuery(); 74 if (!frame || frame != GetPrimaryFrame()) { 75 // Only support this fast path on <text>, not child <tspan>s, etc. 76 return Nothing(); 77 } 78 79 uint32_t num = 0; 80 81 for (nsINode* n = Element::GetFirstChild(); n; n = n->GetNextSibling()) { 82 if (!n->IsText()) { 83 return Nothing(); 84 } 85 86 const CharacterDataBuffer* characterDataBuffer = &n->AsText()->DataBuffer(); 87 uint32_t length = characterDataBuffer->GetLength(); 88 89 if (characterDataBuffer->Is2b()) { 90 if (FragmentHasSkippableCharacter(characterDataBuffer->Get2b(), length)) { 91 return Nothing(); 92 } 93 } else { 94 const auto* buffer = 95 reinterpret_cast<const uint8_t*>(characterDataBuffer->Get1b()); 96 if (FragmentHasSkippableCharacter(buffer, length)) { 97 return Nothing(); 98 } 99 } 100 101 num += length; 102 } 103 104 return Some(num); 105 } 106 107 int32_t SVGTextContentElement::GetNumberOfChars() { 108 Maybe<int32_t> num = GetNonLayoutDependentNumberOfChars(); 109 if (num) { 110 return *num; 111 } 112 113 SVGTextFrame* textFrame = GetSVGTextFrame(); 114 return textFrame ? textFrame->GetNumberOfChars(this) : 0; 115 } 116 117 float SVGTextContentElement::GetComputedTextLength() { 118 SVGTextFrame* textFrame = GetSVGTextFrame(); 119 return textFrame ? textFrame->GetComputedTextLength(this) : 0.0f; 120 } 121 122 void SVGTextContentElement::SelectSubString(uint32_t charnum, uint32_t nchars, 123 ErrorResult& rv) { 124 SVGTextFrame* textFrame = GetSVGTextFrame(); 125 if (!textFrame) return; 126 127 textFrame->SelectSubString(this, charnum, nchars, rv); 128 } 129 130 float SVGTextContentElement::GetSubStringLength(uint32_t charnum, 131 uint32_t nchars, 132 ErrorResult& rv) { 133 SVGTextFrame* textFrame = GetSVGTextFrameForNonLayoutDependentQuery(); 134 if (!textFrame) return 0.0f; 135 136 if (!textFrame->RequiresSlowFallbackForSubStringLength()) { 137 return textFrame->GetSubStringLengthFastPath(this, charnum, nchars, rv); 138 } 139 // We need to make sure that we've been reflowed before using the slow 140 // fallback path as it may affect glyph positioning. GetSVGTextFrame will do 141 // that for us. 142 // XXX perf: It may be possible to limit reflow to just calling ReflowSVG, 143 // but we would still need to resort to full reflow for percentage 144 // positioning attributes. For now we just do a full reflow regardless 145 // since the cases that would cause us to be called are relatively uncommon. 146 textFrame = GetSVGTextFrame(); 147 if (!textFrame) return 0.0f; 148 149 return textFrame->GetSubStringLengthSlowFallback(this, charnum, nchars, rv); 150 } 151 152 already_AddRefed<DOMSVGPoint> SVGTextContentElement::GetStartPositionOfChar( 153 uint32_t charnum, ErrorResult& rv) { 154 SVGTextFrame* textFrame = GetSVGTextFrame(); 155 if (!textFrame) { 156 rv.ThrowInvalidStateError("No layout information available for SVG text"); 157 return nullptr; 158 } 159 160 return textFrame->GetStartPositionOfChar(this, charnum, rv); 161 } 162 163 already_AddRefed<DOMSVGPoint> SVGTextContentElement::GetEndPositionOfChar( 164 uint32_t charnum, ErrorResult& rv) { 165 SVGTextFrame* textFrame = GetSVGTextFrame(); 166 if (!textFrame) { 167 rv.ThrowInvalidStateError("No layout information available for SVG text"); 168 return nullptr; 169 } 170 171 return textFrame->GetEndPositionOfChar(this, charnum, rv); 172 } 173 174 already_AddRefed<SVGRect> SVGTextContentElement::GetExtentOfChar( 175 uint32_t charnum, ErrorResult& rv) { 176 SVGTextFrame* textFrame = GetSVGTextFrame(); 177 178 if (!textFrame) { 179 rv.ThrowInvalidStateError("No layout information available for SVG text"); 180 return nullptr; 181 } 182 183 return textFrame->GetExtentOfChar(this, charnum, rv); 184 } 185 186 float SVGTextContentElement::GetRotationOfChar(uint32_t charnum, 187 ErrorResult& rv) { 188 SVGTextFrame* textFrame = GetSVGTextFrame(); 189 190 if (!textFrame) { 191 rv.ThrowInvalidStateError("No layout information available for SVG text"); 192 return 0.0f; 193 } 194 195 return textFrame->GetRotationOfChar(this, charnum, rv); 196 } 197 198 int32_t SVGTextContentElement::GetCharNumAtPosition( 199 const DOMPointInit& aPoint) { 200 SVGTextFrame* textFrame = GetSVGTextFrame(); 201 return textFrame ? textFrame->GetCharNumAtPosition(this, aPoint) : -1; 202 } 203 204 } // namespace mozilla::dom