1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@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 40using namespace std; 41 42namespace WebCore { 43 44Text::Text(Document* document, const String& data) 45 : CharacterData(document, data, CreateText) 46{ 47} 48 49PassRefPtr<Text> Text::create(Document* document, const String& data) 50{ 51 return adoptRef(new Text(document, data)); 52} 53 54PassRefPtr<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())->setText(dataImpl()); 81 82 return newText.release(); 83} 84 85static 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 102static 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 119String 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 resultLength += data.length(); 132 } 133 UChar* resultData; 134 String result = String::createUninitialized(resultLength, resultData); 135 UChar* p = resultData; 136 for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) { 137 if (!n->isTextNode()) 138 continue; 139 const Text* t = static_cast<const Text*>(n); 140 const String& data = t->data(); 141 unsigned dataLength = data.length(); 142 memcpy(p, data.characters(), dataLength * sizeof(UChar)); 143 p += dataLength; 144 } 145 ASSERT(p == resultData + resultLength); 146 147 return result; 148} 149 150PassRefPtr<Text> Text::replaceWholeText(const String& newText, ExceptionCode&) 151{ 152 // Remove all adjacent text nodes, and replace the contents of this one. 153 154 // Protect startText and endText against mutation event handlers removing the last ref 155 RefPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this)); 156 RefPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this)); 157 158 RefPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away 159 Node* parent = parentNode(); // Protect against mutation handlers moving this node during traversal 160 ExceptionCode ignored = 0; 161 for (RefPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) { 162 RefPtr<Node> nodeToRemove(n.release()); 163 n = nodeToRemove->nextSibling(); 164 parent->removeChild(nodeToRemove.get(), ignored); 165 } 166 167 if (this != endText) { 168 Node* onePastEndText = endText->nextSibling(); 169 for (RefPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) { 170 RefPtr<Node> nodeToRemove(n.release()); 171 n = nodeToRemove->nextSibling(); 172 parent->removeChild(nodeToRemove.get(), ignored); 173 } 174 } 175 176 if (newText.isEmpty()) { 177 if (parent && parentNode() == parent) 178 parent->removeChild(this, ignored); 179 return 0; 180 } 181 182 setData(newText, ignored); 183 return protectedThis.release(); 184} 185 186String Text::nodeName() const 187{ 188 return textAtom.string(); 189} 190 191Node::NodeType Text::nodeType() const 192{ 193 return TEXT_NODE; 194} 195 196PassRefPtr<Node> Text::cloneNode(bool /*deep*/) 197{ 198 return create(document(), data()); 199} 200 201bool Text::rendererIsNeeded(RenderStyle *style) 202{ 203 if (!CharacterData::rendererIsNeeded(style)) 204 return false; 205 206 bool onlyWS = containsOnlyWhitespace(); 207 if (!onlyWS) 208 return true; 209 210 RenderObject *par = parentNode()->renderer(); 211 212 if (par->isTable() || par->isTableRow() || par->isTableSection() || par->isTableCol() || par->isFrameSet()) 213 return false; 214 215 if (style->preserveNewline()) // pre/pre-wrap/pre-line always make renderers. 216 return true; 217 218 RenderObject *prev = previousRenderer(); 219 if (prev && prev->isBR()) // <span><br/> <br/></span> 220 return false; 221 222 if (par->isRenderInline()) { 223 // <span><div/> <div/></span> 224 if (prev && !prev->isInline()) 225 return false; 226 } else { 227 if (par->isRenderBlock() && !par->childrenInline() && (!prev || !prev->isInline())) 228 return false; 229 230 RenderObject *first = par->firstChild(); 231 while (first && first->isFloatingOrPositioned()) 232 first = first->nextSibling(); 233 RenderObject *next = nextRenderer(); 234 if (!first || next == first) 235 // Whitespace at the start of a block just goes away. Don't even 236 // make a render object for this text. 237 return false; 238 } 239 240 return true; 241} 242 243RenderObject* Text::createRenderer(RenderArena* arena, RenderStyle*) 244{ 245#if ENABLE(SVG) 246 if (parentNode()->isSVGElement() 247#if ENABLE(SVG_FOREIGN_OBJECT) 248 && !parentNode()->hasTagName(SVGNames::foreignObjectTag) 249#endif 250 ) 251 return new (arena) RenderSVGInlineText(this, dataImpl()); 252#endif 253 254 return new (arena) RenderText(this, dataImpl()); 255} 256 257void Text::attach() 258{ 259#if ENABLE(WML) 260 if (document()->isWMLDocument() && !containsOnlyWhitespace()) { 261 String text = data(); 262 ASSERT(!text.isEmpty()); 263 264 text = substituteVariableReferences(text, document()); 265 266 ExceptionCode code = 0; 267 setData(text, code); 268 ASSERT(!code); 269 } 270#endif 271 272 createRendererIfNeeded(); 273 CharacterData::attach(); 274} 275 276void Text::recalcStyle(StyleChange change) 277{ 278 if (change != NoChange && parentNode()) { 279 if (renderer()) 280 renderer()->setStyle(parentNode()->renderer()->style()); 281 } 282 if (needsStyleRecalc()) { 283 if (renderer()) { 284 if (renderer()->isText()) 285 toRenderText(renderer())->setText(dataImpl()); 286 } else { 287 if (attached()) 288 detach(); 289 attach(); 290 } 291 } 292 setNeedsStyleRecalc(NoStyleChange); 293} 294 295bool Text::childTypeAllowed(NodeType) 296{ 297 return false; 298} 299 300PassRefPtr<Text> Text::virtualCreate(const String& data) 301{ 302 return create(document(), data); 303} 304 305PassRefPtr<Text> Text::createWithLengthLimit(Document* document, const String& data, unsigned& charsLeft, unsigned maxChars) 306{ 307 unsigned dataLength = data.length(); 308 309 if (charsLeft == dataLength && charsLeft <= maxChars) { 310 charsLeft = 0; 311 return create(document, data); 312 } 313 314 unsigned start = dataLength - charsLeft; 315 unsigned end = start + min(charsLeft, maxChars); 316 317 // Check we are not on an unbreakable boundary. 318 // Some text break iterator implementations work best if the passed buffer is as small as possible, 319 // see <https://bugs.webkit.org/show_bug.cgi?id=29092>. 320 // We need at least two characters look-ahead to account for UTF-16 surrogates. 321 if (end < dataLength) { 322 TextBreakIterator* it = characterBreakIterator(data.characters() + start, (end + 2 > dataLength) ? dataLength - start : end - start + 2); 323 if (!isTextBreak(it, end - start)) 324 end = textBreakPreceding(it, end - start) + start; 325 } 326 327 // If we have maxChars of unbreakable characters the above could lead to 328 // an infinite loop. 329 // FIXME: It would be better to just have the old value of end before calling 330 // textBreakPreceding rather than this, because this exceeds the length limit. 331 if (end <= start) 332 end = dataLength; 333 334 charsLeft = dataLength - end; 335 return create(document, data.substring(start, end - start)); 336} 337 338#ifndef NDEBUG 339void Text::formatForDebugger(char *buffer, unsigned length) const 340{ 341 String result; 342 String s; 343 344 s = nodeName(); 345 if (s.length() > 0) { 346 result += s; 347 } 348 349 s = data(); 350 if (s.length() > 0) { 351 if (result.length() > 0) 352 result += "; "; 353 result += "value="; 354 result += s; 355 } 356 357 strncpy(buffer, result.utf8().data(), length - 1); 358} 359#endif 360 361} // namespace WebCore 362