File indexing completed on 2024-05-05 16:11:42

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