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 "Text.h" 24 25 #include "CString.h" 26 #include "ExceptionCode.h" 27 #include "RenderText.h" 28 #include "TextBreakIterator.h" 29 30 #if ENABLE(SVG) 31 #include "RenderSVGInlineText.h" 32 #include "SVGNames.h" 33 #endif 34 35 #if ENABLE(WML) 36 #include "WMLDocument.h" 37 #include "WMLVariables.h" 38 #endif 39 40 using namespace std; 41 42 namespace WebCore { 43 44 Text::Text(Document* document, const String& data) 45 : CharacterData(document, data, CreateText) 46 { 47 } 48 49 PassRefPtr<Text> Text::create(Document* document, const String& data) 50 { 51 return adoptRef(new Text(document, data)); 52 } 53 54 PassRefPtr<Text> Text::splitText(unsigned offset, ExceptionCode& ec) 55 { 56 ec = 0; 57 58 // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than 59 // the number of 16-bit units in data. 60 if (offset > length()) { 61 ec = INDEX_SIZE_ERR; 62 return 0; 63 } 64 65 RefPtr<StringImpl> oldStr = dataImpl(); 66 RefPtr<Text> newText = virtualCreate(oldStr->substring(offset)); 67 setDataImpl(oldStr->substring(0, offset)); 68 69 dispatchModifiedEvent(oldStr.get()); 70 71 if (parentNode()) 72 parentNode()->insertBefore(newText.get(), nextSibling(), ec); 73 if (ec) 74 return 0; 75 76 if (parentNode()) 77 document()->textNodeSplit(this); 78 79 if (renderer()) 80 toRenderText(renderer())->setTextWithOffset(dataImpl(), 0, oldStr->length()); 81 82 return newText.release(); 83 } 84 85 static const Text* earliestLogicallyAdjacentTextNode(const Text* t) 86 { 87 const Node* n = t; 88 while ((n = n->previousSibling())) { 89 Node::NodeType type = n->nodeType(); 90 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 91 t = static_cast<const Text*>(n); 92 continue; 93 } 94 95 // We would need to visit EntityReference child text nodes if they existed 96 ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes()); 97 break; 98 } 99 return t; 100 } 101 102 static const Text* latestLogicallyAdjacentTextNode(const Text* t) 103 { 104 const Node* n = t; 105 while ((n = n->nextSibling())) { 106 Node::NodeType type = n->nodeType(); 107 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 108 t = static_cast<const Text*>(n); 109 continue; 110 } 111 112 // We would need to visit EntityReference child text nodes if they existed 113 ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes()); 114 break; 115 } 116 return t; 117 } 118 119 String Text::wholeText() const 120 { 121 const Text* startText = earliestLogicallyAdjacentTextNode(this); 122 const Text* endText = latestLogicallyAdjacentTextNode(this); 123 124 Node* onePastEndText = endText->nextSibling(); 125 unsigned resultLength = 0; 126 for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) { 127 if (!n->isTextNode()) 128 continue; 129 const Text* t = static_cast<const Text*>(n); 130 const String& data = t->data(); 131 if (std::numeric_limits<unsigned>::max() - data.length() < resultLength) 132 CRASH(); 133 resultLength += data.length(); 134 } 135 UChar* resultData; 136 String result = String::createUninitialized(resultLength, resultData); 137 UChar* p = resultData; 138 for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) { 139 if (!n->isTextNode()) 140 continue; 141 const Text* t = static_cast<const Text*>(n); 142 const String& data = t->data(); 143 unsigned dataLength = data.length(); 144 memcpy(p, data.characters(), dataLength * sizeof(UChar)); 145 p += dataLength; 146 } 147 ASSERT(p == resultData + resultLength); 148 149 return result; 150 } 151 152 PassRefPtr<Text> Text::replaceWholeText(const String& newText, ExceptionCode&) 153 { 154 // Remove all adjacent text nodes, and replace the contents of this one. 155 156 // Protect startText and endText against mutation event handlers removing the last ref 157 RefPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this)); 158 RefPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this)); 159 160 RefPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away 161 Node* parent = parentNode(); // Protect against mutation handlers moving this node during traversal 162 ExceptionCode ignored = 0; 163 for (RefPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) { 164 RefPtr<Node> nodeToRemove(n.release()); 165 n = nodeToRemove->nextSibling(); 166 parent->removeChild(nodeToRemove.get(), ignored); 167 } 168 169 if (this != endText) { 170 Node* onePastEndText = endText->nextSibling(); 171 for (RefPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) { 172 RefPtr<Node> nodeToRemove(n.release()); 173 n = nodeToRemove->nextSibling(); 174 parent->removeChild(nodeToRemove.get(), ignored); 175 } 176 } 177 178 if (newText.isEmpty()) { 179 if (parent && parentNode() == parent) 180 parent->removeChild(this, ignored); 181 return 0; 182 } 183 184 setData(newText, ignored); 185 return protectedThis.release(); 186 } 187 188 String Text::nodeName() const 189 { 190 return textAtom.string(); 191 } 192 193 Node::NodeType Text::nodeType() const 194 { 195 return TEXT_NODE; 196 } 197 198 PassRefPtr<Node> Text::cloneNode(bool /*deep*/) 199 { 200 return create(document(), data()); 201 } 202 203 bool Text::rendererIsNeeded(RenderStyle *style) 204 { 205 if (!CharacterData::rendererIsNeeded(style)) 206 return false; 207 208 bool onlyWS = containsOnlyWhitespace(); 209 if (!onlyWS) 210 return true; 211 212 RenderObject *par = parentNode()->renderer(); 213 214 if (par->isTable() || par->isTableRow() || par->isTableSection() || par->isTableCol() || par->isFrameSet()) 215 return false; 216 217 if (style->preserveNewline()) // pre/pre-wrap/pre-line always make renderers. 218 return true; 219 220 RenderObject *prev = previousRenderer(); 221 if (prev && prev->isBR()) // <span><br/> <br/></span> 222 return false; 223 224 if (par->isRenderInline()) { 225 // <span><div/> <div/></span> 226 if (prev && !prev->isInline()) 227 return false; 228 } else { 229 if (par->isRenderBlock() && !par->childrenInline() && (!prev || !prev->isInline())) 230 return false; 231 232 RenderObject *first = par->firstChild(); 233 while (first && first->isFloatingOrPositioned()) 234 first = first->nextSibling(); 235 RenderObject *next = nextRenderer(); 236 if (!first || next == first) 237 // Whitespace at the start of a block just goes away. Don't even 238 // make a render object for this text. 239 return false; 240 } 241 242 return true; 243 } 244 245 RenderObject* Text::createRenderer(RenderArena* arena, RenderStyle*) 246 { 247 #if ENABLE(SVG) 248 if (parentNode()->isSVGElement() 249 #if ENABLE(SVG_FOREIGN_OBJECT) 250 && !parentNode()->hasTagName(SVGNames::foreignObjectTag) 251 #endif 252 ) 253 return new (arena) RenderSVGInlineText(this, dataImpl()); 254 #endif 255 256 return new (arena) RenderText(this, dataImpl()); 257 } 258 259 void Text::attach() 260 { 261 #if ENABLE(WML) 262 if (document()->isWMLDocument() && !containsOnlyWhitespace()) { 263 String text = data(); 264 ASSERT(!text.isEmpty()); 265 266 text = substituteVariableReferences(text, document()); 267 268 ExceptionCode code = 0; 269 setData(text, code); 270 ASSERT(!code); 271 } 272 #endif 273 274 createRendererIfNeeded(); 275 CharacterData::attach(); 276 } 277 278 void Text::recalcStyle(StyleChange change) 279 { 280 if (change != NoChange && parentNode()) { 281 if (renderer()) 282 renderer()->setStyle(parentNode()->renderer()->style()); 283 } 284 if (needsStyleRecalc()) { 285 if (renderer()) { 286 if (renderer()->isText()) 287 toRenderText(renderer())->setText(dataImpl()); 288 } else { 289 if (attached()) 290 detach(); 291 attach(); 292 } 293 } 294 setNeedsStyleRecalc(NoStyleChange); 295 } 296 297 bool Text::childTypeAllowed(NodeType) 298 { 299 return false; 300 } 301 302 PassRefPtr<Text> Text::virtualCreate(const String& data) 303 { 304 return create(document(), data); 305 } 306 307 PassRefPtr<Text> Text::createWithLengthLimit(Document* document, const String& data, unsigned& charsLeft, unsigned maxChars) 308 { 309 unsigned dataLength = data.length(); 310 311 if (charsLeft == dataLength && charsLeft <= maxChars) { 312 charsLeft = 0; 313 return create(document, data); 314 } 315 316 unsigned start = dataLength - charsLeft; 317 unsigned end = start + min(charsLeft, maxChars); 318 319 // Check we are not on an unbreakable boundary. 320 // Some text break iterator implementations work best if the passed buffer is as small as possible, 321 // see <https://bugs.webkit.org/show_bug.cgi?id=29092>. 322 // We need at least two characters look-ahead to account for UTF-16 surrogates. 323 if (end < dataLength) { 324 TextBreakIterator* it = characterBreakIterator(data.characters() + start, (end + 2 > dataLength) ? dataLength - start : end - start + 2); 325 if (!isTextBreak(it, end - start)) 326 end = textBreakPreceding(it, end - start) + start; 327 } 328 329 // If we have maxChars of unbreakable characters the above could lead to 330 // an infinite loop. 331 // FIXME: It would be better to just have the old value of end before calling 332 // textBreakPreceding rather than this, because this exceeds the length limit. 333 if (end <= start) 334 end = dataLength; 335 336 charsLeft = dataLength - end; 337 return create(document, data.substring(start, end - start)); 338 } 339 340 #ifndef NDEBUG 341 void Text::formatForDebugger(char *buffer, unsigned length) const 342 { 343 String result; 344 String s; 345 346 s = nodeName(); 347 if (s.length() > 0) { 348 result += s; 349 } 350 351 s = data(); 352 if (s.length() > 0) { 353 if (result.length() > 0) 354 result += "; "; 355 result += "value="; 356 result += s; 357 } 358 359 strncpy(buffer, result.utf8().data(), length - 1); 360 } 361 #endif 362 363 } // namespace WebCore 364