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/ScopedEventQueue.h" 33 #include "core/dom/shadow/ShadowRoot.h" 34 #include "core/rendering/RenderCombineText.h" 35 #include "core/rendering/RenderText.h" 36 #include "core/rendering/svg/RenderSVGInlineText.h" 37 #include "wtf/text/CString.h" 38 #include "wtf/text/StringBuilder.h" 39 40 using namespace std; 41 42 namespace WebCore { 43 44 PassRefPtr<Text> Text::create(Document* document, const String& data) 45 { 46 return adoptRef(new Text(document, data, CreateText)); 47 } 48 49 PassRefPtr<Text> Text::createEditingText(Document* document, const String& data) 50 { 51 return adoptRef(new Text(document, data, CreateEditingText)); 52 } 53 54 PassRefPtr<Text> Text::splitText(unsigned offset, ExceptionState& es) 55 { 56 // IndexSizeError: Raised if the specified offset is negative or greater than 57 // the number of 16-bit units in data. 58 if (offset > length()) { 59 es.throwDOMException(IndexSizeError); 60 return 0; 61 } 62 63 EventQueueScope scope; 64 String oldStr = data(); 65 RefPtr<Text> newText = cloneWithData(oldStr.substring(offset)); 66 setDataWithoutUpdate(oldStr.substring(0, offset)); 67 68 didModifyData(oldStr); 69 70 if (parentNode()) 71 parentNode()->insertBefore(newText.get(), nextSibling(), es); 72 if (es.hadException()) 73 return 0; 74 75 if (parentNode()) 76 document()->textNodeSplit(this); 77 78 if (renderer()) 79 toRenderText(renderer())->setTextWithOffset(dataImpl(), 0, oldStr.length()); 80 81 return newText.release(); 82 } 83 84 static const Text* earliestLogicallyAdjacentTextNode(const Text* t) 85 { 86 const Node* n = t; 87 while ((n = n->previousSibling())) { 88 Node::NodeType type = n->nodeType(); 89 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 90 t = static_cast<const Text*>(n); 91 continue; 92 } 93 94 break; 95 } 96 return t; 97 } 98 99 static const Text* latestLogicallyAdjacentTextNode(const Text* t) 100 { 101 const Node* n = t; 102 while ((n = n->nextSibling())) { 103 Node::NodeType type = n->nodeType(); 104 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 105 t = static_cast<const Text*>(n); 106 continue; 107 } 108 109 break; 110 } 111 return t; 112 } 113 114 String Text::wholeText() const 115 { 116 const Text* startText = earliestLogicallyAdjacentTextNode(this); 117 const Text* endText = latestLogicallyAdjacentTextNode(this); 118 119 Node* onePastEndText = endText->nextSibling(); 120 unsigned resultLength = 0; 121 for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) { 122 if (!n->isTextNode()) 123 continue; 124 const Text* t = static_cast<const Text*>(n); 125 const String& data = t->data(); 126 if (std::numeric_limits<unsigned>::max() - data.length() < resultLength) 127 CRASH(); 128 resultLength += data.length(); 129 } 130 StringBuilder result; 131 result.reserveCapacity(resultLength); 132 for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) { 133 if (!n->isTextNode()) 134 continue; 135 const Text* t = static_cast<const Text*>(n); 136 result.append(t->data()); 137 } 138 ASSERT(result.length() == resultLength); 139 140 return result.toString(); 141 } 142 143 PassRefPtr<Text> Text::replaceWholeText(const String& newText) 144 { 145 // Remove all adjacent text nodes, and replace the contents of this one. 146 147 // Protect startText and endText against mutation event handlers removing the last ref 148 RefPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this)); 149 RefPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this)); 150 151 RefPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away 152 RefPtr<ContainerNode> parent = parentNode(); // Protect against mutation handlers moving this node during traversal 153 for (RefPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) { 154 RefPtr<Node> nodeToRemove(n.release()); 155 n = nodeToRemove->nextSibling(); 156 parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION); 157 } 158 159 if (this != endText) { 160 Node* onePastEndText = endText->nextSibling(); 161 for (RefPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) { 162 RefPtr<Node> nodeToRemove(n.release()); 163 n = nodeToRemove->nextSibling(); 164 parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION); 165 } 166 } 167 168 if (newText.isEmpty()) { 169 if (parent && parentNode() == parent) 170 parent->removeChild(this, IGNORE_EXCEPTION); 171 return 0; 172 } 173 174 setData(newText); 175 return protectedThis.release(); 176 } 177 178 String Text::nodeName() const 179 { 180 return textAtom.string(); 181 } 182 183 Node::NodeType Text::nodeType() const 184 { 185 return TEXT_NODE; 186 } 187 188 PassRefPtr<Node> Text::cloneNode(bool /*deep*/) 189 { 190 return cloneWithData(data()); 191 } 192 193 bool Text::textRendererIsNeeded(const NodeRenderingContext& context) 194 { 195 if (isEditingText()) 196 return true; 197 198 if (!length()) 199 return false; 200 201 if (context.style()->display() == NONE) 202 return false; 203 204 if (!containsOnlyWhitespace()) 205 return true; 206 207 RenderObject* parent = context.parentRenderer(); 208 if (!parent->canHaveWhitespaceChildren()) 209 return false; 210 211 if (context.style()->preserveNewline()) // pre/pre-wrap/pre-line always make renderers. 212 return true; 213 214 RenderObject* prev = context.previousRenderer(); 215 if (prev && prev->isBR()) // <span><br/> <br/></span> 216 return false; 217 218 if (parent->isRenderInline()) { 219 // <span><div/> <div/></span> 220 if (prev && !prev->isInline()) 221 return false; 222 } else { 223 if (parent->isRenderBlock() && !parent->childrenInline() && (!prev || !prev->isInline())) 224 return false; 225 226 // Avoiding creation of a Renderer for the text node is a non-essential memory optimization. 227 // So to avoid blowing up on very wide DOMs, we limit the number of siblings to visit. 228 unsigned maxSiblingsToVisit = 50; 229 230 RenderObject* first = parent->firstChild(); 231 while (first && first->isFloatingOrOutOfFlowPositioned() && maxSiblingsToVisit--) 232 first = first->nextSibling(); 233 if (!first || context.nextRenderer() == first) 234 // Whitespace at the start of a block just goes away. Don't even 235 // make a render object for this text. 236 return false; 237 } 238 return true; 239 } 240 241 static bool isSVGShadowText(Text* text) 242 { 243 Node* parentNode = text->parentNode(); 244 return parentNode->isShadowRoot() && toShadowRoot(parentNode)->host()->hasTagName(SVGNames::trefTag); 245 } 246 247 static bool isSVGText(Text* text) 248 { 249 Node* parentOrShadowHostNode = text->parentOrShadowHostNode(); 250 return parentOrShadowHostNode->isSVGElement() && !parentOrShadowHostNode->hasTagName(SVGNames::foreignObjectTag); 251 } 252 253 RenderText* Text::createTextRenderer(RenderStyle* style) 254 { 255 if (isSVGText(this) || isSVGShadowText(this)) 256 return new RenderSVGInlineText(this, dataImpl()); 257 258 if (style->hasTextCombine()) 259 return new RenderCombineText(this, dataImpl()); 260 261 return new RenderText(this, dataImpl()); 262 } 263 264 void Text::attach(const AttachContext& context) 265 { 266 NodeRenderingContext(this, context.resolvedStyle).createRendererForTextIfNeeded(); 267 CharacterData::attach(context); 268 } 269 270 bool Text::recalcTextStyle(StyleChange change) 271 { 272 if (RenderText* renderer = toRenderText(this->renderer())) { 273 if (change != NoChange || needsStyleRecalc()) 274 renderer->setStyle(document()->styleResolver()->styleForText(this)); 275 if (needsStyleRecalc()) 276 renderer->setText(dataImpl()); 277 clearNeedsStyleRecalc(); 278 } else if (needsStyleRecalc() || needsWhitespaceRenderer()) { 279 reattach(); 280 return true; 281 } 282 return false; 283 } 284 285 // If a whitespace node had no renderer and goes through a recalcStyle it may 286 // need to create one if the parent style now has white-space: pre. 287 bool Text::needsWhitespaceRenderer() 288 { 289 ASSERT(!renderer()); 290 if (RenderStyle* style = parentRenderStyle()) 291 return style->preserveNewline(); 292 return false; 293 } 294 295 void Text::updateTextRenderer(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData) 296 { 297 if (!attached()) 298 return; 299 RenderText* textRenderer = toRenderText(renderer()); 300 if (!textRenderer || !textRendererIsNeeded(NodeRenderingContext(this, textRenderer->style()))) { 301 reattach(); 302 return; 303 } 304 textRenderer->setTextWithOffset(dataImpl(), offsetOfReplacedData, lengthOfReplacedData); 305 } 306 307 bool Text::childTypeAllowed(NodeType) const 308 { 309 return false; 310 } 311 312 PassRefPtr<Text> Text::cloneWithData(const String& data) 313 { 314 return create(document(), data); 315 } 316 317 PassRefPtr<Text> Text::createWithLengthLimit(Document* document, const String& data, unsigned start, unsigned lengthLimit) 318 { 319 unsigned dataLength = data.length(); 320 321 if (!start && dataLength <= lengthLimit) 322 return create(document, data); 323 324 RefPtr<Text> result = Text::create(document, String()); 325 result->parserAppendData(data, start, lengthLimit); 326 327 return result; 328 } 329 330 #ifndef NDEBUG 331 void Text::formatForDebugger(char *buffer, unsigned length) const 332 { 333 StringBuilder result; 334 String s; 335 336 result.append(nodeName()); 337 338 s = data(); 339 if (s.length() > 0) { 340 if (result.length()) 341 result.appendLiteral("; "); 342 result.appendLiteral("value="); 343 result.append(s); 344 } 345 346 strncpy(buffer, result.toString().utf8().data(), length - 1); 347 } 348 #endif 349 350 } // namespace WebCore 351