| 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 | |