1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 "core/dom/Text.h" 24 25 #include "bindings/v8/ExceptionState.h" 26 #include "bindings/v8/ExceptionStatePlaceholder.h" 27 #include "core/SVGNames.h" 28 #include "core/css/resolver/StyleResolver.h" 29 #include "core/dom/ExceptionCode.h" 30 #include "core/dom/NodeRenderStyle.h" 31 #include "core/dom/NodeRenderingTraversal.h" 32 #include "core/dom/NodeTraversal.h" 33 #include "core/dom/RenderTreeBuilder.h" 34 #include "core/dom/shadow/ShadowRoot.h" 35 #include "core/events/ScopedEventQueue.h" 36 #include "core/rendering/RenderCombineText.h" 37 #include "core/rendering/RenderText.h" 38 #include "core/rendering/svg/RenderSVGInlineText.h" 39 #include "core/svg/SVGForeignObjectElement.h" 40 #include "wtf/text/CString.h" 41 #include "wtf/text/StringBuilder.h" 42 43 namespace WebCore { 44 45 PassRefPtrWillBeRawPtr<Text> Text::create(Document& document, const String& data) 46 { 47 return adoptRefWillBeNoop(new Text(document, data, CreateText)); 48 } 49 50 PassRefPtrWillBeRawPtr<Text> Text::createEditingText(Document& document, const String& data) 51 { 52 return adoptRefWillBeNoop(new Text(document, data, CreateEditingText)); 53 } 54 55 PassRefPtrWillBeRawPtr<Node> Text::mergeNextSiblingNodesIfPossible() 56 { 57 RefPtrWillBeRawPtr<Node> protect(this); 58 59 // Remove empty text nodes. 60 if (!length()) { 61 // Care must be taken to get the next node before removing the current node. 62 RefPtrWillBeRawPtr<Node> nextNode(NodeTraversal::nextPostOrder(*this)); 63 remove(IGNORE_EXCEPTION); 64 return nextNode.release(); 65 } 66 67 // Merge text nodes. 68 while (Node* nextSibling = this->nextSibling()) { 69 if (nextSibling->nodeType() != TEXT_NODE) 70 break; 71 72 RefPtrWillBeRawPtr<Text> nextText = toText(nextSibling); 73 74 // Remove empty text nodes. 75 if (!nextText->length()) { 76 nextText->remove(IGNORE_EXCEPTION); 77 continue; 78 } 79 80 // Both non-empty text nodes. Merge them. 81 unsigned offset = length(); 82 String nextTextData = nextText->data(); 83 String oldTextData = data(); 84 setDataWithoutUpdate(data() + nextTextData); 85 updateTextRenderer(oldTextData.length(), 0); 86 87 // Empty nextText for layout update. 88 nextText->setDataWithoutUpdate(emptyString()); 89 nextText->updateTextRenderer(0, nextTextData.length()); 90 91 document().didMergeTextNodes(*nextText, offset); 92 93 // Restore nextText for mutation event. 94 nextText->setDataWithoutUpdate(nextTextData); 95 nextText->updateTextRenderer(0, 0); 96 97 document().incDOMTreeVersion(); 98 didModifyData(oldTextData); 99 nextText->remove(IGNORE_EXCEPTION); 100 } 101 102 return NodeTraversal::nextPostOrder(*this); 103 } 104 105 PassRefPtrWillBeRawPtr<Text> Text::splitText(unsigned offset, ExceptionState& exceptionState) 106 { 107 // IndexSizeError: Raised if the specified offset is negative or greater than 108 // the number of 16-bit units in data. 109 if (offset > length()) { 110 exceptionState.throwDOMException(IndexSizeError, "The offset " + String::number(offset) + " is larger than the Text node's length."); 111 return nullptr; 112 } 113 114 EventQueueScope scope; 115 String oldStr = data(); 116 RefPtrWillBeRawPtr<Text> newText = cloneWithData(oldStr.substring(offset)); 117 setDataWithoutUpdate(oldStr.substring(0, offset)); 118 119 didModifyData(oldStr); 120 121 if (parentNode()) 122 parentNode()->insertBefore(newText.get(), nextSibling(), exceptionState); 123 if (exceptionState.hadException()) 124 return nullptr; 125 126 if (renderer()) 127 toRenderText(renderer())->setTextWithOffset(dataImpl(), 0, oldStr.length()); 128 129 if (parentNode()) 130 document().didSplitTextNode(*this); 131 132 return newText.release(); 133 } 134 135 static const Text* earliestLogicallyAdjacentTextNode(const Text* t) 136 { 137 const Node* n = t; 138 while ((n = n->previousSibling())) { 139 Node::NodeType type = n->nodeType(); 140 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 141 t = toText(n); 142 continue; 143 } 144 145 break; 146 } 147 return t; 148 } 149 150 static const Text* latestLogicallyAdjacentTextNode(const Text* t) 151 { 152 const Node* n = t; 153 while ((n = n->nextSibling())) { 154 Node::NodeType type = n->nodeType(); 155 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 156 t = toText(n); 157 continue; 158 } 159 160 break; 161 } 162 return t; 163 } 164 165 String Text::wholeText() const 166 { 167 const Text* startText = earliestLogicallyAdjacentTextNode(this); 168 const Text* endText = latestLogicallyAdjacentTextNode(this); 169 170 Node* onePastEndText = endText->nextSibling(); 171 unsigned resultLength = 0; 172 for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) { 173 if (!n->isTextNode()) 174 continue; 175 const String& data = toText(n)->data(); 176 if (std::numeric_limits<unsigned>::max() - data.length() < resultLength) 177 CRASH(); 178 resultLength += data.length(); 179 } 180 StringBuilder result; 181 result.reserveCapacity(resultLength); 182 for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) { 183 if (!n->isTextNode()) 184 continue; 185 result.append(toText(n)->data()); 186 } 187 ASSERT(result.length() == resultLength); 188 189 return result.toString(); 190 } 191 192 PassRefPtrWillBeRawPtr<Text> Text::replaceWholeText(const String& newText) 193 { 194 // Remove all adjacent text nodes, and replace the contents of this one. 195 196 // Protect startText and endText against mutation event handlers removing the last ref 197 RefPtrWillBeRawPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this)); 198 RefPtrWillBeRawPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this)); 199 200 RefPtrWillBeRawPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away 201 RefPtrWillBeRawPtr<ContainerNode> parent = parentNode(); // Protect against mutation handlers moving this node during traversal 202 for (RefPtrWillBeRawPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) { 203 RefPtrWillBeRawPtr<Node> nodeToRemove(n.release()); 204 n = nodeToRemove->nextSibling(); 205 parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION); 206 } 207 208 if (this != endText) { 209 Node* onePastEndText = endText->nextSibling(); 210 for (RefPtrWillBeRawPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) { 211 RefPtrWillBeRawPtr<Node> nodeToRemove(n.release()); 212 n = nodeToRemove->nextSibling(); 213 parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION); 214 } 215 } 216 217 if (newText.isEmpty()) { 218 if (parent && parentNode() == parent) 219 parent->removeChild(this, IGNORE_EXCEPTION); 220 return nullptr; 221 } 222 223 setData(newText); 224 return protectedThis.release(); 225 } 226 227 String Text::nodeName() const 228 { 229 return "#text"; 230 } 231 232 Node::NodeType Text::nodeType() const 233 { 234 return TEXT_NODE; 235 } 236 237 PassRefPtrWillBeRawPtr<Node> Text::cloneNode(bool /*deep*/) 238 { 239 return cloneWithData(data()); 240 } 241 242 bool Text::textRendererIsNeeded(const RenderStyle& style, const RenderObject& parent) 243 { 244 if (isEditingText()) 245 return true; 246 247 if (!length()) 248 return false; 249 250 if (style.display() == NONE) 251 return false; 252 253 if (!containsOnlyWhitespace()) 254 return true; 255 256 if (!parent.canHaveWhitespaceChildren()) 257 return false; 258 259 if (style.preserveNewline()) // pre/pre-wrap/pre-line always make renderers. 260 return true; 261 262 const RenderObject* prev = NodeRenderingTraversal::previousSiblingRenderer(this); 263 if (prev && prev->isBR()) // <span><br/> <br/></span> 264 return false; 265 266 if (parent.isRenderInline()) { 267 // <span><div/> <div/></span> 268 if (prev && !prev->isInline()) 269 return false; 270 } else { 271 if (parent.isRenderBlock() && !parent.childrenInline() && (!prev || !prev->isInline())) 272 return false; 273 274 // Avoiding creation of a Renderer for the text node is a non-essential memory optimization. 275 // So to avoid blowing up on very wide DOMs, we limit the number of siblings to visit. 276 unsigned maxSiblingsToVisit = 50; 277 278 RenderObject* first = parent.slowFirstChild(); 279 while (first && first->isFloatingOrOutOfFlowPositioned() && maxSiblingsToVisit--) 280 first = first->nextSibling(); 281 if (!first || NodeRenderingTraversal::nextSiblingRenderer(this) == first) 282 // Whitespace at the start of a block just goes away. Don't even 283 // make a render object for this text. 284 return false; 285 } 286 return true; 287 } 288 289 static bool isSVGText(Text* text) 290 { 291 Node* parentOrShadowHostNode = text->parentOrShadowHostNode(); 292 ASSERT(parentOrShadowHostNode); 293 return parentOrShadowHostNode->isSVGElement() && !isSVGForeignObjectElement(*parentOrShadowHostNode); 294 } 295 296 RenderText* Text::createTextRenderer(RenderStyle* style) 297 { 298 if (isSVGText(this)) 299 return new RenderSVGInlineText(this, dataImpl()); 300 301 if (style->hasTextCombine()) 302 return new RenderCombineText(this, dataImpl()); 303 304 return new RenderText(this, dataImpl()); 305 } 306 307 void Text::attach(const AttachContext& context) 308 { 309 RenderTreeBuilder(this, context.resolvedStyle).createRendererForTextIfNeeded(); 310 CharacterData::attach(context); 311 } 312 313 void Text::recalcTextStyle(StyleRecalcChange change, Text* nextTextSibling) 314 { 315 if (RenderText* renderer = toRenderText(this->renderer())) { 316 if (change != NoChange || needsStyleRecalc()) 317 renderer->setStyle(document().ensureStyleResolver().styleForText(this)); 318 if (needsStyleRecalc()) 319 renderer->setText(dataImpl()); 320 clearNeedsStyleRecalc(); 321 } else if (needsStyleRecalc() || needsWhitespaceRenderer()) { 322 reattach(); 323 reattachWhitespaceSiblings(nextTextSibling); 324 } 325 } 326 327 // If a whitespace node had no renderer and goes through a recalcStyle it may 328 // need to create one if the parent style now has white-space: pre. 329 bool Text::needsWhitespaceRenderer() 330 { 331 ASSERT(!renderer()); 332 if (RenderStyle* style = parentRenderStyle()) 333 return style->preserveNewline(); 334 return false; 335 } 336 337 void Text::updateTextRenderer(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData, RecalcStyleBehavior recalcStyleBehavior) 338 { 339 if (!inActiveDocument()) 340 return; 341 RenderText* textRenderer = toRenderText(renderer()); 342 if (!textRenderer || !textRendererIsNeeded(*textRenderer->style(), *textRenderer->parent())) { 343 lazyReattachIfAttached(); 344 // FIXME: Editing should be updated so this is not neccesary. 345 if (recalcStyleBehavior == DeprecatedRecalcStyleImmediatlelyForEditing) 346 document().updateRenderTreeIfNeeded(); 347 return; 348 } 349 textRenderer->setTextWithOffset(dataImpl(), offsetOfReplacedData, lengthOfReplacedData); 350 } 351 352 PassRefPtrWillBeRawPtr<Text> Text::cloneWithData(const String& data) 353 { 354 return create(document(), data); 355 } 356 357 #ifndef NDEBUG 358 void Text::formatForDebugger(char *buffer, unsigned length) const 359 { 360 StringBuilder result; 361 String s; 362 363 result.append(nodeName()); 364 365 s = data(); 366 if (s.length() > 0) { 367 if (result.length()) 368 result.appendLiteral("; "); 369 result.appendLiteral("value="); 370 result.append(s); 371 } 372 373 strncpy(buffer, result.toString().utf8().data(), length - 1); 374 } 375 #endif 376 377 } // namespace WebCore 378