Text.cpp revision 2daae5fd11344eaa88a0d92b0f6d65f8d2255c00
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 "ExceptionCode.h" 26#include "RenderCombineText.h" 27#include "RenderText.h" 28 29#if ENABLE(SVG) 30#include "RenderSVGInlineText.h" 31#include "SVGNames.h" 32#endif 33 34#if ENABLE(WML) 35#include "WMLDocument.h" 36#include "WMLVariables.h" 37#endif 38 39using namespace std; 40 41namespace WebCore { 42 43PassRefPtr<Text> Text::create(Document* document, const String& data) 44{ 45 return adoptRef(new Text(document, data)); 46} 47 48PassRefPtr<Text> Text::splitText(unsigned offset, ExceptionCode& ec) 49{ 50 ec = 0; 51 52 // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than 53 // the number of 16-bit units in data. 54 if (offset > length()) { 55 ec = INDEX_SIZE_ERR; 56 return 0; 57 } 58 59 RefPtr<StringImpl> oldStr = dataImpl(); 60 RefPtr<Text> newText = virtualCreate(oldStr->substring(offset)); 61 setDataImpl(oldStr->substring(0, offset)); 62 63 dispatchModifiedEvent(oldStr.get()); 64 65 if (parentNode()) 66 parentNode()->insertBefore(newText.get(), nextSibling(), ec); 67 if (ec) 68 return 0; 69 70 if (parentNode()) 71 document()->textNodeSplit(this); 72 73 if (renderer()) 74 toRenderText(renderer())->setTextWithOffset(dataImpl(), 0, oldStr->length()); 75 76 return newText.release(); 77} 78 79static const Text* earliestLogicallyAdjacentTextNode(const Text* t) 80{ 81 const Node* n = t; 82 while ((n = n->previousSibling())) { 83 Node::NodeType type = n->nodeType(); 84 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 85 t = static_cast<const Text*>(n); 86 continue; 87 } 88 89 // We would need to visit EntityReference child text nodes if they existed 90 ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes()); 91 break; 92 } 93 return t; 94} 95 96static const Text* latestLogicallyAdjacentTextNode(const Text* t) 97{ 98 const Node* n = t; 99 while ((n = n->nextSibling())) { 100 Node::NodeType type = n->nodeType(); 101 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 102 t = static_cast<const Text*>(n); 103 continue; 104 } 105 106 // We would need to visit EntityReference child text nodes if they existed 107 ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes()); 108 break; 109 } 110 return t; 111} 112 113String Text::wholeText() const 114{ 115 const Text* startText = earliestLogicallyAdjacentTextNode(this); 116 const Text* endText = latestLogicallyAdjacentTextNode(this); 117 118 Node* onePastEndText = endText->nextSibling(); 119 unsigned resultLength = 0; 120 for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) { 121 if (!n->isTextNode()) 122 continue; 123 const Text* t = static_cast<const Text*>(n); 124 const String& data = t->data(); 125 if (std::numeric_limits<unsigned>::max() - data.length() < resultLength) 126 CRASH(); 127 resultLength += data.length(); 128 } 129 UChar* resultData; 130 String result = String::createUninitialized(resultLength, resultData); 131 UChar* p = resultData; 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 const String& data = t->data(); 137 unsigned dataLength = data.length(); 138 memcpy(p, data.characters(), dataLength * sizeof(UChar)); 139 p += dataLength; 140 } 141 ASSERT(p == resultData + resultLength); 142 143 return result; 144} 145 146PassRefPtr<Text> Text::replaceWholeText(const String& newText, ExceptionCode&) 147{ 148 // Remove all adjacent text nodes, and replace the contents of this one. 149 150 // Protect startText and endText against mutation event handlers removing the last ref 151 RefPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this)); 152 RefPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this)); 153 154 RefPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away 155 ContainerNode* parent = parentNode(); // Protect against mutation handlers moving this node during traversal 156 ExceptionCode ignored = 0; 157 for (RefPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) { 158 RefPtr<Node> nodeToRemove(n.release()); 159 n = nodeToRemove->nextSibling(); 160 parent->removeChild(nodeToRemove.get(), ignored); 161 } 162 163 if (this != endText) { 164 Node* onePastEndText = endText->nextSibling(); 165 for (RefPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) { 166 RefPtr<Node> nodeToRemove(n.release()); 167 n = nodeToRemove->nextSibling(); 168 parent->removeChild(nodeToRemove.get(), ignored); 169 } 170 } 171 172 if (newText.isEmpty()) { 173 if (parent && parentNode() == parent) 174 parent->removeChild(this, ignored); 175 return 0; 176 } 177 178 setData(newText, ignored); 179 return protectedThis.release(); 180} 181 182String Text::nodeName() const 183{ 184 return textAtom.string(); 185} 186 187Node::NodeType Text::nodeType() const 188{ 189 return TEXT_NODE; 190} 191 192PassRefPtr<Node> Text::cloneNode(bool /*deep*/) 193{ 194 return create(document(), data()); 195} 196 197bool Text::rendererIsNeeded(RenderStyle *style) 198{ 199 if (!CharacterData::rendererIsNeeded(style)) 200 return false; 201 202 bool onlyWS = containsOnlyWhitespace(); 203 if (!onlyWS) 204 return true; 205 206 RenderObject *par = parentNode()->renderer(); 207 208 if (par->isTable() || par->isTableRow() || par->isTableSection() || par->isTableCol() || par->isFrameSet()) 209 return false; 210 211 if (style->preserveNewline()) // pre/pre-wrap/pre-line always make renderers. 212 return true; 213 214 RenderObject *prev = previousRenderer(); 215 if (prev && prev->isBR()) // <span><br/> <br/></span> 216 return false; 217 218 if (par->isRenderInline()) { 219 // <span><div/> <div/></span> 220 if (prev && !prev->isInline()) 221 return false; 222 } else { 223 if (par->isRenderBlock() && !par->childrenInline() && (!prev || !prev->isInline())) 224 return false; 225 226 RenderObject *first = par->firstChild(); 227 while (first && first->isFloatingOrPositioned()) 228 first = first->nextSibling(); 229 RenderObject *next = nextRenderer(); 230 if (!first || next == first) 231 // Whitespace at the start of a block just goes away. Don't even 232 // make a render object for this text. 233 return false; 234 } 235 236 return true; 237} 238 239RenderObject* Text::createRenderer(RenderArena* arena, RenderStyle* style) 240{ 241#if ENABLE(SVG) 242 Node* parentOrHost = parentOrHostNode(); 243 if (parentOrHost->isSVGElement() 244#if ENABLE(SVG_FOREIGN_OBJECT) 245 && !parentOrHost->hasTagName(SVGNames::foreignObjectTag) 246#endif 247 ) 248 return new (arena) RenderSVGInlineText(this, dataImpl()); 249#endif 250 251 if (style->hasTextCombine()) 252 return new (arena) RenderCombineText(this, dataImpl()); 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 clearNeedsStyleRecalc(); 293} 294 295bool Text::childTypeAllowed(NodeType) const 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 start, unsigned maxChars) 306{ 307 unsigned dataLength = data.length(); 308 309 if (!start && dataLength <= maxChars) 310 return create(document, data); 311 312 RefPtr<Text> result = Text::create(document, String()); 313 result->parserAppendData(data.characters() + start, dataLength - start, maxChars); 314 315 return result; 316} 317 318#ifndef NDEBUG 319void Text::formatForDebugger(char *buffer, unsigned length) const 320{ 321 String result; 322 String s; 323 324 s = nodeName(); 325 if (s.length() > 0) { 326 result += s; 327 } 328 329 s = data(); 330 if (s.length() > 0) { 331 if (result.length() > 0) 332 result += "; "; 333 result += "value="; 334 result += s; 335 } 336 337 strncpy(buffer, result.utf8().data(), length - 1); 338} 339#endif 340 341} // namespace WebCore 342