1 | /* |
2 | * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 | * (C) 1999 Antti Koivisto (koivisto@kde.org) |
4 | * Copyright (C) 2003-2018 Apple Inc. All rights reserved. |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public License |
17 | * along with this library; see the file COPYING.LIB. If not, write to |
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | */ |
21 | |
22 | #include "config.h" |
23 | #include "Text.h" |
24 | |
25 | #include "Event.h" |
26 | #include "RenderCombineText.h" |
27 | #include "RenderSVGInlineText.h" |
28 | #include "RenderText.h" |
29 | #include "SVGElement.h" |
30 | #include "SVGNames.h" |
31 | #include "ScopedEventQueue.h" |
32 | #include "ShadowRoot.h" |
33 | #include "StyleInheritedData.h" |
34 | #include "StyleResolver.h" |
35 | #include "StyleUpdate.h" |
36 | #include "TextNodeTraversal.h" |
37 | #include <wtf/CheckedArithmetic.h> |
38 | #include <wtf/IsoMallocInlines.h> |
39 | #include <wtf/text/CString.h> |
40 | #include <wtf/text/StringBuilder.h> |
41 | |
42 | namespace WebCore { |
43 | |
44 | WTF_MAKE_ISO_ALLOCATED_IMPL(Text); |
45 | |
46 | Ref<Text> Text::create(Document& document, const String& data) |
47 | { |
48 | return adoptRef(*new Text(document, data, CreateText)); |
49 | } |
50 | |
51 | Ref<Text> Text::createEditingText(Document& document, const String& data) |
52 | { |
53 | return adoptRef(*new Text(document, data, CreateEditingText)); |
54 | } |
55 | |
56 | Text::~Text() = default; |
57 | |
58 | ExceptionOr<Ref<Text>> Text::splitText(unsigned offset) |
59 | { |
60 | if (offset > length()) |
61 | return Exception { IndexSizeError }; |
62 | |
63 | EventQueueScope scope; |
64 | auto oldData = data(); |
65 | auto newText = virtualCreate(oldData.substring(offset)); |
66 | setDataWithoutUpdate(oldData.substring(0, offset)); |
67 | |
68 | dispatchModifiedEvent(oldData); |
69 | |
70 | if (auto* parent = parentNode()) { |
71 | auto insertResult = parent->insertBefore(newText, nextSibling()); |
72 | if (insertResult.hasException()) |
73 | return insertResult.releaseException(); |
74 | } |
75 | |
76 | document().textNodeSplit(*this); |
77 | |
78 | if (renderer()) |
79 | renderer()->setTextWithOffset(data(), 0, oldData.length()); |
80 | |
81 | return newText; |
82 | } |
83 | |
84 | static const Text* earliestLogicallyAdjacentTextNode(const Text* text) |
85 | { |
86 | const Node* node = text; |
87 | while ((node = node->previousSibling())) { |
88 | if (!is<Text>(*node)) |
89 | break; |
90 | text = downcast<Text>(node); |
91 | } |
92 | return text; |
93 | } |
94 | |
95 | static const Text* latestLogicallyAdjacentTextNode(const Text* text) |
96 | { |
97 | const Node* node = text; |
98 | while ((node = node->nextSibling())) { |
99 | if (!is<Text>(*node)) |
100 | break; |
101 | text = downcast<Text>(node); |
102 | } |
103 | return text; |
104 | } |
105 | |
106 | String Text::wholeText() const |
107 | { |
108 | const Text* startText = earliestLogicallyAdjacentTextNode(this); |
109 | const Text* endText = latestLogicallyAdjacentTextNode(this); |
110 | ASSERT(endText); |
111 | const Node* onePastEndText = TextNodeTraversal::nextSibling(*endText); |
112 | |
113 | StringBuilder result; |
114 | for (const Text* text = startText; text != onePastEndText; text = TextNodeTraversal::nextSibling(*text)) |
115 | result.append(text->data()); |
116 | return result.toString(); |
117 | } |
118 | |
119 | RefPtr<Text> Text::replaceWholeText(const String& newText) |
120 | { |
121 | // Remove all adjacent text nodes, and replace the contents of this one. |
122 | |
123 | // Protect startText and endText against mutation event handlers removing the last ref |
124 | RefPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this)); |
125 | RefPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this)); |
126 | |
127 | RefPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away |
128 | RefPtr<ContainerNode> parent = parentNode(); // Protect against mutation handlers moving this node during traversal |
129 | for (RefPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) { |
130 | Ref<Node> nodeToRemove(n.releaseNonNull()); |
131 | n = nodeToRemove->nextSibling(); |
132 | parent->removeChild(nodeToRemove); |
133 | } |
134 | |
135 | if (this != endText) { |
136 | Node* onePastEndText = endText->nextSibling(); |
137 | for (RefPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) { |
138 | Ref<Node> nodeToRemove(n.releaseNonNull()); |
139 | n = nodeToRemove->nextSibling(); |
140 | parent->removeChild(nodeToRemove); |
141 | } |
142 | } |
143 | |
144 | if (newText.isEmpty()) { |
145 | if (parent && parentNode() == parent) |
146 | parent->removeChild(*this); |
147 | return nullptr; |
148 | } |
149 | |
150 | setData(newText); |
151 | return protectedThis; |
152 | } |
153 | |
154 | String Text::nodeName() const |
155 | { |
156 | return "#text"_s ; |
157 | } |
158 | |
159 | Node::NodeType Text::nodeType() const |
160 | { |
161 | return TEXT_NODE; |
162 | } |
163 | |
164 | Ref<Node> Text::cloneNodeInternal(Document& targetDocument, CloningOperation) |
165 | { |
166 | return create(targetDocument, data()); |
167 | } |
168 | |
169 | static bool isSVGShadowText(Text* text) |
170 | { |
171 | Node* parentNode = text->parentNode(); |
172 | ASSERT(parentNode); |
173 | return is<ShadowRoot>(*parentNode) && downcast<ShadowRoot>(*parentNode).host()->hasTagName(SVGNames::trefTag); |
174 | } |
175 | |
176 | static bool isSVGText(Text* text) |
177 | { |
178 | Node* parentOrShadowHostNode = text->parentOrShadowHostNode(); |
179 | return parentOrShadowHostNode->isSVGElement() && !parentOrShadowHostNode->hasTagName(SVGNames::foreignObjectTag); |
180 | } |
181 | |
182 | RenderPtr<RenderText> Text::createTextRenderer(const RenderStyle& style) |
183 | { |
184 | if (isSVGText(this) || isSVGShadowText(this)) |
185 | return createRenderer<RenderSVGInlineText>(*this, data()); |
186 | |
187 | if (style.hasTextCombine()) |
188 | return createRenderer<RenderCombineText>(*this, data()); |
189 | |
190 | return createRenderer<RenderText>(*this, data()); |
191 | } |
192 | |
193 | bool Text::childTypeAllowed(NodeType) const |
194 | { |
195 | return false; |
196 | } |
197 | |
198 | Ref<Text> Text::virtualCreate(const String& data) |
199 | { |
200 | return create(document(), data); |
201 | } |
202 | |
203 | Ref<Text> Text::createWithLengthLimit(Document& document, const String& data, unsigned start, unsigned lengthLimit) |
204 | { |
205 | unsigned dataLength = data.length(); |
206 | |
207 | if (!start && dataLength <= lengthLimit) |
208 | return create(document, data); |
209 | |
210 | Ref<Text> result = Text::create(document, String()); |
211 | result->parserAppendData(data, start, lengthLimit); |
212 | return result; |
213 | } |
214 | |
215 | void Text::updateRendererAfterContentChange(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData) |
216 | { |
217 | ASSERT(parentNode()); |
218 | if (styleValidity() >= Style::Validity::SubtreeAndRenderersInvalid) |
219 | return; |
220 | |
221 | document().updateTextRenderer(*this, offsetOfReplacedData, lengthOfReplacedData); |
222 | } |
223 | |
224 | #if ENABLE(TREE_DEBUGGING) |
225 | void Text::formatForDebugger(char* buffer, unsigned length) const |
226 | { |
227 | StringBuilder result; |
228 | String s; |
229 | |
230 | result.append(nodeName()); |
231 | |
232 | s = data(); |
233 | if (s.length() > 0) { |
234 | if (result.length()) |
235 | result.appendLiteral("; " ); |
236 | result.appendLiteral("length=" ); |
237 | result.appendNumber(s.length()); |
238 | result.appendLiteral("; value=\"" ); |
239 | result.append(s); |
240 | result.append('"'); |
241 | } |
242 | |
243 | strncpy(buffer, result.toString().utf8().data(), length - 1); |
244 | buffer[length - 1] = '\0'; |
245 | } |
246 | #endif |
247 | |
248 | } // namespace WebCore |
249 | |