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 "SVGNames.h" 26 #include "bindings/v8/ExceptionState.h" 27 #include "bindings/v8/ExceptionStatePlaceholder.h" 28 #include "core/css/resolver/StyleResolver.h" 29 #include "core/dom/ExceptionCode.h" 30 #include "core/dom/NodeRenderStyle.h" 31 #include "core/dom/NodeRenderingContext.h" 32 #include "core/dom/NodeTraversal.h" 33 #include "core/events/ScopedEventQueue.h" 34 #include "core/dom/shadow/ShadowRoot.h" 35 #include "core/rendering/RenderCombineText.h" 36 #include "core/rendering/RenderText.h" 37 #include "core/rendering/svg/RenderSVGInlineText.h" 38 #include "wtf/text/CString.h" 39 #include "wtf/text/StringBuilder.h" 40 41 using namespace std; 42 43 namespace WebCore { 44 45 PassRefPtr<Text> Text::create(Document& document, const String& data) 46 { 47 return adoptRef(new Text(document, data, CreateText)); 48 } 49 50 PassRefPtr<Text> Text::createEditingText(Document& document, const String& data) 51 { 52 return adoptRef(new Text(document, data, CreateEditingText)); 53 } 54 55 PassRefPtr<Node> Text::mergeNextSiblingNodesIfPossible() 56 { 57 RefPtr<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 RefPtr<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 RefPtr<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.get(), 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 PassRefPtr<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 0; 112 } 113 114 EventQueueScope scope; 115 String oldStr = data(); 116 RefPtr<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 0; 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 PassRefPtr<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 RefPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this)); 198 RefPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this)); 199 200 RefPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away 201 RefPtr<ContainerNode> parent = parentNode(); // Protect against mutation handlers moving this node during traversal 202 for (RefPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) { 203 RefPtr<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 (RefPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) { 211 RefPtr<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 0; 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 PassRefPtr<Node> Text::cloneNode(bool /*deep*/) 238 { 239 return cloneWithData(data()); 240 } 241 242 bool Text::textRendererIsNeeded(const NodeRenderingContext& context) 243 { 244 if (isEditingText()) 245 return true; 246 247 if (!length()) 248 return false; 249 250 if (context.style()->display() == NONE) 251 return false; 252 253 if (!containsOnlyWhitespace()) 254 return true; 255 256 RenderObject* parent = context.parentRenderer(); 257 if (!parent->canHaveWhitespaceChildren()) 258 return false; 259 260 if (context.style()->preserveNewline()) // pre/pre-wrap/pre-line always make renderers. 261 return true; 262 263 RenderObject* prev = context.previousRenderer(); 264 if (prev && prev->isBR()) // <span><br/> <br/></span> 265 return false; 266 267 if (parent->isRenderInline()) { 268 // <span><div/> <div/></span> 269 if (prev && !prev->isInline()) 270 return false; 271 } else { 272 if (parent->isRenderBlock() && !parent->childrenInline() && (!prev || !prev->isInline())) 273 return false; 274 275 // Avoiding creation of a Renderer for the text node is a non-essential memory optimization. 276 // So to avoid blowing up on very wide DOMs, we limit the number of siblings to visit. 277 unsigned maxSiblingsToVisit = 50; 278 279 RenderObject* first = parent->firstChild(); 280 while (first && first->isFloatingOrOutOfFlowPositioned() && maxSiblingsToVisit--) 281 first = first->nextSibling(); 282 if (!first || context.nextRenderer() == first) 283 // Whitespace at the start of a block just goes away. Don't even 284 // make a render object for this text. 285 return false; 286 } 287 return true; 288 } 289 290 static bool isSVGText(Text* text) 291 { 292 Node* parentOrShadowHostNode = text->parentOrShadowHostNode(); 293 return parentOrShadowHostNode->isSVGElement() && !parentOrShadowHostNode->hasTagName(SVGNames::foreignObjectTag); 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 NodeRenderingContext(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(NodeRenderingContext(this, textRenderer->style()))) { 343 lazyReattachIfAttached(); 344 // FIXME: Editing should be updated so this is not neccesary. 345 if (recalcStyleBehavior == DeprecatedRecalcStyleImmediatlelyForEditing) 346 document().updateStyleIfNeeded(); 347 return; 348 } 349 textRenderer->setTextWithOffset(dataImpl(), offsetOfReplacedData, lengthOfReplacedData); 350 } 351 352 bool Text::childTypeAllowed(NodeType) const 353 { 354 return false; 355 } 356 357 PassRefPtr<Text> Text::cloneWithData(const String& data) 358 { 359 return create(document(), data); 360 } 361 362 #ifndef NDEBUG 363 void Text::formatForDebugger(char *buffer, unsigned length) const 364 { 365 StringBuilder result; 366 String s; 367 368 result.append(nodeName()); 369 370 s = data(); 371 if (s.length() > 0) { 372 if (result.length()) 373 result.appendLiteral("; "); 374 result.appendLiteral("value="); 375 result.append(s); 376 } 377 378 strncpy(buffer, result.toString().utf8().data(), length - 1); 379 } 380 #endif 381 382 } // namespace WebCore 383