File indexing completed on 2024-05-05 12:16:46
0001 /** 0002 * This file is part of the DOM implementation for KDE. 0003 * 0004 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) 0005 * (C) 2001-2003 Dirk Mueller (mueller@kde.org) 0006 * (C) 1999 Antti Koivisto (koivisto@kde.org) 0007 * (C) 2002-2003 Apple Computer, Inc. 0008 * 0009 * This library is free software; you can redistribute it and/or 0010 * modify it under the terms of the GNU Library General Public 0011 * License as published by the Free Software Foundation; either 0012 * version 2 of the License, or (at your option) any later version. 0013 * 0014 * This library is distributed in the hope that it will be useful, 0015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0017 * Library General Public License for more details. 0018 * 0019 * You should have received a copy of the GNU Library General Public License 0020 * along with this library; see the file COPYING.LIB. If not, write to 0021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0022 * Boston, MA 02110-1301, USA. 0023 */ 0024 0025 #include "dom_textimpl.h" 0026 #include "dom2_eventsimpl.h" 0027 #include "dom_docimpl.h" 0028 #include <dom/dom_exception.h> 0029 #include <css/cssstyleselector.h> 0030 0031 #include <rendering/render_text.h> 0032 #include <rendering/render_flow.h> 0033 #include <wtf/PassRefPtr.h> 0034 #include <wtf/RefPtr.h> 0035 0036 #include "khtml_debug.h" 0037 0038 // for SVG 0039 #include <rendering/RenderSVGInlineText.h> 0040 0041 using namespace DOM; 0042 using namespace khtml; 0043 0044 static DOMString escapeHTML(const DOMString &in) 0045 { 0046 return in.implementation()->escapeHTML(); 0047 } 0048 0049 CharacterDataImpl::CharacterDataImpl(DocumentImpl *doc, DOMStringImpl *_text) 0050 : NodeImpl(doc) 0051 { 0052 str = _text ? _text : new DOMStringImpl((QChar *)nullptr, 0); 0053 str->ref(); 0054 } 0055 0056 CharacterDataImpl::~CharacterDataImpl() 0057 { 0058 if (str) { 0059 str->deref(); 0060 } 0061 } 0062 0063 void CharacterDataImpl::setData(const DOMString &_data, int &exceptioncode) 0064 { 0065 // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly 0066 if (isReadOnly()) { 0067 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; 0068 return; 0069 } 0070 0071 if (str == _data.impl) { 0072 return; // ### fire DOMCharacterDataModified if modified? 0073 } 0074 DOMStringImpl *oldStr = str; 0075 str = _data.impl; 0076 if (!str) { 0077 str = new DOMStringImpl((QChar *)nullptr, 0); 0078 } 0079 str->ref(); 0080 if (m_render) { 0081 (static_cast<RenderText *>(m_render))->setText(str); 0082 } 0083 setChanged(true); 0084 0085 dispatchModifiedEvent(oldStr); 0086 if (oldStr) { 0087 oldStr->deref(); 0088 } 0089 } 0090 0091 unsigned long CharacterDataImpl::length() const 0092 { 0093 return str->l; 0094 } 0095 0096 DOMString CharacterDataImpl::substringData(const unsigned long offset, const unsigned long count, int &exceptioncode) 0097 { 0098 exceptioncode = 0; 0099 if ((long)count < 0) { 0100 exceptioncode = DOMException::INDEX_SIZE_ERR; 0101 } else { 0102 checkCharDataOperation(offset, exceptioncode); 0103 } 0104 if (exceptioncode) { 0105 return DOMString(); 0106 } 0107 0108 return str->substring(offset, count); 0109 } 0110 0111 void CharacterDataImpl::appendData(const DOMString &arg, int &exceptioncode) 0112 { 0113 exceptioncode = 0; 0114 0115 // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly 0116 if (isReadOnly()) { 0117 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; 0118 return; 0119 } 0120 0121 DOMStringImpl *oldStr = str; 0122 str = str->copy(); 0123 str->ref(); 0124 str->append(arg.impl); 0125 if (m_render) { 0126 (static_cast<RenderText *>(m_render))->setText(str); 0127 } 0128 setChanged(true); 0129 0130 dispatchModifiedEvent(oldStr); 0131 oldStr->deref(); 0132 } 0133 0134 void CharacterDataImpl::insertData(const unsigned long offset, const DOMString &arg, int &exceptioncode) 0135 { 0136 exceptioncode = 0; 0137 checkCharDataOperation(offset, exceptioncode); 0138 if (exceptioncode) { 0139 return; 0140 } 0141 0142 DOMStringImpl *oldStr = str; 0143 str = str->copy(); 0144 str->ref(); 0145 str->insert(arg.impl, offset); 0146 if (m_render) { 0147 (static_cast<RenderText *>(m_render))->setText(str); 0148 } 0149 setChanged(true); 0150 0151 dispatchModifiedEvent(oldStr); 0152 oldStr->deref(); 0153 } 0154 0155 void CharacterDataImpl::deleteData(const unsigned long offset, const unsigned long count, int &exceptioncode) 0156 { 0157 exceptioncode = 0; 0158 if ((long)count < 0) { 0159 exceptioncode = DOMException::INDEX_SIZE_ERR; 0160 } else { 0161 checkCharDataOperation(offset, exceptioncode); 0162 } 0163 if (exceptioncode) { 0164 return; 0165 } 0166 0167 DOMStringImpl *oldStr = str; 0168 str = str->copy(); 0169 str->ref(); 0170 str->remove(offset, count); 0171 if (m_render) { 0172 (static_cast<RenderText *>(m_render))->setText(str); 0173 } 0174 setChanged(true); 0175 0176 dispatchModifiedEvent(oldStr); 0177 oldStr->deref(); 0178 } 0179 0180 void CharacterDataImpl::replaceData(const unsigned long offset, const unsigned long count, const DOMString &arg, int &exceptioncode) 0181 { 0182 exceptioncode = 0; 0183 if ((long)count < 0) { 0184 exceptioncode = DOMException::INDEX_SIZE_ERR; 0185 } else { 0186 checkCharDataOperation(offset, exceptioncode); 0187 } 0188 if (exceptioncode) { 0189 return; 0190 } 0191 0192 unsigned long realCount; 0193 if (offset + count > str->l) { 0194 realCount = str->l - offset; 0195 } else { 0196 realCount = count; 0197 } 0198 0199 DOMStringImpl *oldStr = str; 0200 str = str->copy(); 0201 str->ref(); 0202 str->remove(offset, realCount); 0203 str->insert(arg.impl, offset); 0204 if (m_render) { 0205 (static_cast<RenderText *>(m_render))->setText(str); 0206 } 0207 setChanged(true); 0208 0209 dispatchModifiedEvent(oldStr); 0210 oldStr->deref(); 0211 } 0212 0213 DOMString CharacterDataImpl::nodeValue() const 0214 { 0215 return str; 0216 } 0217 0218 bool CharacterDataImpl::containsOnlyWhitespace() const 0219 { 0220 return str->containsOnlyWhitespace(); 0221 } 0222 0223 void CharacterDataImpl::setNodeValue(const DOMString &_nodeValue, int &exceptioncode) 0224 { 0225 // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData() 0226 setData(_nodeValue, exceptioncode); 0227 } 0228 0229 void CharacterDataImpl::dispatchModifiedEvent(DOMStringImpl *prevValue) 0230 { 0231 if (parentNode()) { 0232 parentNode()->childrenChanged(); 0233 } 0234 if ((str->length() == 0) != (prevValue->length() == 0)) { 0235 // changes in emptiness triggers changes in :empty selector 0236 if (parentNode() && parentNode()->isElementNode()) { 0237 parentNode()->backwardsStructureChanged(); 0238 } 0239 // ### to fully support dynamic changes to :contains selector 0240 // backwardsStructureChanged should be called for all changes 0241 } 0242 if (!document()->hasListenerType(DocumentImpl::DOMCHARACTERDATAMODIFIED_LISTENER)) { 0243 return; 0244 } 0245 0246 DOMStringImpl *newValue = str->copy(); 0247 newValue->ref(); 0248 int exceptioncode = 0; 0249 MutationEventImpl *const evt = new MutationEventImpl(EventImpl::DOMCHARACTERDATAMODIFIED_EVENT, true, false, nullptr, prevValue, newValue, DOMString(), 0); 0250 evt->ref(); 0251 dispatchEvent(evt, exceptioncode); 0252 evt->deref(); 0253 newValue->deref(); 0254 dispatchSubtreeModifiedEvent(); 0255 } 0256 0257 void CharacterDataImpl::checkCharDataOperation(const unsigned long offset, int &exceptioncode) 0258 { 0259 exceptioncode = 0; 0260 0261 // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than the number of 16-bit 0262 // units in data. 0263 if (offset > str->l) { 0264 exceptioncode = DOMException::INDEX_SIZE_ERR; 0265 return; 0266 } 0267 0268 // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly 0269 if (isReadOnly()) { 0270 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; 0271 return; 0272 } 0273 } 0274 0275 long CharacterDataImpl::maxOffset() const 0276 { 0277 RenderText *r = static_cast<RenderText *>(renderer()); 0278 if (!r || !r->isText()) { 0279 return 0; 0280 } 0281 return (long)length(); 0282 } 0283 0284 long CharacterDataImpl::caretMinOffset() const 0285 { 0286 RenderText *r = static_cast<RenderText *>(renderer()); 0287 return r && r->isText() ? r->convertToDOMPosition(r->caretMinOffset()) : 0; 0288 } 0289 0290 long CharacterDataImpl::caretMaxOffset() const 0291 { 0292 RenderText *r = static_cast<RenderText *>(renderer()); 0293 return r && r->isText() ? r->convertToDOMPosition(r->caretMaxOffset()) : (long)length(); 0294 } 0295 0296 unsigned long CharacterDataImpl::caretMaxRenderedOffset() const 0297 { 0298 RenderText *r = static_cast<RenderText *>(renderer()); 0299 return r ? r->caretMaxRenderedOffset() : length(); 0300 } 0301 0302 bool CharacterDataImpl::rendererIsNeeded(khtml::RenderStyle *style) 0303 { 0304 if (!str || !str->l) { 0305 return false; 0306 } 0307 return NodeImpl::rendererIsNeeded(style); 0308 } 0309 0310 // --------------------------------------------------------------------------- 0311 0312 DOMString CommentImpl::nodeName() const 0313 { 0314 return "#comment"; 0315 } 0316 0317 unsigned short CommentImpl::nodeType() const 0318 { 0319 return Node::COMMENT_NODE; 0320 } 0321 0322 WTF::PassRefPtr<NodeImpl> CommentImpl::cloneNode(bool /*deep*/) 0323 { 0324 return document()->createComment(str); 0325 } 0326 0327 NodeImpl::Id CommentImpl::id() const 0328 { 0329 return ID_COMMENT; 0330 } 0331 0332 // DOM Section 1.1.1 0333 bool CommentImpl::childTypeAllowed(unsigned short /*type*/) 0334 { 0335 return false; 0336 } 0337 0338 DOMString CommentImpl::toString() const 0339 { 0340 // FIXME: substitute entity references as needed! 0341 return DOMString("<!--") + nodeValue() + DOMString("-->"); 0342 } 0343 0344 // --------------------------------------------------------------------------- 0345 0346 TextImpl *TextImpl::splitText(const unsigned long offset, int &exceptioncode) 0347 { 0348 exceptioncode = 0; 0349 0350 // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than 0351 // the number of 16-bit units in data. 0352 0353 // ### we explicitly check for a negative long that has been cast to an unsigned long 0354 // ... this can happen if JS code passes in -1 - we need to catch this earlier! (in the 0355 // kjs bindings) 0356 if (offset > str->l || (long)offset < 0) { 0357 exceptioncode = DOMException::INDEX_SIZE_ERR; 0358 return nullptr; 0359 } 0360 0361 // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. 0362 if (isReadOnly()) { 0363 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; 0364 return nullptr; 0365 } 0366 0367 DOMStringImpl *oldStr = str; 0368 TextImpl *newText = createNew(str->substring(offset, str->l - offset)); 0369 str = str->copy(); 0370 str->ref(); 0371 str->remove(offset, str->l - offset); 0372 0373 dispatchModifiedEvent(oldStr); 0374 oldStr->deref(); 0375 0376 if (parentNode()) { 0377 parentNode()->insertBefore(newText, nextSibling(), exceptioncode); 0378 } 0379 if (exceptioncode) { 0380 return nullptr; 0381 } 0382 0383 if (m_render) { 0384 (static_cast<RenderText *>(m_render))->setText(str); 0385 } 0386 setChanged(true); 0387 return newText; 0388 } 0389 0390 static const TextImpl *earliestLogicallyAdjacentTextNode(const TextImpl *t) 0391 { 0392 const NodeImpl *n = t; 0393 while ((n = n->previousSibling())) { 0394 unsigned short type = n->nodeType(); 0395 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 0396 t = static_cast<const TextImpl *>(n); 0397 continue; 0398 } 0399 0400 // We would need to visit EntityReference child text nodes if they existed 0401 assert(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes()); 0402 break; 0403 } 0404 return t; 0405 } 0406 0407 static const TextImpl *latestLogicallyAdjacentTextNode(const TextImpl *t) 0408 { 0409 const NodeImpl *n = t; 0410 while ((n = n->nextSibling())) { 0411 unsigned short type = n->nodeType(); 0412 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { 0413 t = static_cast<const TextImpl *>(n); 0414 continue; 0415 } 0416 0417 // We would need to visit EntityReference child text nodes if they existed 0418 assert(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes()); 0419 break; 0420 } 0421 return t; 0422 } 0423 0424 DOMString TextImpl::wholeText() const 0425 { 0426 const TextImpl *startText = earliestLogicallyAdjacentTextNode(this); 0427 const TextImpl *endText = latestLogicallyAdjacentTextNode(this); 0428 0429 DOMString result; 0430 NodeImpl *onePastEndText = endText->nextSibling(); 0431 for (const NodeImpl *n = startText; n != onePastEndText; n = n->nextSibling()) { 0432 if (!n->isTextNode()) { 0433 continue; 0434 } 0435 const TextImpl *t = static_cast<const TextImpl *>(n); 0436 const DOMString &data = t->data(); 0437 result += data; 0438 } 0439 0440 return result; 0441 } 0442 0443 TextImpl *TextImpl::replaceWholeText(const DOMString &newText, int &ec) 0444 { 0445 Q_UNUSED(ec); 0446 // We don't support "read-only" text nodes (no Entity node support) 0447 // Thus, we remove all adjacent text nodes, and replace the contents of this one. 0448 assert(!isReadOnly()); 0449 // This method only raises exceptions when dealing with Entity nodes (which we don't support) 0450 0451 // Protect startText and endText against mutation event handlers removing the last ref 0452 RefPtr<TextImpl> startText = const_cast<TextImpl *>(earliestLogicallyAdjacentTextNode(this)); 0453 RefPtr<TextImpl> endText = const_cast<TextImpl *>(latestLogicallyAdjacentTextNode(this)); 0454 0455 RefPtr<TextImpl> protectedThis(this); // Mutation event handlers could cause our last ref to go away 0456 NodeImpl *parent = parentNode(); // Protect against mutation handlers moving this node during traversal 0457 int ignored = 0; 0458 for (RefPtr<NodeImpl> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) { 0459 RefPtr<NodeImpl> nodeToRemove(n.release()); 0460 n = nodeToRemove->nextSibling(); 0461 parent->removeChild(nodeToRemove.get(), ignored); 0462 } 0463 0464 if (this != endText) { 0465 NodeImpl *onePastEndText = endText->nextSibling(); 0466 for (RefPtr<NodeImpl> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) { 0467 RefPtr<NodeImpl> nodeToRemove(n.release()); 0468 n = nodeToRemove->nextSibling(); 0469 parent->removeChild(nodeToRemove.get(), ignored); 0470 } 0471 } 0472 0473 if (newText.isEmpty()) { 0474 if (parent && parentNode() == parent) { 0475 parent->removeChild(this, ignored); 0476 } 0477 return nullptr; 0478 } 0479 0480 setData(newText, ignored); 0481 return protectedThis.release().get(); 0482 } 0483 0484 DOMString TextImpl::nodeName() const 0485 { 0486 return "#text"; 0487 } 0488 0489 unsigned short TextImpl::nodeType() const 0490 { 0491 return Node::TEXT_NODE; 0492 } 0493 0494 WTF::PassRefPtr<NodeImpl> TextImpl::cloneNode(bool /*deep*/) 0495 { 0496 return document()->createTextNode(str); 0497 } 0498 0499 bool TextImpl::rendererIsNeeded(RenderStyle *style) 0500 { 0501 if (!CharacterDataImpl::rendererIsNeeded(style)) { 0502 return false; 0503 } 0504 bool onlyWS = containsOnlyWhitespace(); 0505 if (!onlyWS) { 0506 return true; 0507 } 0508 0509 RenderObject *par = parentNode()->renderer(); 0510 0511 if (par->isTable() || par->isTableRow() || par->isTableSection()) { 0512 return false; 0513 } 0514 0515 if (style->preserveWS() || style->preserveLF()) { 0516 return true; 0517 } 0518 0519 RenderObject *prev = previousRenderer(); 0520 if (par->isInlineFlow()) { 0521 // <span><div/> <div/></span> 0522 if (prev && !prev->isInline()) { 0523 return false; 0524 } 0525 } else { 0526 if (par->isRenderBlock() && !par->childrenInline() && (!prev || !prev->isInline())) { 0527 return false; 0528 } 0529 0530 RenderObject *first = par->firstChild(); 0531 while (first && first->isFloatingOrPositioned()) { 0532 first = first->nextSibling(); 0533 } 0534 RenderObject *next = nextRenderer(); 0535 if (!first || next == first) { 0536 // Whitespace at the start of a block just goes away. Don't even 0537 // make a render object for this text. 0538 return false; 0539 } 0540 } 0541 0542 return true; 0543 } 0544 0545 RenderObject *TextImpl::createRenderer(RenderArena *arena, RenderStyle * /*style*/) 0546 { 0547 // for SVG 0548 if (parentNode()->isSVGElement()) { 0549 return new(arena) WebCore::RenderSVGInlineText(this, str); 0550 } 0551 return new(arena) RenderText(this, str); 0552 } 0553 0554 void TextImpl::attach() 0555 { 0556 createRendererIfNeeded(); 0557 CharacterDataImpl::attach(); 0558 } 0559 0560 NodeImpl::Id TextImpl::id() const 0561 { 0562 return ID_TEXT; 0563 } 0564 0565 void TextImpl::recalcStyle(StyleChange change) 0566 { 0567 // qDebug("textImpl::recalcStyle"); 0568 // Create renderer if now needed 0569 if (changed() && !m_render) { 0570 createRendererIfNeeded(); 0571 } 0572 if (change != NoChange && parentNode()) { 0573 // qDebug("DomText::recalcStyle"); 0574 if (m_render) { 0575 m_render->setStyle(parentNode()->renderer()->style()); 0576 } 0577 } 0578 if (changed() && m_render && m_render->isText()) { 0579 static_cast<RenderText *>(m_render)->setText(str); 0580 } 0581 setChanged(false); 0582 } 0583 0584 // DOM Section 1.1.1 0585 bool TextImpl::childTypeAllowed(unsigned short /*type*/) 0586 { 0587 return false; 0588 } 0589 0590 TextImpl *TextImpl::createNew(DOMStringImpl *_str) 0591 { 0592 return new TextImpl(docPtr(), _str); 0593 } 0594 0595 DOMStringImpl *TextImpl::renderString() const 0596 { 0597 if (renderer()) { 0598 return static_cast<RenderText *>(renderer())->string(); 0599 } else { 0600 return string(); 0601 } 0602 } 0603 0604 static bool textNeedsEscaping(const NodeImpl *n) 0605 { 0606 // Exceptions based on "Serializing HTML fragments" section of 0607 // HTML 5 specification (with some adaptions to reality) 0608 const NodeImpl *p = n->parentNode(); 0609 if (!p) { 0610 return true; 0611 } 0612 switch (p->id()) { 0613 case ID_IFRAME: 0614 // follow deviating examples of FF 3.5.6 and Opera 9.6 0615 // case ID_NOEMBED: 0616 // case ID_NOFRAMES: 0617 case ID_NOSCRIPT: 0618 case ID_PLAINTEXT: 0619 case ID_SCRIPT: 0620 case ID_STYLE: 0621 case ID_XMP: 0622 return false; 0623 default: 0624 return true; 0625 } 0626 } 0627 0628 DOMString TextImpl::toString() const 0629 { 0630 return textNeedsEscaping(this) ? escapeHTML(nodeValue()) : nodeValue(); 0631 } 0632 0633 DOMString TextImpl::toString(long long startOffset, long long endOffset) const 0634 { 0635 DOMString str = nodeValue(); 0636 if (endOffset >= 0 || startOffset > 0) { 0637 str = str.copy(); //we are going to modify this, so make a copy. I hope I'm doing this right. 0638 } 0639 if (endOffset >= 0) { 0640 str.truncate(endOffset); 0641 } 0642 if (startOffset > 0) { //note the order of these 2 'if' statements so that it works right when n==m_startContainer==m_endContainer 0643 str.remove(0, startOffset); 0644 } 0645 return textNeedsEscaping(this) ? escapeHTML(str) : str; 0646 } 0647 0648 // --------------------------------------------------------------------------- 0649 0650 DOMString CDATASectionImpl::nodeName() const 0651 { 0652 return "#cdata-section"; 0653 } 0654 0655 unsigned short CDATASectionImpl::nodeType() const 0656 { 0657 return Node::CDATA_SECTION_NODE; 0658 } 0659 0660 WTF::PassRefPtr<NodeImpl> CDATASectionImpl::cloneNode(bool /*deep*/) 0661 { 0662 int ec = 0; 0663 return document()->createCDATASection(str, ec); 0664 } 0665 0666 // DOM Section 1.1.1 0667 bool CDATASectionImpl::childTypeAllowed(unsigned short /*type*/) 0668 { 0669 return false; 0670 } 0671 0672 TextImpl *CDATASectionImpl::createNew(DOMStringImpl *_str) 0673 { 0674 return new CDATASectionImpl(docPtr(), _str); 0675 } 0676 0677 DOMString CDATASectionImpl::toString() const 0678 { 0679 return DOMString("<![CDATA[") + nodeValue() + DOMString("]]>"); 0680 } 0681 0682 // --------------------------------------------------------------------------- 0683 0684 EditingTextImpl::EditingTextImpl(DocumentImpl *impl, const DOMString &text) 0685 : TextImpl(impl, text.implementation()) 0686 { 0687 } 0688 0689 EditingTextImpl::EditingTextImpl(DocumentImpl *impl) 0690 : TextImpl(impl) 0691 { 0692 } 0693 0694 EditingTextImpl::~EditingTextImpl() 0695 { 0696 } 0697 0698 bool EditingTextImpl::rendererIsNeeded(RenderStyle */*style*/) 0699 { 0700 return true; 0701 } 0702