File indexing completed on 2024-05-05 16:10:09

0001 /**
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
0005  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
0006  *           (C) 2010 Maksim Orlovich (maksim@kde.org)
0007  *
0008  * This library is free software; you can redistribute it and/or
0009  * modify it under the terms of the GNU Library General Public
0010  * License as published by the Free Software Foundation; either
0011  * version 2 of the License, or (at your option) any later version.
0012  *
0013  * This library is distributed in the hope that it will be useful,
0014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016  * Library General Public License for more details.
0017  *
0018  * You should have received a copy of the GNU Library General Public License
0019  * along with this library; see the file COPYING.LIB.  If not, write to
0020  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021  * Boston, MA 02110-1301, USA.
0022  *
0023  */
0024 
0025 #include "html/html_documentimpl.h"
0026 #include "html/html_imageimpl.h"
0027 #include "html/html_headimpl.h"
0028 #include "html/html_baseimpl.h"
0029 #include "html/htmltokenizer.h"
0030 #include "html/html_formimpl.h"
0031 
0032 #include "khtmlview.h"
0033 #include "khtml_part.h"
0034 #include "khtmlpart_p.h"
0035 #include "khtml_settings.h"
0036 
0037 #include "xml/xml_tokenizer.h"
0038 #include "xml/dom2_eventsimpl.h"
0039 
0040 #include <khtml_global.h>
0041 #include "rendering/render_object.h"
0042 #include "dom/dom_exception.h"
0043 
0044 #include "khtml_debug.h"
0045 #include <QDBusConnection>
0046 #include <kcookiejar_interface.h>
0047 
0048 #include "css/cssproperties.h"
0049 #include "css/cssstyleselector.h"
0050 #include "css/css_stylesheetimpl.h"
0051 #include <stdlib.h>
0052 
0053 using namespace DOM;
0054 using namespace khtml;
0055 
0056 HTMLDocumentImpl::HTMLDocumentImpl(KHTMLView *v)
0057     : DocumentImpl(v)
0058 {
0059 //    qCDebug(KHTML_LOG) << "HTMLDocumentImpl constructor this = " << this;
0060     htmlElement = nullptr;
0061 
0062     m_doAutoFill = false;
0063     m_determineParseMode = false;
0064 
0065     /* dynamic history stuff to be fixed later (pfeiffer)
0066         connect( KHTMLGlobal::vLinks(), SIGNAL(removed(QString)),
0067                  SLOT(slotHistoryChanged()));
0068     */
0069     connect(KHTMLGlobal::vLinks(), SIGNAL(inserted(QString)),
0070             SLOT(slotHistoryChanged()));
0071     connect(KHTMLGlobal::vLinks(), SIGNAL(cleared()),
0072             SLOT(slotHistoryChanged()));
0073 }
0074 
0075 HTMLDocumentImpl::~HTMLDocumentImpl()
0076 {
0077 }
0078 
0079 DOMString HTMLDocumentImpl::referrer() const
0080 {
0081     if (part()) {
0082         return part()->pageReferrer();
0083     }
0084     return DOMString();
0085 }
0086 
0087 DOMString HTMLDocumentImpl::lastModified() const
0088 {
0089     if (part()) {
0090         return part()->lastModified();
0091     }
0092     return DOMString();
0093 }
0094 
0095 DOMString HTMLDocumentImpl::cookie() const
0096 {
0097     WId windowId = 0;
0098     KHTMLView *v = view();
0099 
0100     if (v && v->topLevelWidget()) {
0101         windowId = v->topLevelWidget()->winId();
0102     }
0103 
0104     org::kde::KCookieServer kcookiejar("org.kde.kded5", "/modules/kcookiejar", QDBusConnection::sessionBus());
0105     QDBusReply<QString> reply = kcookiejar.findDOMCookies(URL().url(), qlonglong(windowId));
0106 
0107     if (!reply.isValid()) {
0108         qCWarning(KHTML_LOG) << "Can't communicate with cookiejar!";
0109         return DOMString();
0110     }
0111 
0112     return DOMString(reply.value());
0113 }
0114 
0115 void HTMLDocumentImpl::setCookie(const DOMString &value)
0116 {
0117     WId windowId = 0;
0118     KHTMLView *v = view();
0119 
0120     if (v && v->topLevelWidget()) {
0121         windowId = v->topLevelWidget()->winId();
0122     }
0123 
0124     QByteArray fake_header("Set-Cookie: ");
0125     fake_header.append(value.string().toLatin1().constData());
0126     fake_header.append("\n");
0127     // Note that kded modules are autoloaded so we don't need to call loadModule ourselves.
0128     org::kde::KCookieServer kcookiejar("org.kde.kded5", "/modules/kcookiejar", QDBusConnection::sessionBus());
0129     // Can't use kcookiejar.addCookies because then we can't pass NoBlock...
0130     kcookiejar.call(QDBus::NoBlock, "addCookies",
0131                     URL().url(), fake_header, qlonglong(windowId));
0132 }
0133 
0134 void HTMLDocumentImpl::setBody(HTMLElementImpl *_body, int &exceptioncode)
0135 {
0136     HTMLElementImpl *b = body();
0137     if (!_body) {
0138         exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
0139         return;
0140     }
0141     if (!b) {
0142         documentElement()->appendChild(_body, exceptioncode);
0143     } else {
0144         documentElement()->replaceChild(_body, b, exceptioncode);
0145     }
0146 }
0147 
0148 Tokenizer *HTMLDocumentImpl::createTokenizer()
0149 {
0150     return new HTMLTokenizer(docPtr(), m_view);
0151 }
0152 
0153 // --------------------------------------------------------------------------
0154 // not part of the DOM
0155 // --------------------------------------------------------------------------
0156 
0157 bool HTMLDocumentImpl::childAllowed(NodeImpl *newChild)
0158 {
0159     // ### support comments. etc as a child
0160     return (newChild->id() == ID_HTML || newChild->id() == ID_COMMENT || newChild->nodeType() == Node::DOCUMENT_TYPE_NODE);
0161 }
0162 
0163 ElementImpl *HTMLDocumentImpl::createElement(const DOMString &name, int *pExceptioncode)
0164 {
0165     if (pExceptioncode && !Element::khtmlValidQualifiedName(name)) {
0166         *pExceptioncode = DOMException::INVALID_CHARACTER_ERR;
0167         return nullptr;
0168     }
0169 
0170     return createHTMLElement(name, hMode != XHtml);
0171 }
0172 
0173 ElementImpl *HTMLDocumentImpl::activeElement() const
0174 {
0175     NodeImpl *fn = focusNode();
0176     if (!fn || !fn->isElementNode()) {
0177         return body();
0178     } else {
0179         return static_cast<ElementImpl *>(fn);
0180     }
0181 }
0182 
0183 void HTMLDocumentImpl::slotHistoryChanged()
0184 {
0185     if (true || !m_render) {
0186         return;
0187     }
0188 
0189     recalcStyle(Force);
0190     m_render->repaint();
0191 }
0192 
0193 HTMLMapElementImpl *HTMLDocumentImpl::getMap(const DOMString &_url)
0194 {
0195     QString url = _url.string();
0196     QString s;
0197     int pos = url.indexOf('#');
0198     //qCDebug(KHTML_LOG) << "map pos of #:" << pos;
0199     s = QString(_url.unicode() + pos + 1, _url.length() - pos - 1);
0200 
0201     QMap<QString, HTMLMapElementImpl *>::const_iterator it = mapMap.constFind(s);
0202 
0203     if (it != mapMap.constEnd()) {
0204         return *it;
0205     } else {
0206         return nullptr;
0207     }
0208 }
0209 
0210 void HTMLDocumentImpl::contentLoaded()
0211 {
0212     // auto fill: walk the tree and try to fill in login credentials
0213     if (view() && m_doAutoFill) {
0214         for (NodeImpl *n = this; n; n = n->traverseNextNode())
0215             if (n->id() == ID_FORM) {
0216                 static_cast<HTMLFormElementImpl *>(n)->doAutoFill();
0217             }
0218         m_doAutoFill = false;
0219     }
0220 }
0221 
0222 void HTMLDocumentImpl::close()
0223 {
0224     bool doload = !parsing() && m_tokenizer;
0225 
0226     DocumentImpl::close();
0227 
0228     if (doload) {
0229 
0230         if (title().isEmpty()) { // ensure setTitle is called at least once
0231             setTitle(DOMString());
0232         }
0233 
0234         // According to dom the load event must not bubble
0235         // but other browsers execute in a frameset document
0236         // the first(IE)/last(Moz/Konq) registered onload on a <frame> and the
0237         // first(IE)/last(Moz/Konq) registered onload on a <frameset>.
0238 
0239         //qCDebug(KHTML_LOG) << "dispatching LOAD_EVENT on document " << document() << " " << (view()?view()->part()->name():0);
0240 
0241         //Make sure to flush any pending image events now, as we want them out before the document's load event
0242         dispatchImageLoadEventsNow();
0243         document()->dispatchWindowEvent(EventImpl::LOAD_EVENT, false, false);
0244 
0245         // don't update rendering if we're going to redirect anyway
0246         if (part() && (part()->d->m_redirectURL.isEmpty() ||
0247                        part()->d->m_delayRedirect > 1)) {
0248             updateRendering();
0249         }
0250     }
0251 }
0252 
0253 void HTMLDocumentImpl::determineParseMode()
0254 {
0255     m_determineParseMode = true;
0256     if (m_doctype == nullptr) {
0257         // Currently we haven't got any doctype, so default to quirks mode and Html4
0258         changeModes(Compat, Html4);
0259     }
0260 }
0261 
0262 void HTMLDocumentImpl::changeModes(ParseMode newPMode, HTMLMode newHMode)
0263 {
0264     if (!m_determineParseMode) { // change mode only when KHTMLPart called determineParseMode
0265         return;
0266     }
0267     ParseMode oldPMode = pMode;
0268     pMode = newPMode;
0269     hMode = newHMode;
0270     // This needs to be done last, see tests/parser/compatmode_xhtml_mixed.html
0271     if (hMode == Html4 && !m_htmlRequested) {
0272         // this part is still debatable and possibly UA dependent
0273         hMode = XHtml;
0274         pMode = Transitional;
0275     }
0276     m_htmlCompat = (hMode != XHtml);
0277 
0278     m_styleSelector->strictParsing = !inCompatMode();
0279 
0280 #if 0
0281     qCDebug(KHTML_LOG) << "DocumentImpl::determineParseMode: publicId =" << publicID << " systemId = " << systemID;
0282     qCDebug(KHTML_LOG) << "DocumentImpl::determineParseMode: htmlMode = " << hMode;
0283     if (pMode == Strict) {
0284         qCDebug(KHTML_LOG) << " using strict parseMode";
0285     } else if (pMode == Compat) {
0286         qCDebug(KHTML_LOG) << " using compatibility parseMode";
0287     } else {
0288         qCDebug(KHTML_LOG) << " using transitional parseMode";
0289     }
0290 #endif
0291 
0292     if (pMode != oldPMode && styleSelector()) {
0293         updateStyleSelector(true/*shallow*/);
0294     }
0295 }
0296 
0297 NodeListImpl *HTMLDocumentImpl::getElementsByName(const DOMString &elementName)
0298 {
0299     return new NameNodeListImpl(this, elementName);
0300 }
0301 
0302 HTMLCollectionImpl *HTMLDocumentImpl::images()
0303 {
0304     return new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_IMAGES);
0305 }
0306 
0307 HTMLCollectionImpl *HTMLDocumentImpl::applets()
0308 {
0309     return new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_APPLETS);
0310 }
0311 
0312 HTMLCollectionImpl *HTMLDocumentImpl::links()
0313 {
0314     return new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_LINKS);
0315 }
0316 
0317 HTMLCollectionImpl *HTMLDocumentImpl::forms()
0318 {
0319     return new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_FORMS);
0320 }
0321 
0322 HTMLCollectionImpl *HTMLDocumentImpl::layers()
0323 {
0324     return new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_LAYERS);
0325 }
0326 
0327 HTMLCollectionImpl *HTMLDocumentImpl::anchors()
0328 {
0329     return new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_ANCHORS);
0330 }
0331 
0332 HTMLCollectionImpl *HTMLDocumentImpl::all()
0333 {
0334     return new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_ALL);
0335 }
0336 
0337 HTMLCollectionImpl *HTMLDocumentImpl::scripts()
0338 {
0339     return new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_SCRIPTS);
0340 }
0341 
0342 // --------------------------------------------------------------------------
0343 // Support for displaying plaintext
0344 // --------------------------------------------------------------------------
0345 
0346 class HTMLTextTokenizer: public khtml::Tokenizer
0347 {
0348 public:
0349     HTMLTextTokenizer(DOM::HTMLDocumentImpl *doc): m_doc(doc)
0350     {}
0351 
0352     void begin() override;
0353     void write(const TokenizerString &str, bool appendData) override;
0354 
0355     void end() override
0356     {
0357         emit finishedParsing();
0358     };
0359     void finish() override
0360     {
0361         end();
0362     };
0363 
0364     // We don't support any inline scripts here
0365     bool isWaitingForScripts() const override
0366     {
0367         return false;
0368     }
0369     bool isExecutingScript() const override
0370     {
0371         return false;
0372     }
0373     void executeScriptsWaitingForStylesheets() override {};
0374 private:
0375     DOM::HTMLDocumentImpl *m_doc;
0376 };
0377 
0378 void HTMLTextTokenizer::begin()
0379 {
0380     int dummy;
0381     DOM::ElementImpl *html = m_doc->createElement("html", &dummy);
0382     DOM::ElementImpl *head = m_doc->createElement("head", &dummy);
0383     DOM::ElementImpl *body = m_doc->createElement("body", &dummy);
0384     DOM::ElementImpl *pre  = m_doc->createElement("pre", &dummy);
0385 
0386     m_doc->appendChild(html, dummy);
0387     html->appendChild(head, dummy);
0388     html->appendChild(body, dummy);
0389     body->appendChild(pre, dummy);
0390 }
0391 
0392 void HTMLTextTokenizer::write(const TokenizerString &str, bool /*appendData*/)
0393 {
0394     // A potential worry here is the document being modified by
0395     // a script while we're still loading. To handle that, we always look up
0396     // the pre again, and append to it, even for document.write mess
0397     // and the like.
0398     RefPtr<NodeListImpl> coll = m_doc->getElementsByTagName("pre");
0399     if (coll->length() >= 1ul) {
0400         int dummy;
0401         coll->item(0)->appendChild(m_doc->createTextNode(str.toString()), dummy);
0402     }
0403 }
0404 
0405 HTMLTextDocumentImpl::HTMLTextDocumentImpl(KHTMLView *v): HTMLDocumentImpl(v)
0406 {}
0407 
0408 khtml::Tokenizer *HTMLTextDocumentImpl::createTokenizer()
0409 {
0410     return new HTMLTextTokenizer(this);
0411 }
0412 
0413 #include "moc_html_documentimpl.cpp"