File indexing completed on 2024-04-28 11:39:38

0001 /**
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 2000 Peter Kelly (pmk@post.com)
0005  *
0006  * This library is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Library General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2 of the License, or (at your option) any later version.
0010  *
0011  * This library is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014  * Library General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU Library General Public License
0017  * along with this library; see the file COPYING.LIB.  If not, write to
0018  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020  */
0021 
0022 #include "dom_xmlimpl.h"
0023 
0024 #include <dom/dom_exception.h>
0025 
0026 #include "dom_docimpl.h"
0027 #include "dom_stringimpl.h"
0028 #include <css/css_stylesheetimpl.h>
0029 #include <css/css_mediaquery.h>
0030 #include <misc/loader.h>
0031 
0032 using namespace DOM;
0033 
0034 EntityImpl::EntityImpl(DocumentImpl *doc) : NodeBaseImpl(doc)
0035 {
0036     m_publicId = nullptr;
0037     m_systemId = nullptr;
0038     m_notationName = nullptr;
0039     m_name = nullptr;
0040 }
0041 
0042 EntityImpl::EntityImpl(DocumentImpl *doc, DOMString _name) : NodeBaseImpl(doc)
0043 {
0044     m_publicId = nullptr;
0045     m_systemId = nullptr;
0046     m_notationName = nullptr;
0047     m_name = _name.implementation();
0048     if (m_name) {
0049         m_name->ref();
0050     }
0051 }
0052 
0053 EntityImpl::EntityImpl(DocumentImpl *doc, DOMString _publicId, DOMString _systemId, DOMString _notationName) : NodeBaseImpl(doc)
0054 {
0055     m_publicId = _publicId.implementation();
0056     if (m_publicId) {
0057         m_publicId->ref();
0058     }
0059     m_systemId = _systemId.implementation();
0060     if (m_systemId) {
0061         m_systemId->ref();
0062     }
0063     m_notationName = _notationName.implementation();
0064     if (m_notationName) {
0065         m_notationName->ref();
0066     }
0067     m_name = nullptr;
0068 }
0069 
0070 EntityImpl::~EntityImpl()
0071 {
0072     if (m_publicId) {
0073         m_publicId->deref();
0074     }
0075     if (m_systemId) {
0076         m_systemId->deref();
0077     }
0078     if (m_notationName) {
0079         m_notationName->deref();
0080     }
0081     if (m_name) {
0082         m_name->deref();
0083     }
0084 }
0085 
0086 DOMString EntityImpl::publicId() const
0087 {
0088     return m_publicId;
0089 }
0090 
0091 DOMString EntityImpl::systemId() const
0092 {
0093     return m_systemId;
0094 }
0095 
0096 DOMString EntityImpl::notationName() const
0097 {
0098     return m_notationName;
0099 }
0100 
0101 DOMString EntityImpl::nodeName() const
0102 {
0103     return m_name;
0104 }
0105 
0106 unsigned short EntityImpl::nodeType() const
0107 {
0108     return Node::ENTITY_NODE;
0109 }
0110 
0111 WTF::PassRefPtr<NodeImpl> EntityImpl::cloneNode(bool /*deep*/)
0112 {
0113     // Spec says cloning Document nodes is "implementation dependent"
0114     // so we do not support it...
0115     return nullptr;
0116 }
0117 
0118 // DOM Section 1.1.1
0119 bool EntityImpl::childTypeAllowed(unsigned short type)
0120 {
0121     switch (type) {
0122     case Node::ELEMENT_NODE:
0123     case Node::PROCESSING_INSTRUCTION_NODE:
0124     case Node::COMMENT_NODE:
0125     case Node::TEXT_NODE:
0126     case Node::CDATA_SECTION_NODE:
0127     case Node::ENTITY_REFERENCE_NODE:
0128         return true;
0129         break;
0130     default:
0131         return false;
0132     }
0133 }
0134 
0135 DOMString EntityImpl::toString() const
0136 {
0137     DOMString result = "<!ENTITY' ";
0138 
0139     if (m_name && m_name->l != 0) {
0140         result += " ";
0141         result += m_name;
0142     }
0143 
0144     if (m_publicId && m_publicId->l != 0) {
0145         result += " PUBLIC \"";
0146         result += m_publicId;
0147         result += "\" \"";
0148         result += m_systemId;
0149         result += "\"";
0150     } else if (m_systemId && m_systemId->l != 0) {
0151         result += " SYSTEM \"";
0152         result += m_systemId;
0153         result += "\"";
0154     }
0155 
0156     if (m_notationName && m_notationName->l != 0) {
0157         result += " NDATA ";
0158         result += m_notationName;
0159     }
0160 
0161     result += ">";
0162 
0163     return result;
0164 }
0165 
0166 // -------------------------------------------------------------------------
0167 
0168 EntityReferenceImpl::EntityReferenceImpl(DocumentImpl *doc) : NodeBaseImpl(doc)
0169 {
0170     m_entityName = nullptr;
0171 }
0172 
0173 EntityReferenceImpl::EntityReferenceImpl(DocumentImpl *doc, DOMStringImpl *_entityName) : NodeBaseImpl(doc)
0174 {
0175     m_entityName = _entityName;
0176     if (m_entityName) {
0177         m_entityName->ref();
0178     }
0179 }
0180 
0181 EntityReferenceImpl::~EntityReferenceImpl()
0182 {
0183     if (m_entityName) {
0184         m_entityName->deref();
0185     }
0186 }
0187 
0188 DOMString EntityReferenceImpl::nodeName() const
0189 {
0190     return m_entityName;
0191 }
0192 
0193 unsigned short EntityReferenceImpl::nodeType() const
0194 {
0195     return Node::ENTITY_REFERENCE_NODE;
0196 }
0197 
0198 WTF::PassRefPtr<NodeImpl> EntityReferenceImpl::cloneNode(bool deep)
0199 {
0200     EntityReferenceImpl *clone = new EntityReferenceImpl(docPtr(), m_entityName);
0201     // ### make sure children are readonly
0202     // ### since we are a reference, should we clone children anyway (even if not deep?)
0203     if (deep) {
0204         cloneChildNodes(clone);
0205     }
0206     return clone;
0207 }
0208 
0209 // DOM Section 1.1.1
0210 bool EntityReferenceImpl::childTypeAllowed(unsigned short type)
0211 {
0212     switch (type) {
0213     case Node::ELEMENT_NODE:
0214     case Node::PROCESSING_INSTRUCTION_NODE:
0215     case Node::COMMENT_NODE:
0216     case Node::TEXT_NODE:
0217     case Node::CDATA_SECTION_NODE:
0218     case Node::ENTITY_REFERENCE_NODE:
0219         return true;
0220         break;
0221     default:
0222         return false;
0223     }
0224 }
0225 
0226 DOMString EntityReferenceImpl::toString() const
0227 {
0228     DOMString result = "&";
0229     result += m_entityName;
0230     result += ";";
0231 
0232     return result;
0233 }
0234 
0235 // -------------------------------------------------------------------------
0236 
0237 NotationImpl::NotationImpl(DocumentImpl *doc) : NodeBaseImpl(doc)
0238 {
0239     m_publicId = nullptr;
0240     m_systemId = nullptr;
0241     m_name = nullptr;
0242 }
0243 
0244 NotationImpl::NotationImpl(DocumentImpl *doc, DOMString _name, DOMString _publicId, DOMString _systemId) : NodeBaseImpl(doc)
0245 {
0246     m_name = _name.implementation();
0247     if (m_name) {
0248         m_name->ref();
0249     }
0250     m_publicId = _publicId.implementation();
0251     if (m_publicId) {
0252         m_publicId->ref();
0253     }
0254     m_systemId = _systemId.implementation();
0255     if (m_systemId) {
0256         m_systemId->ref();
0257     }
0258 }
0259 
0260 NotationImpl::~NotationImpl()
0261 {
0262     if (m_name) {
0263         m_name->deref();
0264     }
0265     if (m_publicId) {
0266         m_publicId->deref();
0267     }
0268     if (m_systemId) {
0269         m_systemId->deref();
0270     }
0271 }
0272 
0273 DOMString NotationImpl::publicId() const
0274 {
0275     return m_publicId;
0276 }
0277 
0278 DOMString NotationImpl::systemId() const
0279 {
0280     return m_systemId;
0281 }
0282 
0283 DOMString NotationImpl::nodeName() const
0284 {
0285     return m_name;
0286 }
0287 
0288 unsigned short NotationImpl::nodeType() const
0289 {
0290     return Node::NOTATION_NODE;
0291 }
0292 
0293 WTF::PassRefPtr<NodeImpl> NotationImpl::cloneNode(bool /*deep*/)
0294 {
0295     // Spec says cloning Document nodes is "implementation dependent"
0296     // so we do not support it...
0297     return nullptr;
0298 }
0299 
0300 // DOM Section 1.1.1
0301 bool NotationImpl::childTypeAllowed(unsigned short /*type*/)
0302 {
0303     return false;
0304 }
0305 
0306 // -------------------------------------------------------------------------
0307 
0308 // ### need a way of updating these properly whenever child nodes of the processing instruction
0309 // change or are added/removed
0310 
0311 ProcessingInstructionImpl::ProcessingInstructionImpl(DocumentImpl *doc) : NodeBaseImpl(doc)
0312 {
0313     m_target = nullptr;
0314     m_data = nullptr;
0315     m_localHref = nullptr;
0316     m_alternate = false;
0317     m_title = nullptr;
0318     m_media = nullptr;
0319     m_sheet = nullptr;
0320     m_cachedSheet = nullptr;
0321 }
0322 
0323 ProcessingInstructionImpl::ProcessingInstructionImpl(DocumentImpl *doc, DOMString _target, DOMString _data) : NodeBaseImpl(doc)
0324 {
0325     m_target = _target.implementation();
0326     if (m_target) {
0327         m_target->ref();
0328     }
0329     m_data = _data.implementation();
0330     if (m_data) {
0331         m_data->ref();
0332     }
0333     m_sheet = nullptr;
0334     m_cachedSheet = nullptr;
0335     m_localHref = nullptr;
0336     m_title = nullptr;
0337     m_media = nullptr;
0338     m_alternate = false;
0339 }
0340 
0341 ProcessingInstructionImpl::~ProcessingInstructionImpl()
0342 {
0343     if (m_target) {
0344         m_target->deref();
0345     }
0346     if (m_title) {
0347         m_title->deref();
0348     }
0349     if (m_media) {
0350         m_media->deref();
0351     }
0352     if (m_data) {
0353         m_data->deref();
0354     }
0355     if (m_cachedSheet) {
0356         m_cachedSheet->deref(this);
0357     }
0358     if (m_sheet) {
0359         m_sheet->deref();
0360     }
0361 }
0362 
0363 DOMString ProcessingInstructionImpl::target() const
0364 {
0365     return m_target;
0366 }
0367 
0368 void ProcessingInstructionImpl::setData(const DOMString &_data, int &exceptioncode)
0369 {
0370     // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
0371     if (isReadOnly()) {
0372         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
0373         return;
0374     }
0375 
0376     if (m_data) {
0377         m_data->deref();
0378     }
0379     m_data = _data.implementation();
0380     if (m_data) {
0381         m_data->ref();
0382     }
0383 }
0384 
0385 DOMString ProcessingInstructionImpl::nodeName() const
0386 {
0387     return m_target;
0388 }
0389 
0390 unsigned short ProcessingInstructionImpl::nodeType() const
0391 {
0392     return Node::PROCESSING_INSTRUCTION_NODE;
0393 }
0394 
0395 DOMString ProcessingInstructionImpl::nodeValue() const
0396 {
0397     return m_data;
0398 }
0399 
0400 void ProcessingInstructionImpl::setNodeValue(const DOMString &_nodeValue, int &exceptioncode)
0401 {
0402     // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData()
0403     setData(_nodeValue, exceptioncode);
0404 }
0405 
0406 WTF::PassRefPtr<NodeImpl> ProcessingInstructionImpl::cloneNode(bool /*deep*/)
0407 {
0408     // ### copy m_localHref
0409     return new ProcessingInstructionImpl(docPtr(), m_target, m_data);
0410 }
0411 
0412 DOMString ProcessingInstructionImpl::localHref() const
0413 {
0414     return m_localHref;
0415 }
0416 
0417 // DOM Section 1.1.1
0418 bool ProcessingInstructionImpl::childTypeAllowed(unsigned short /*type*/)
0419 {
0420     return false;
0421 }
0422 
0423 void ProcessingInstructionImpl::checkStyleSheet()
0424 {
0425     if (m_target && DOMString(m_target) == "xml-stylesheet") {
0426         // see https://www.w3.org/TR/xml-stylesheet/
0427         // ### check that this occurs only in the prolog
0428         // ### support stylesheet included in a fragment of this (or another) document
0429         // ### make sure this gets called when adding from javascript
0430         XMLAttributeReader attrReader(DOMString(m_data).string());
0431         bool attrsOk;
0432         QXmlAttributes attrs = attrReader.readAttrs(attrsOk);
0433         if (!attrsOk) {
0434             return;
0435         }
0436         if (attrs.value("type") != "text/css" && !attrs.value("type").isEmpty()) {
0437             return;
0438         }
0439 
0440         DOMString href = attrs.value("href");
0441         DOMString alternate = attrs.value("alternate");
0442         m_alternate = alternate == "yes";
0443         DOMString title = attrs.value("title");
0444         DOMString media = attrs.value("media");
0445         if (m_title) {
0446             m_title->deref();
0447         }
0448         m_title = title.implementation();
0449         if (m_title) {
0450             m_title->ref();
0451         }
0452         if (m_media) {
0453             m_media->deref();
0454         }
0455         m_media = media.implementation();
0456         if (m_media) {
0457             m_media->ref();
0458         }
0459 
0460         if (href.length() > 1) {
0461             if (href[0] == '#') {
0462                 if (m_localHref) {
0463                     m_localHref->deref();
0464                 }
0465                 m_localHref = href.implementation()->split(1);
0466                 if (m_localHref) {
0467                     m_localHref->ref();
0468                 }
0469             } else {
0470                 // ### some validation on the URL?
0471                 // ### FIXME charset
0472                 if (m_cachedSheet) {
0473                     m_cachedSheet->deref(this);
0474                 }
0475                 m_cachedSheet = document()->docLoader()->requestStyleSheet(document()->completeURL(href.string()), QString());
0476                 if (m_cachedSheet) {
0477                     document()->addPendingSheet(); //before ref, because during the ref it might load!
0478                     m_cachedSheet->ref(this);
0479                 }
0480             }
0481 
0482         }
0483     }
0484 }
0485 
0486 StyleSheetImpl *ProcessingInstructionImpl::sheet() const
0487 {
0488     return m_sheet;
0489 }
0490 
0491 void ProcessingInstructionImpl::setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheet, const DOM::DOMString &charset, const DOM::DOMString &mimetype)
0492 {
0493     if (m_sheet) {
0494         m_sheet->deref();
0495     }
0496     m_sheet = new CSSStyleSheetImpl(document(), url);
0497     m_sheet->ref();
0498     m_sheet->setCharset(charset);
0499     m_sheet->setTitle(m_title);
0500     m_sheet->setMedia(new MediaListImpl((CSSStyleSheetImpl *)m_sheet, m_media, false));
0501     m_sheet->parseString(khtml::isAcceptableCSSMimetype(mimetype) ? sheet : "");
0502     if (m_cachedSheet) {
0503         m_cachedSheet->deref(this);
0504     }
0505     m_cachedSheet = nullptr;
0506 
0507     document()->styleSheetLoaded();
0508 }
0509 
0510 void ProcessingInstructionImpl::setStyleSheet(CSSStyleSheetImpl *sheet)
0511 {
0512     if (m_sheet) {
0513         m_sheet->deref();
0514     }
0515     m_sheet = sheet;
0516     if (m_sheet) {
0517         m_sheet->ref();
0518         m_sheet->setTitle(m_title);
0519     }
0520 }
0521 
0522 DOMString ProcessingInstructionImpl::toString() const
0523 {
0524     DOMString result = "<?";
0525     result += m_target;
0526     result += " ";
0527     result += m_data;
0528     result += ">";
0529     return result;
0530 }
0531 
0532 int ProcessingInstructionImpl::maxCharacterOffset() const
0533 {
0534     return m_data ? static_cast<int>(m_data->length()) : 0;
0535 }
0536 
0537 // -------------------------------------------------------------------------
0538 
0539 XMLAttributeReader::XMLAttributeReader(const QString &_attrString)
0540 {
0541     m_attrString = _attrString;
0542 }
0543 
0544 XMLAttributeReader::~XMLAttributeReader()
0545 {
0546 }
0547 
0548 QXmlAttributes XMLAttributeReader::readAttrs(bool &ok)
0549 {
0550     // parse xml file
0551     QXmlInputSource source;
0552     source.setData("<?xml version=\"1.0\"?><attrs " + m_attrString + " />");
0553     QXmlSimpleReader reader;
0554     reader.setContentHandler(this);
0555     ok = reader.parse(source);
0556     return attrs;
0557 }
0558 
0559 bool XMLAttributeReader::startElement(const QString & /*namespaceURI*/, const QString &localName,
0560                                       const QString & /*qName*/, const QXmlAttributes &atts)
0561 {
0562     if (localName == "attrs") {
0563         attrs = atts;
0564         return true;
0565     } else {
0566         return false;    // we shouldn't have any other elements
0567     }
0568 }