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/core/v8/ExceptionState.h" 26 #include "bindings/core/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 blink { 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 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 for (const Node* n = t->previousSibling(); n; n = n->previousSibling()) { 138 Node::NodeType type = n->nodeType(); 139 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 140 t = toText(n); 141 continue; 142 } 143 144 break; 145 } 146 return t; 147 } 148 149 static const Text* latestLogicallyAdjacentTextNode(const Text* t) 150 { 151 for (const Node* n = t->nextSibling(); n; n = n->nextSibling()) { 152 Node::NodeType type = n->nodeType(); 153 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 154 t = toText(n); 155 continue; 156 } 157 158 break; 159 } 160 return t; 161 } 162 163 String Text::wholeText() const 164 { 165 const Text* startText = earliestLogicallyAdjacentTextNode(this); 166 const Text* endText = latestLogicallyAdjacentTextNode(this); 167 168 Node* onePastEndText = endText->nextSibling(); 169 unsigned resultLength = 0; 170 for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) { 171 if (!n->isTextNode()) 172 continue; 173 const String& data = toText(n)->data(); 174 if (std::numeric_limits<unsigned>::max() - data.length() < resultLength) 175 CRASH(); 176 resultLength += data.length(); 177 } 178 StringBuilder result; 179 result.reserveCapacity(resultLength); 180 for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) { 181 if (!n->isTextNode()) 182 continue; 183 result.append(toText(n)->data()); 184 } 185 ASSERT(result.length() == resultLength); 186 187 return result.toString(); 188 } 189 190 PassRefPtrWillBeRawPtr<Text> Text::replaceWholeText(const String& newText) 191 { 192 // Remove all adjacent text nodes, and replace the contents of this one. 193 194 // Protect startText and endText against mutation event handlers removing the last ref 195 RefPtrWillBeRawPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this)); 196 RefPtrWillBeRawPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this)); 197 198 RefPtrWillBeRawPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away 199 RefPtrWillBeRawPtr<ContainerNode> parent = parentNode(); // Protect against mutation handlers moving this node during traversal 200 for (RefPtrWillBeRawPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) { 201 RefPtrWillBeRawPtr<Node> nodeToRemove(n.release()); 202 n = nodeToRemove->nextSibling(); 203 parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION); 204 } 205 206 if (this != endText) { 207 Node* onePastEndText = endText->nextSibling(); 208 for (RefPtrWillBeRawPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) { 209 RefPtrWillBeRawPtr<Node> nodeToRemove(n.release()); 210 n = nodeToRemove->nextSibling(); 211 parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION); 212 } 213 } 214 215 if (newText.isEmpty()) { 216 if (parent && parentNode() == parent) 217 parent->removeChild(this, IGNORE_EXCEPTION); 218 return nullptr; 219 } 220 221 setData(newText); 222 return protectedThis.release(); 223 } 224 225 String Text::nodeName() const 226 { 227 return "#text"; 228 } 229 230 Node::NodeType Text::nodeType() const 231 { 232 return TEXT_NODE; 233 } 234 235 PassRefPtrWillBeRawPtr<Node> Text::cloneNode(bool /*deep*/) 236 { 237 return cloneWithData(data()); 238 } 239 240 bool Text::textRendererIsNeeded(const RenderStyle& style, const RenderObject& parent) 241 { 242 if (isEditingText()) 243 return true; 244 245 if (!length()) 246 return false; 247 248 if (style.display() == NONE) 249 return false; 250 251 if (!containsOnlyWhitespace()) 252 return true; 253 254 if (!parent.canHaveWhitespaceChildren()) 255 return false; 256 257 if (style.preserveNewline()) // pre/pre-wrap/pre-line always make renderers. 258 return true; 259 260 const RenderObject* prev = NodeRenderingTraversal::previousSiblingRenderer(this); 261 if (prev && prev->isBR()) // <span><br/> <br/></span> 262 return false; 263 264 if (parent.isRenderInline()) { 265 // <span><div/> <div/></span> 266 if (prev && !prev->isInline()) 267 return false; 268 } else { 269 if (parent.isRenderBlock() && !parent.childrenInline() && (!prev || !prev->isInline())) 270 return false; 271 272 // Avoiding creation of a Renderer for the text node is a non-essential memory optimization. 273 // So to avoid blowing up on very wide DOMs, we limit the number of siblings to visit. 274 unsigned maxSiblingsToVisit = 50; 275 276 RenderObject* first = parent.slowFirstChild(); 277 while (first && first->isFloatingOrOutOfFlowPositioned() && maxSiblingsToVisit--) 278 first = first->nextSibling(); 279 if (!first || NodeRenderingTraversal::nextSiblingRenderer(this) == first) 280 // Whitespace at the start of a block just goes away. Don't even 281 // make a render object for this text. 282 return false; 283 } 284 return true; 285 } 286 287 static bool isSVGText(Text* text) 288 { 289 Node* parentOrShadowHostNode = text->parentOrShadowHostNode(); 290 ASSERT(parentOrShadowHostNode); 291 return parentOrShadowHostNode->isSVGElement() && !isSVGForeignObjectElement(*parentOrShadowHostNode); 292 } 293 294 RenderText* Text::createTextRenderer(RenderStyle* style) 295 { 296 if (isSVGText(this)) 297 return new RenderSVGInlineText(this, dataImpl()); 298 299 if (style->hasTextCombine()) 300 return new RenderCombineText(this, dataImpl()); 301 302 return new RenderText(this, dataImpl()); 303 } 304 305 void Text::attach(const AttachContext& context) 306 { 307 RenderTreeBuilder(this, context.resolvedStyle).createRendererForTextIfNeeded(); 308 CharacterData::attach(context); 309 } 310 311 void Text::recalcTextStyle(StyleRecalcChange change, Text* nextTextSibling) 312 { 313 if (RenderText* renderer = this->renderer()) { 314 if (change != NoChange || needsStyleRecalc()) 315 renderer->setStyle(document().ensureStyleResolver().styleForText(this)); 316 if (needsStyleRecalc()) 317 renderer->setText(dataImpl()); 318 clearNeedsStyleRecalc(); 319 } else if (needsStyleRecalc() || needsWhitespaceRenderer()) { 320 reattach(); 321 if (this->renderer()) 322 reattachWhitespaceSiblings(nextTextSibling); 323 } 324 } 325 326 // If a whitespace node had no renderer and goes through a recalcStyle it may 327 // need to create one if the parent style now has white-space: pre. 328 bool Text::needsWhitespaceRenderer() 329 { 330 ASSERT(!renderer()); 331 if (RenderStyle* style = parentRenderStyle()) 332 return style->preserveNewline(); 333 return false; 334 } 335 336 void Text::updateTextRenderer(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData, RecalcStyleBehavior recalcStyleBehavior) 337 { 338 if (!inActiveDocument()) 339 return; 340 RenderText* textRenderer = renderer(); 341 if (!textRenderer || !textRendererIsNeeded(*textRenderer->style(), *textRenderer->parent())) { 342 lazyReattachIfAttached(); 343 // FIXME: Editing should be updated so this is not neccesary. 344 if (recalcStyleBehavior == DeprecatedRecalcStyleImmediatlelyForEditing) 345 document().updateRenderTreeIfNeeded(); 346 return; 347 } 348 textRenderer->setTextWithOffset(dataImpl(), offsetOfReplacedData, lengthOfReplacedData); 349 } 350 351 PassRefPtrWillBeRawPtr<Text> Text::cloneWithData(const String& data) 352 { 353 return create(document(), data); 354 } 355 356 #ifndef NDEBUG 357 void Text::formatForDebugger(char *buffer, unsigned length) const 358 { 359 StringBuilder result; 360 String s; 361 362 result.append(nodeName()); 363 364 s = data(); 365 if (s.length() > 0) { 366 if (result.length()) 367 result.appendLiteral("; "); 368 result.appendLiteral("value="); 369 result.append(s); 370 } 371 372 strncpy(buffer, result.toString().utf8().data(), length - 1); 373 } 374 #endif 375 376 } // namespace blink 377