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