File indexing completed on 2024-04-21 14:58:55

0001 /* This file is part of the KDE project
0002  *
0003  * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
0004  *                     1999 Lars Knoll <knoll@kde.org>
0005  *                     1999 Antti Koivisto <koivisto@kde.org>
0006  *                     2000 Simon Hausmann <hausmann@kde.org>
0007  *                     2000 Stefan Schimanski <1Stein@gmx.de>
0008  *                     2001-2005 George Staikos <staikos@kde.org>
0009  *                     2001-2003 Dirk Mueller <mueller@kde.org>
0010  *                     2000-2005 David Faure <faure@kde.org>
0011  *                     2002 Apple Computer, Inc.
0012  *                     2010 Maksim Orlovich (maksim@kde.org)
0013  *
0014  * This library is free software; you can redistribute it and/or
0015  * modify it under the terms of the GNU Library General Public
0016  * License as published by the Free Software Foundation; either
0017  * version 2 of the License, or (at your option) any later version.
0018  *
0019  * This library is distributed in the hope that it will be useful,
0020  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0022  * Library General Public License for more details.
0023  *
0024  * You should have received a copy of the GNU Library General Public License
0025  * along with this library; see the file COPYING.LIB.  If not, write to
0026  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0027  * Boston, MA 02110-1301, USA.
0028  */
0029 
0030 //#define SPEED_DEBUG
0031 #include "khtml_part.h"
0032 
0033 #include "ui_htmlpageinfo.h"
0034 
0035 #include "khtmlviewbar.h"
0036 #include "khtml_pagecache.h"
0037 
0038 #include "dom/dom_string.h"
0039 #include "dom/dom_element.h"
0040 #include "dom/dom_exception.h"
0041 #include "dom/html_document.h"
0042 #include "editing/editor.h"
0043 #include "html/html_documentimpl.h"
0044 #include "html/html_baseimpl.h"
0045 #include "html/html_objectimpl.h"
0046 #include "html/html_miscimpl.h"
0047 #include "html/html_imageimpl.h"
0048 #include "imload/imagemanager.h"
0049 #include "rendering/render_text.h"
0050 #include "rendering/render_frames.h"
0051 #include "rendering/render_layer.h"
0052 #include "rendering/render_position.h"
0053 #include "misc/loader.h"
0054 #include "misc/khtml_partaccessor.h"
0055 #include "xml/dom2_eventsimpl.h"
0056 #include "xml/dom2_rangeimpl.h"
0057 #include "xml/xml_tokenizer.h"
0058 #include "css/cssstyleselector.h"
0059 using namespace DOM;
0060 
0061 #include "khtmlview.h"
0062 #include <kparts/partmanager.h>
0063 #include <kparts/browseropenorsavequestion.h>
0064 #include <kparts/guiactivateevent.h>
0065 
0066 #include <kacceleratormanager.h>
0067 #include "ecma/kjs_proxy.h"
0068 #include "ecma/kjs_window.h"
0069 #include "ecma/kjs_events.h"
0070 #include "khtml_settings.h"
0071 #include "kjserrordlg.h"
0072 
0073 #include <kjs/function.h>
0074 #include <kjs/interpreter.h>
0075 
0076 #include <sys/types.h>
0077 #include <assert.h>
0078 
0079 #include <kstringhandler.h>
0080 #include <kio/job.h>
0081 #include <kio/jobuidelegate.h>
0082 #include <kio/global.h>
0083 #include <kio/pixmaploader.h>
0084 #include <kio/hostinfo.h>
0085 #include <kprotocolmanager.h>
0086 #include "khtml_debug.h"
0087 #include <kjobwidgets.h>
0088 #include <kmessagebox.h>
0089 #include <kstandardaction.h>
0090 #include <kstandardguiitem.h>
0091 #include <kactioncollection.h>
0092 #include <kmimetypetrader.h>
0093 #include <qtemporaryfile.h>
0094 #include <ktoolinvocation.h>
0095 #include <kurlauthorized.h>
0096 #include <kparts/browserinterface.h>
0097 #include <kparts/scriptableextension.h>
0098 #include <kparts/liveconnectextension.h>
0099 #include <kactionmenu.h>
0100 #include <ktoggleaction.h>
0101 #include <kcodecaction.h>
0102 #include <kselectaction.h>
0103 
0104 #include <QDBusConnection>
0105 #include <ksslinfodialog.h>
0106 #include <ksslsettings.h>
0107 
0108 #include <QDBusInterface>
0109 #include <QMimeData>
0110 #include <kfileitem.h>
0111 #include <kurifilter.h>
0112 #include <kurllabel.h>
0113 #include <kurlmimedata.h>
0114 
0115 #include <QClipboard>
0116 #include <QLocale>
0117 #include <QMenu>
0118 #include <QToolTip>
0119 #include <QDrag>
0120 #include <QMouseEvent>
0121 #include <QFile>
0122 #include <QMetaEnum>
0123 #include <QTextDocument>
0124 #include <QDate>
0125 #include <QtNetwork/QSslCertificate>
0126 #include <QStatusBar>
0127 #include <QStyle>
0128 #include <qmimedatabase.h>
0129 #include <qplatformdefs.h>
0130 #include <QFileInfo>
0131 
0132 #include "khtmlpart_p.h"
0133 #include "khtml_iface.h"
0134 
0135 #include "kpassivepopup.h"
0136 #include "rendering/render_form.h"
0137 #include <kwindowsystem.h>
0138 #include <kconfiggroup.h>
0139 #include <ksharedconfig.h>
0140 
0141 #ifdef KJS_DEBUGGER
0142 #include "ecma/debugger/debugwindow.h"
0143 #endif
0144 
0145 // SVG
0146 #include <svg/SVGDocument.h>
0147 #include <qstandardpaths.h>
0148 
0149 bool KHTMLPartPrivate::s_dnsInitialised = false;
0150 
0151 // DNS prefetch settings
0152 static const int sMaxDNSPrefetchPerPage = 42;
0153 static const int sDNSPrefetchTimerDelay = 200;
0154 static const int sDNSTTLSeconds = 400;
0155 static const int sDNSCacheSize = 500;
0156 
0157 namespace khtml
0158 {
0159 
0160 class PartStyleSheetLoader : public CachedObjectClient
0161 {
0162 public:
0163     PartStyleSheetLoader(KHTMLPart *part, DOM::DOMString url, DocLoader *dl)
0164     {
0165         m_part = part;
0166         m_cachedSheet = dl->requestStyleSheet(url, QString(), "text/css",
0167                                               true /* "user sheet" */);
0168         if (m_cachedSheet) {
0169             m_cachedSheet->ref(this);
0170         }
0171     }
0172     virtual ~PartStyleSheetLoader()
0173     {
0174         if (m_cachedSheet) {
0175             m_cachedSheet->deref(this);
0176         }
0177     }
0178     void setStyleSheet(const DOM::DOMString &, const DOM::DOMString &sheet, const DOM::DOMString &, const DOM::DOMString &/*mimetype*/) override
0179     {
0180         if (m_part) {
0181             m_part->setUserStyleSheet(sheet.string());
0182         }
0183 
0184         delete this;
0185     }
0186     void error(int, const QString &) override
0187     {
0188         delete this;
0189     }
0190     QPointer<KHTMLPart> m_part;
0191     khtml::CachedCSSStyleSheet *m_cachedSheet;
0192 };
0193 }
0194 
0195 KHTMLPart::KHTMLPart(QWidget *parentWidget, QObject *parent, GUIProfile prof)
0196     : KParts::ReadOnlyPart(parent)
0197 {
0198     d = nullptr;
0199     KHTMLGlobal::registerPart(this);
0200     setComponentData(KHTMLGlobal::aboutData(), false);
0201     init(new KHTMLView(this, parentWidget), prof);
0202 }
0203 
0204 KHTMLPart::KHTMLPart(KHTMLView *view, QObject *parent, GUIProfile prof)
0205     : KParts::ReadOnlyPart(parent)
0206 {
0207     d = nullptr;
0208     KHTMLGlobal::registerPart(this);
0209     setComponentData(KHTMLGlobal::aboutData(), false);
0210     assert(view);
0211     if (!view->part()) {
0212         view->setPart(this);
0213     }
0214     init(view, prof);
0215 }
0216 
0217 void KHTMLPart::init(KHTMLView *view, GUIProfile prof)
0218 {
0219     if (prof == DefaultGUI) {
0220         setXMLFile("khtml.rc");
0221     } else if (prof == BrowserViewGUI) {
0222         setXMLFile("khtml_browser.rc");
0223     }
0224 
0225     d = new KHTMLPartPrivate(this, parent());
0226 
0227     d->m_view = view;
0228 
0229     if (!parentPart()) {
0230         QWidget *widget = new QWidget(view->parentWidget());
0231         widget->setObjectName("khtml_part_widget");
0232         QVBoxLayout *layout = new QVBoxLayout(widget);
0233         layout->setContentsMargins(0, 0, 0, 0);
0234         layout->setSpacing(0);
0235         widget->setLayout(layout);
0236 
0237         d->m_topViewBar = new KHTMLViewBar(KHTMLViewBar::Top, d->m_view, widget);
0238         d->m_bottomViewBar = new KHTMLViewBar(KHTMLViewBar::Bottom, d->m_view, widget);
0239 
0240         layout->addWidget(d->m_topViewBar);
0241         layout->addWidget(d->m_view);
0242         layout->addWidget(d->m_bottomViewBar);
0243         setWidget(widget);
0244         widget->setFocusProxy(d->m_view);
0245     } else {
0246         setWidget(view);
0247     }
0248 
0249     d->m_guiProfile = prof;
0250     d->m_extension = new KHTMLPartBrowserExtension(this);
0251     d->m_extension->setObjectName("KHTMLBrowserExtension");
0252     d->m_hostExtension = new KHTMLPartBrowserHostExtension(this);
0253     d->m_statusBarExtension = new KParts::StatusBarExtension(this);
0254     d->m_scriptableExtension = new KJS::KHTMLPartScriptable(this);
0255     new KHTMLTextExtension(this);
0256     new KHTMLHtmlExtension(this);
0257     d->m_statusBarPopupLabel = nullptr;
0258     d->m_openableSuppressedPopups = 0;
0259 
0260     d->m_paLoadImages = nullptr;
0261     d->m_paDebugScript = nullptr;
0262     d->m_bMousePressed = false;
0263     d->m_bRightMousePressed = false;
0264     d->m_bCleared = false;
0265 
0266     if (prof == BrowserViewGUI) {
0267         d->m_paViewDocument = new QAction(i18n("View Do&cument Source"), this);
0268         actionCollection()->addAction("viewDocumentSource", d->m_paViewDocument);
0269         connect(d->m_paViewDocument, SIGNAL(triggered(bool)), this, SLOT(slotViewDocumentSource()));
0270         if (!parentPart()) {
0271             actionCollection()->setDefaultShortcut(d->m_paViewDocument,QKeySequence(Qt::CTRL | Qt::Key_U));
0272         }
0273 
0274         d->m_paViewFrame = new QAction(i18n("View Frame Source"), this);
0275         actionCollection()->addAction("viewFrameSource", d->m_paViewFrame);
0276         connect(d->m_paViewFrame, SIGNAL(triggered(bool)), this, SLOT(slotViewFrameSource()));
0277         if (!parentPart()) {
0278             actionCollection()->setDefaultShortcut(d->m_paViewFrame,QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_U));
0279         }
0280 
0281         d->m_paViewInfo = new QAction(i18n("View Document Information"), this);
0282         actionCollection()->addAction("viewPageInfo", d->m_paViewInfo);
0283         if (!parentPart()) {
0284             actionCollection()->setDefaultShortcut(d->m_paViewInfo, QKeySequence(Qt::CTRL | Qt::Key_I));
0285         }
0286         connect(d->m_paViewInfo, SIGNAL(triggered(bool)), this, SLOT(slotViewPageInfo()));
0287 
0288         d->m_paSaveBackground = new QAction(i18n("Save &Background Image As..."), this);
0289         actionCollection()->addAction("saveBackground", d->m_paSaveBackground);
0290         connect(d->m_paSaveBackground, SIGNAL(triggered(bool)), this, SLOT(slotSaveBackground()));
0291 
0292         d->m_paSaveDocument = actionCollection()->addAction(KStandardAction::SaveAs, "saveDocument",
0293                               this, SLOT(slotSaveDocument()));
0294         if (parentPart()) {
0295             d->m_paSaveDocument->setShortcuts(QList<QKeySequence>());    // avoid clashes
0296         }
0297 
0298         d->m_paSaveFrame = new QAction(i18n("Save &Frame As..."), this);
0299         actionCollection()->addAction("saveFrame", d->m_paSaveFrame);
0300         connect(d->m_paSaveFrame, SIGNAL(triggered(bool)), this, SLOT(slotSaveFrame()));
0301     } else {
0302         d->m_paViewDocument = nullptr;
0303         d->m_paViewFrame = nullptr;
0304         d->m_paViewInfo = nullptr;
0305         d->m_paSaveBackground = nullptr;
0306         d->m_paSaveDocument = nullptr;
0307         d->m_paSaveFrame = nullptr;
0308     }
0309 
0310     d->m_paSecurity = new QAction(i18n("SSL"), this);
0311     actionCollection()->addAction("security", d->m_paSecurity);
0312     connect(d->m_paSecurity, SIGNAL(triggered(bool)), this, SLOT(slotSecurity()));
0313 
0314     d->m_paDebugRenderTree = new QAction(i18n("Print Rendering Tree to STDOUT"), this);
0315     actionCollection()->addAction("debugRenderTree", d->m_paDebugRenderTree);
0316     connect(d->m_paDebugRenderTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugRenderTree()));
0317 
0318     d->m_paDebugDOMTree = new QAction(i18n("Print DOM Tree to STDOUT"), this);
0319     actionCollection()->addAction("debugDOMTree", d->m_paDebugDOMTree);
0320     connect(d->m_paDebugDOMTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugDOMTree()));
0321 
0322     QAction *paDebugFrameTree = new QAction(i18n("Print frame tree to STDOUT"), this);
0323     actionCollection()->addAction("debugFrameTree", paDebugFrameTree);
0324     connect(paDebugFrameTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugFrameTree()));
0325 
0326     d->m_paStopAnimations = new QAction(i18n("Stop Animated Images"), this);
0327     actionCollection()->addAction("stopAnimations", d->m_paStopAnimations);
0328     connect(d->m_paStopAnimations, SIGNAL(triggered(bool)), this, SLOT(slotStopAnimations()));
0329 
0330     d->m_paSetEncoding = new KCodecAction(QIcon::fromTheme("character-set"), i18n("Set &Encoding"), this, true);
0331     actionCollection()->addAction("setEncoding", d->m_paSetEncoding);
0332 //   d->m_paSetEncoding->setDelayed( false );
0333 
0334     connect(d->m_paSetEncoding, &KSelectAction::textTriggered, this, &KHTMLPart::slotSetEncoding);
0335     connect(d->m_paSetEncoding, &KCodecAction::encodingProberTriggered, this, &KHTMLPart::slotAutomaticDetectionLanguage);
0336 
0337     if (KSharedConfig::openConfig()->hasGroup("HTML Settings")) {
0338         KConfigGroup config(KSharedConfig::openConfig(), "HTML Settings");
0339 
0340         d->m_autoDetectLanguage = static_cast<KEncodingProber::ProberType>(config.readEntry("AutomaticDetectionLanguage", /*static_cast<int>(language) */0));
0341         if (d->m_autoDetectLanguage == KEncodingProber::None) {
0342             const QByteArray name = QTextCodec::codecForLocale()->name().toLower();
0343 //       qCWarning(KHTML_LOG) << "00000000 ";
0344             if (name.endsWith("1251") || name.startsWith("koi") || name == "iso-8859-5") {
0345                 d->m_autoDetectLanguage = KEncodingProber::Cyrillic;
0346             } else if (name.endsWith("1256") || name == "iso-8859-6") {
0347                 d->m_autoDetectLanguage = KEncodingProber::Arabic;
0348             } else if (name.endsWith("1257") || name == "iso-8859-13" || name == "iso-8859-4") {
0349                 d->m_autoDetectLanguage = KEncodingProber::Baltic;
0350             } else if (name.endsWith("1250") || name == "ibm852" || name == "iso-8859-2" || name == "iso-8859-3") {
0351                 d->m_autoDetectLanguage = KEncodingProber::CentralEuropean;
0352             } else if (name.endsWith("1253") || name == "iso-8859-7") {
0353                 d->m_autoDetectLanguage = KEncodingProber::Greek;
0354             } else if (name.endsWith("1255") || name == "iso-8859-8" || name == "iso-8859-8-i") {
0355                 d->m_autoDetectLanguage = KEncodingProber::Hebrew;
0356             } else if (name == "jis7" || name == "eucjp" || name == "sjis") {
0357                 d->m_autoDetectLanguage = KEncodingProber::Japanese;
0358             } else if (name == "gb2312" || name == "gbk" || name == "gb18030") {
0359                 d->m_autoDetectLanguage = KEncodingProber::ChineseSimplified;
0360             } else if (name == "big5") {
0361                 d->m_autoDetectLanguage = KEncodingProber::ChineseTraditional;
0362             } else if (name == "euc-kr") {
0363                 d->m_autoDetectLanguage = KEncodingProber::Korean;
0364             } else if (name.endsWith("1254") || name == "iso-8859-9") {
0365                 d->m_autoDetectLanguage = KEncodingProber::Turkish;
0366             } else if (name.endsWith("1252") || name == "iso-8859-1" || name == "iso-8859-15") {
0367                 d->m_autoDetectLanguage = KEncodingProber::WesternEuropean;
0368             } else {
0369                 d->m_autoDetectLanguage = KEncodingProber::Universal;
0370             }
0371 //         qCWarning(KHTML_LOG) << "0000000end " << d->m_autoDetectLanguage << " " << QTextCodec::codecForLocale()->mibEnum();
0372         }
0373         d->m_paSetEncoding->setCurrentProberType(d->m_autoDetectLanguage);
0374     }
0375 
0376     d->m_paUseStylesheet = new KSelectAction(i18n("Use S&tylesheet"), this);
0377     actionCollection()->addAction("useStylesheet", d->m_paUseStylesheet);
0378     connect(d->m_paUseStylesheet, SIGNAL(triggered(int)), this, SLOT(slotUseStylesheet()));
0379 
0380     if (prof == BrowserViewGUI) {
0381         d->m_paIncZoomFactor = new KHTMLZoomFactorAction(this, true, "format-font-size-more", i18n("Enlarge Font"), this);
0382         actionCollection()->addAction("incFontSizes", d->m_paIncZoomFactor);
0383         connect(d->m_paIncZoomFactor, SIGNAL(triggered(bool)), SLOT(slotIncFontSizeFast()));
0384         d->m_paIncZoomFactor->setWhatsThis(i18n("<qt>Enlarge Font<br /><br />"
0385                                                 "Make the font in this window bigger. "
0386                                                 "Click and hold down the mouse button for a menu with all available font sizes.</qt>"));
0387 
0388         d->m_paDecZoomFactor = new KHTMLZoomFactorAction(this, false, "format-font-size-less", i18n("Shrink Font"), this);
0389         actionCollection()->addAction("decFontSizes", d->m_paDecZoomFactor);
0390         connect(d->m_paDecZoomFactor, SIGNAL(triggered(bool)), SLOT(slotDecFontSizeFast()));
0391         d->m_paDecZoomFactor->setWhatsThis(i18n("<qt>Shrink Font<br /><br />"
0392                                                 "Make the font in this window smaller. "
0393                                                 "Click and hold down the mouse button for a menu with all available font sizes.</qt>"));
0394         if (!parentPart()) {
0395             // For framesets, this action also affects frames, so only
0396             // the frameset needs to define a shortcut for the action.
0397 
0398             // TODO: Why also CTRL+=?  Because of http://trolltech.com/developer/knowledgebase/524/?
0399             // Nobody else does it...
0400             actionCollection()->setDefaultShortcut(d->m_paIncZoomFactor, QKeySequence("CTRL++; CTRL+="));
0401             actionCollection()->setDefaultShortcut(d->m_paDecZoomFactor, QKeySequence(Qt::CTRL | Qt::Key_Minus));
0402         }
0403     }
0404 
0405     d->m_paFind = actionCollection()->addAction(KStandardAction::Find, "find", this, SLOT(slotFind()));
0406     d->m_paFind->setWhatsThis(i18n("<qt>Find text<br /><br />"
0407                                    "Shows a dialog that allows you to find text on the displayed page.</qt>"));
0408 
0409     d->m_paFindNext = actionCollection()->addAction(KStandardAction::FindNext, "findNext", this, SLOT(slotFindNext()));
0410     d->m_paFindNext->setWhatsThis(i18n("<qt>Find next<br /><br />"
0411                                        "Find the next occurrence of the text that you "
0412                                        "have found using the <b>Find Text</b> function.</qt>"));
0413 
0414     d->m_paFindPrev = actionCollection()->addAction(KStandardAction::FindPrev, "findPrevious",
0415                       this, SLOT(slotFindPrev()));
0416     d->m_paFindPrev->setWhatsThis(i18n("<qt>Find previous<br /><br />"
0417                                        "Find the previous occurrence of the text that you "
0418                                        "have found using the <b>Find Text</b> function.</qt>"));
0419 
0420     // These two actions aren't visible in the menus, but exist for the (configurable) shortcut
0421     d->m_paFindAheadText = new QAction(i18n("Find Text as You Type"), this);
0422     actionCollection()->addAction("findAheadText", d->m_paFindAheadText);
0423     actionCollection()->setDefaultShortcut(d->m_paFindAheadText, QKeySequence("/"));
0424     d->m_paFindAheadText->setToolTip(i18n("This shortcut shows the find bar, for finding text in the displayed page. It cancels the effect of \"Find Links as You Type\", which sets the \"Find links only\" option."));
0425     d->m_paFindAheadText->setStatusTip(d->m_paFindAheadText->toolTip());
0426     connect(d->m_paFindAheadText, SIGNAL(triggered(bool)), this, SLOT(slotFindAheadText()));
0427 
0428     d->m_paFindAheadLinks = new QAction(i18n("Find Links as You Type"), this);
0429     actionCollection()->addAction("findAheadLink", d->m_paFindAheadLinks);
0430     // The issue is that it sets the (sticky) option FindLinksOnly, so
0431     // if you trigger this shortcut once by mistake, Esc and Ctrl+F will still have the option set.
0432     // Better let advanced users configure a shortcut for this advanced option
0433     //d->m_paFindAheadLinks->setShortcut( QKeySequence("\'") );
0434     d->m_paFindAheadLinks->setToolTip(i18n("This shortcut shows the find bar, and sets the option \"Find links only\"."));
0435     d->m_paFindAheadLinks->setStatusTip(d->m_paFindAheadLinks->toolTip());
0436     connect(d->m_paFindAheadLinks, SIGNAL(triggered(bool)), this, SLOT(slotFindAheadLink()));
0437 
0438     if (parentPart()) {
0439         d->m_paFind->setShortcuts(QList<QKeySequence>());   // avoid clashes
0440         d->m_paFindNext->setShortcuts(QList<QKeySequence>());   // avoid clashes
0441         d->m_paFindPrev->setShortcuts(QList<QKeySequence>());   // avoid clashes
0442         d->m_paFindAheadText->setShortcuts(QList<QKeySequence>());
0443         d->m_paFindAheadLinks->setShortcuts(QList<QKeySequence>());
0444     }
0445 
0446     d->m_paPrintFrame = new QAction(i18n("Print Frame..."), this);
0447     actionCollection()->addAction("printFrame", d->m_paPrintFrame);
0448     d->m_paPrintFrame->setIcon(QIcon::fromTheme("document-print-frame"));
0449     connect(d->m_paPrintFrame, SIGNAL(triggered(bool)), this, SLOT(slotPrintFrame()));
0450     d->m_paPrintFrame->setWhatsThis(i18n("<qt>Print Frame<br /><br />"
0451                                          "Some pages have several frames. To print only a single frame, click "
0452                                          "on it and then use this function.</qt>"));
0453 
0454     // Warning: The name selectAll is used hardcoded by some 3rd parties to remove the
0455     // shortcut for selectAll so they do not get ambigous shortcuts. Renaming it
0456     // will either crash or render useless that workaround. It would be better
0457     // to use the name KStandardAction::name(KStandardAction::SelectAll) but we
0458     // can't for the same reason.
0459     d->m_paSelectAll = actionCollection()->addAction(KStandardAction::SelectAll, "selectAll",
0460                        this, SLOT(slotSelectAll()));
0461     if (parentPart()) { // Only the frameset has the shortcut, but the slot uses the current frame.
0462         d->m_paSelectAll->setShortcuts(QList<QKeySequence>());    // avoid clashes
0463     }
0464 
0465     d->m_paToggleCaretMode = new KToggleAction(i18n("Toggle Caret Mode"), this);
0466     actionCollection()->addAction("caretMode", d->m_paToggleCaretMode);
0467     actionCollection()->setDefaultShortcut(d->m_paToggleCaretMode, QKeySequence(Qt::Key_F7));
0468     connect(d->m_paToggleCaretMode, SIGNAL(triggered(bool)), this, SLOT(slotToggleCaretMode()));
0469     d->m_paToggleCaretMode->setChecked(isCaretMode());
0470     if (parentPart()) {
0471         d->m_paToggleCaretMode->setShortcuts(QList<QKeySequence>());    // avoid clashes
0472     }
0473 
0474     // set the default java(script) flags according to the current host.
0475     d->m_bOpenMiddleClick = d->m_settings->isOpenMiddleClickEnabled();
0476     d->m_bJScriptEnabled = d->m_settings->isJavaScriptEnabled();
0477     setDebugScript(d->m_settings->isJavaScriptDebugEnabled());
0478     d->m_bJavaEnabled = d->m_settings->isJavaEnabled();
0479     d->m_bPluginsEnabled = d->m_settings->isPluginsEnabled();
0480 
0481     // Set the meta-refresh flag...
0482     d->m_metaRefreshEnabled = d->m_settings->isAutoDelayedActionsEnabled();
0483 
0484     KHTMLSettings::KSmoothScrollingMode ssm = d->m_settings->smoothScrolling();
0485     if (ssm == KHTMLSettings::KSmoothScrollingDisabled) {
0486         d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMDisabled);
0487     } else if (ssm == KHTMLSettings::KSmoothScrollingWhenEfficient) {
0488         d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMWhenEfficient);
0489     } else {
0490         d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMEnabled);
0491     }
0492 
0493     if (d->m_bDNSPrefetchIsDefault && !onlyLocalReferences()) {
0494         KHTMLSettings::KDNSPrefetch dpm = d->m_settings->dnsPrefetch();
0495         if (dpm == KHTMLSettings::KDNSPrefetchDisabled) {
0496             d->m_bDNSPrefetch = DNSPrefetchDisabled;
0497         } else if (dpm == KHTMLSettings::KDNSPrefetchOnlyWWWAndSLD) {
0498             d->m_bDNSPrefetch = DNSPrefetchOnlyWWWAndSLD;
0499         } else {
0500             d->m_bDNSPrefetch = DNSPrefetchEnabled;
0501         }
0502     }
0503 
0504     if (!KHTMLPartPrivate::s_dnsInitialised && d->m_bDNSPrefetch != DNSPrefetchDisabled) {
0505         KIO::HostInfo::setCacheSize(sDNSCacheSize);
0506         KIO::HostInfo::setTTL(sDNSTTLSeconds);
0507         KHTMLPartPrivate::s_dnsInitialised = true;
0508     }
0509 
0510     // all shortcuts should only be active, when this part has focus
0511     foreach (QAction *action, actionCollection()->actions()) {
0512         action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0513     }
0514     actionCollection()->associateWidget(view);
0515 
0516     connect(view, SIGNAL(zoomView(int)), SLOT(slotZoomView(int)));
0517 
0518     connect(this, SIGNAL(completed()),
0519             this, SLOT(updateActions()));
0520     connect(this, SIGNAL(completed(bool)),
0521             this, SLOT(updateActions()));
0522     connect(this, SIGNAL(started(KIO::Job*)),
0523             this, SLOT(updateActions()));
0524 
0525     // #### FIXME: the process wide loader is going to signal every part about every loaded object.
0526     //      That's quite inefficient. Should be per-document-tree somehow. Even signaling to
0527     //      child parts that a request from an ancestor has loaded is inefficent..
0528     connect(khtml::Cache::loader(), SIGNAL(requestStarted(khtml::DocLoader*,khtml::CachedObject*)),
0529             this, SLOT(slotLoaderRequestStarted(khtml::DocLoader*,khtml::CachedObject*)));
0530     connect(khtml::Cache::loader(), SIGNAL(requestDone(khtml::DocLoader*,khtml::CachedObject*)),
0531             this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
0532     connect(khtml::Cache::loader(), SIGNAL(requestFailed(khtml::DocLoader*,khtml::CachedObject*)),
0533             this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
0534 
0535     connect(&d->m_progressUpdateTimer, SIGNAL(timeout()), this, SLOT(slotProgressUpdate()));
0536 
0537     findTextBegin(); //reset find variables
0538 
0539     connect(&d->m_redirectionTimer, SIGNAL(timeout()),
0540             this, SLOT(slotRedirect()));
0541 
0542     if (QDBusConnection::sessionBus().isConnected()) {
0543         new KHTMLPartIface(this); // our "adaptor"
0544         for (int i = 1;; ++i)
0545             if (QDBusConnection::sessionBus().registerObject(QString("/KHTML/%1/widget").arg(i), this)) {
0546                 break;
0547             } else if (i == 0xffff) {
0548                 qFatal("Something is very wrong in KHTMLPart!");
0549             }
0550     }
0551 
0552     if (prof == BrowserViewGUI && !parentPart()) {
0553         loadPlugins();
0554     }
0555 }
0556 
0557 KHTMLPart::~KHTMLPart()
0558 {
0559     // qCDebug(KHTML_LOG) << this;
0560     KConfigGroup config(KSharedConfig::openConfig(), "HTML Settings");
0561     config.writeEntry("AutomaticDetectionLanguage", int(d->m_autoDetectLanguage));
0562 
0563     if (d->m_manager) { // the PartManager for this part's children
0564         d->m_manager->removePart(this);
0565     }
0566 
0567     slotWalletClosed();
0568     if (!parentPart()) { // only delete it if the top khtml_part closes
0569         removeJSErrorExtension();
0570     }
0571 
0572     stopAutoScroll();
0573     d->m_redirectionTimer.stop();
0574 
0575     if (!d->m_bComplete) {
0576         closeUrl();
0577     }
0578 
0579     disconnect(khtml::Cache::loader(), SIGNAL(requestStarted(khtml::DocLoader*,khtml::CachedObject*)),
0580                this, SLOT(slotLoaderRequestStarted(khtml::DocLoader*,khtml::CachedObject*)));
0581     disconnect(khtml::Cache::loader(), SIGNAL(requestDone(khtml::DocLoader*,khtml::CachedObject*)),
0582                this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
0583     disconnect(khtml::Cache::loader(), SIGNAL(requestFailed(khtml::DocLoader*,khtml::CachedObject*)),
0584                this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
0585 
0586     clear();
0587     hide();
0588 
0589     if (d->m_view) {
0590         d->m_view->m_part = nullptr;
0591     }
0592 
0593     // Have to delete this here since we forward declare it in khtmlpart_p and
0594     // at least some compilers won't call the destructor in this case.
0595     delete d->m_jsedlg;
0596     d->m_jsedlg = nullptr;
0597 
0598     if (!parentPart()) { // only delete d->m_frame if the top khtml_part closes
0599         delete d->m_frame;
0600     } else if (d->m_frame && d->m_frame->m_run) { // for kids, they may get detached while
0601         d->m_frame->m_run.data()->abort();    //  resolving mimetype; cancel that if needed
0602     }
0603     delete d; d = nullptr;
0604     KHTMLGlobal::deregisterPart(this);
0605 }
0606 
0607 bool KHTMLPart::restoreURL(const QUrl &url)
0608 {
0609     // qCDebug(KHTML_LOG) << url;
0610 
0611     d->m_redirectionTimer.stop();
0612 
0613     /*
0614      * That's not a good idea as it will call closeUrl() on all
0615      * child frames, preventing them from further loading. This
0616      * method gets called from restoreState() in case of a full frameset
0617      * restoral, and restoreState() calls closeUrl() before restoring
0618      * anyway.
0619     // qCDebug(KHTML_LOG) << "closing old URL";
0620     closeUrl();
0621     */
0622 
0623     d->m_bComplete = false;
0624     d->m_bLoadEventEmitted = false;
0625     d->m_workingURL = url;
0626 
0627     // set the java(script) flags according to the current host.
0628     d->m_bJScriptEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaScriptEnabled(url.host());
0629     setDebugScript(KHTMLGlobal::defaultHTMLSettings()->isJavaScriptDebugEnabled());
0630     d->m_bJavaEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaEnabled(url.host());
0631     d->m_bPluginsEnabled = KHTMLGlobal::defaultHTMLSettings()->isPluginsEnabled(url.host());
0632 
0633     setUrl(url);
0634 
0635     d->m_restoreScrollPosition = true;
0636     disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
0637     connect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
0638 
0639     KHTMLPageCache::self()->fetchData(d->m_cacheId, this, SLOT(slotRestoreData(QByteArray)));
0640 
0641     emit started(nullptr);
0642 
0643     return true;
0644 }
0645 
0646 static bool areUrlsForSamePage(const QUrl &url1, const QUrl &url2)
0647 {
0648     QUrl u1 = url1.adjusted(QUrl::StripTrailingSlash);
0649     u1.setFragment(QString());
0650     if (u1.path() == QLatin1String("/")) {
0651         u1.setPath(QString());
0652     }
0653     QUrl u2 = url2.adjusted(QUrl::StripTrailingSlash);
0654     u2.setFragment(QString());
0655     if (u2.path() == QLatin1String("/")) {
0656         u2.setPath(QString());
0657     }
0658     return u1 == u2;
0659 }
0660 
0661 bool KHTMLPartPrivate::isLocalAnchorJump(const QUrl &url)
0662 {
0663     // kio_help actually uses fragments to identify different pages, so
0664     // always reload with it.
0665     if (url.scheme() == QLatin1String("help")) {
0666         return false;
0667     }
0668 
0669     return url.hasFragment() && areUrlsForSamePage(url, q->url());
0670 }
0671 
0672 void KHTMLPartPrivate::executeAnchorJump(const QUrl &url, bool lockHistory)
0673 {
0674     DOM::HashChangeEventImpl *hashChangeEvImpl = nullptr;
0675     const QString &oldRef = q->url().fragment(QUrl::FullyEncoded);
0676     const QString &newRef = url.fragment(QUrl::FullyEncoded);
0677     const bool hashChanged = (oldRef != newRef) || (oldRef.isNull() && newRef.isEmpty());
0678 
0679     if (hashChanged) {
0680         // Note: we want to emit openUrlNotify first thing to make the history capture the old state,
0681         // however do not update history if a lock was explicitly requested, e.g. Location.replace()
0682         if (!lockHistory) {
0683             emit m_extension->openUrlNotify();
0684         }
0685         // Create hashchange event
0686         hashChangeEvImpl = new DOM::HashChangeEventImpl();
0687         hashChangeEvImpl->initHashChangeEvent("hashchange",
0688                                               true, //bubble
0689                                               false, //cancelable
0690                                               q->url().toString(), //oldURL
0691                                               url.toString() //newURL
0692                                              );
0693     }
0694 
0695     if (!q->gotoAnchor(newRef)) { // encoded fragment
0696         q->gotoAnchor(url.fragment(QUrl::FullyDecoded)); // not encoded fragment
0697     }
0698 
0699     q->setUrl(url);
0700     emit m_extension->setLocationBarUrl(url.toDisplayString());
0701 
0702     if (hashChangeEvImpl) {
0703         m_doc->dispatchWindowEvent(hashChangeEvImpl);
0704     }
0705 }
0706 
0707 bool KHTMLPart::openUrl(const QUrl &url)
0708 {
0709     // qCDebug(KHTML_LOG) << this << "opening" << url;
0710 
0711 #ifndef KHTML_NO_WALLET
0712     // Wallet forms are per page, so clear it when loading a different page if we
0713     // are not an iframe (because we store walletforms only on the topmost part).
0714     if (!parentPart()) {
0715         d->m_walletForms.clear();
0716     }
0717 #endif
0718     d->m_redirectionTimer.stop();
0719 
0720     // check to see if this is an "error://" URL. This is caused when an error
0721     // occurs before this part was loaded (e.g. KonqRun), and is passed to
0722     // khtmlpart so that it can display the error.
0723     if (url.scheme() == "error") {
0724         closeUrl();
0725 
0726         if (d->m_bJScriptEnabled) {
0727             d->m_statusBarText[BarOverrideText].clear();
0728             d->m_statusBarText[BarDefaultText].clear();
0729         }
0730 
0731         /**
0732          * The format of the error url is that two variables are passed in the query:
0733          * error = int kio error code, errText = QString error text from kio
0734          * and the URL where the error happened is passed as a sub URL.
0735          */
0736         const QUrl mainURL(url.fragment());
0737         //qCDebug(KHTML_LOG) << "Handling error URL. URL count:" << urls.count();
0738 
0739         if (mainURL.isValid()) {
0740             QString query = url.query(QUrl::FullyDecoded);
0741             QRegularExpression pattern("error=(\\d+)&errText=(.*)");
0742             QRegularExpressionMatch match = pattern.match(query);
0743             int error = match.captured(1).toInt();
0744             // error=0 isn't a valid error code, so 0 means it's missing from the URL
0745             if (error == 0) {
0746                 error = KIO::ERR_UNKNOWN;
0747             }
0748             const QString errorText = match.captured(2);
0749             d->m_workingURL = mainURL;
0750             //qCDebug(KHTML_LOG) << "Emitting fixed URL " << d->m_workingURL;
0751             emit d->m_extension->setLocationBarUrl(d->m_workingURL.toDisplayString());
0752             htmlError(error, errorText, d->m_workingURL);
0753             return true;
0754         }
0755     }
0756 
0757     if (!parentPart()) { // only do it for toplevel part
0758         QString host = url.isLocalFile() ? "localhost" : url.host();
0759         QString userAgent = KProtocolManager::userAgentForHost(host);
0760         if (userAgent != KProtocolManager::userAgentForHost(QString())) {
0761             if (!d->m_statusBarUALabel) {
0762                 d->m_statusBarUALabel = new KUrlLabel(d->m_statusBarExtension->statusBar());
0763                 d->m_statusBarUALabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum));
0764                 d->m_statusBarUALabel->setUseCursor(false);
0765                 d->m_statusBarExtension->addStatusBarItem(d->m_statusBarUALabel, 0, false);
0766                 d->m_statusBarUALabel->setPixmap(SmallIcon("preferences-web-browser-identification"));
0767             }
0768             d->m_statusBarUALabel->setToolTip(i18n("The fake user-agent '%1' is in use.", userAgent));
0769         } else if (d->m_statusBarUALabel) {
0770             d->m_statusBarExtension->removeStatusBarItem(d->m_statusBarUALabel);
0771             delete d->m_statusBarUALabel;
0772             d->m_statusBarUALabel = nullptr;
0773         }
0774     }
0775 
0776     KParts::BrowserArguments browserArgs(d->m_extension->browserArguments());
0777     KParts::OpenUrlArguments args(arguments());
0778 
0779     // in case
0780     // a) we have no frameset (don't test m_frames.count(), iframes get in there)
0781     // b) the url is identical with the currently displayed one (except for the htmlref!)
0782     // c) the url request is not a POST operation and
0783     // d) the caller did not request to reload the page
0784     // e) there was no HTTP redirection meanwhile (testcase: webmin's software/tree.cgi)
0785     // => we don't reload the whole document and
0786     // we just jump to the requested html anchor
0787     bool isFrameSet = false;
0788     if (d->m_doc && d->m_doc->isHTMLDocument()) {
0789         HTMLDocumentImpl *htmlDoc = static_cast<HTMLDocumentImpl *>(d->m_doc);
0790         isFrameSet = htmlDoc->body() && (htmlDoc->body()->id() == ID_FRAMESET);
0791     }
0792 
0793     if (isFrameSet && d->isLocalAnchorJump(url) && browserArgs.softReload) {
0794         QList<khtml::ChildFrame *>::Iterator it = d->m_frames.begin();
0795         const QList<khtml::ChildFrame *>::Iterator end = d->m_frames.end();
0796         for (; it != end; ++it) {
0797             KHTMLPart *const part = qobject_cast<KHTMLPart *>((*it)->m_part.data());
0798             if (part) {
0799                 // We are reloading frames to make them jump into offsets.
0800                 KParts::OpenUrlArguments partargs(part->arguments());
0801                 partargs.setReload(true);
0802                 part->setArguments(partargs);
0803 
0804                 part->openUrl(part->url());
0805             }
0806         }/*next it*/
0807         return true;
0808     }
0809 
0810     if (url.hasFragment() && !isFrameSet) {
0811         bool noReloadForced = !args.reload() && !browserArgs.redirectedRequest() && !browserArgs.doPost();
0812         if (noReloadForced &&  d->isLocalAnchorJump(url)) {
0813             // qCDebug(KHTML_LOG) << "jumping to anchor. m_url = " << url;
0814             setUrl(url);
0815             emit started(nullptr);
0816 
0817             if (!gotoAnchor(url.fragment(QUrl::FullyEncoded))) {
0818                 gotoAnchor(url.fragment(QUrl::FullyDecoded));
0819             }
0820 
0821             d->m_bComplete = true;
0822             if (d->m_doc) {
0823                 d->m_doc->setParsing(false);
0824             }
0825 
0826             // qCDebug(KHTML_LOG) << "completed...";
0827             emit completed();
0828             return true;
0829         }
0830     }
0831 
0832     // Save offset of viewport when page is reloaded to be compliant
0833     // to every other capable browser out there.
0834     if (args.reload()) {
0835         args.setXOffset(d->m_view->contentsX());
0836         args.setYOffset(d->m_view->contentsY());
0837         setArguments(args);
0838     }
0839 
0840     if (!d->m_restored) {
0841         closeUrl();
0842     }
0843 
0844     d->m_restoreScrollPosition = d->m_restored;
0845     disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
0846     connect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
0847 
0848     // Classify the mimetype. Some, like images and plugins are handled
0849     // by wrapping things up in tags, so we want to plain output the HTML,
0850     // and not start the job and all that (since we would want the
0851     // KPart or whatever to load it).
0852     // This is also the only place we need to do this, as it's for
0853     // internal iframe use, not any other clients.
0854     MimeType type = d->classifyMimeType(args.mimeType());
0855 
0856     if (type == MimeImage || type == MimeOther) {
0857         begin(url, args.xOffset(), args.yOffset());
0858         write(QString::fromLatin1("<html><head></head><body>"));
0859         if (type == MimeImage) {
0860             write(QString::fromLatin1("<img "));
0861         } else {
0862             write(QString::fromLatin1("<embed "));
0863         }
0864         write(QString::fromLatin1("src=\""));
0865 
0866         assert(url.toString().indexOf('"') == -1);
0867         write(url.toString());
0868 
0869         write(QString::fromLatin1("\">"));
0870         end();
0871         return true;
0872     }
0873 
0874     // initializing m_url to the new url breaks relative links when opening such a link after this call and _before_ begin() is called (when the first
0875     // data arrives) (Simon)
0876     d->m_workingURL = url;
0877     if (url.scheme().startsWith("http") && !url.host().isEmpty() &&
0878             url.path().isEmpty()) {
0879         d->m_workingURL.setPath("/");
0880         emit d->m_extension->setLocationBarUrl(d->m_workingURL.toDisplayString());
0881     }
0882     setUrl(d->m_workingURL);
0883 
0884     QMap<QString, QString> &metaData = args.metaData();
0885     metaData.insert("main_frame_request", parentPart() == nullptr ? "TRUE" : "FALSE");
0886     metaData.insert("ssl_parent_ip", d->m_ssl_parent_ip);
0887     metaData.insert("ssl_parent_cert", d->m_ssl_parent_cert);
0888     metaData.insert("PropagateHttpHeader", "true");
0889     metaData.insert("ssl_was_in_use", d->m_ssl_in_use ? "TRUE" : "FALSE");
0890     metaData.insert("ssl_activate_warnings", "TRUE");
0891     metaData.insert("cross-domain", toplevelURL().toString());
0892 
0893     if (d->m_restored) {
0894         metaData.insert("referrer", d->m_pageReferrer);
0895         d->m_cachePolicy = KIO::CC_Cache;
0896     } else if (args.reload() && !browserArgs.softReload) {
0897         d->m_cachePolicy = KIO::CC_Reload;
0898     } else {
0899         d->m_cachePolicy = KProtocolManager::cacheControl();
0900     }
0901 
0902     if (browserArgs.doPost() && (url.scheme().startsWith("http"))) {
0903         d->m_job = KIO::http_post(url, browserArgs.postData, KIO::HideProgressInfo);
0904         d->m_job->addMetaData("content-type", browserArgs.contentType());
0905     } else {
0906         d->m_job = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo);
0907         d->m_job->addMetaData("cache", KIO::getCacheControlString(d->m_cachePolicy));
0908     }
0909 
0910     if (widget()) {
0911         KJobWidgets::setWindow(d->m_job, widget()->topLevelWidget());
0912     }
0913     d->m_job->addMetaData(metaData);
0914 
0915     connect(d->m_job, SIGNAL(result(KJob*)),
0916             SLOT(slotFinished(KJob*)));
0917     connect(d->m_job, SIGNAL(data(KIO::Job*,QByteArray)),
0918             SLOT(slotData(KIO::Job*,QByteArray)));
0919     connect(d->m_job, SIGNAL(infoMessage(KJob*,QString,QString)),
0920             SLOT(slotInfoMessage(KJob*,QString)));
0921     connect(d->m_job, SIGNAL(redirection(KIO::Job*,QUrl)),
0922             SLOT(slotRedirection(KIO::Job*,QUrl)));
0923 
0924     d->m_bComplete = false;
0925     d->m_bLoadEventEmitted = false;
0926 
0927     // delete old status bar msg's from kjs (if it _was_ activated on last URL)
0928     if (d->m_bJScriptEnabled) {
0929         d->m_statusBarText[BarOverrideText].clear();
0930         d->m_statusBarText[BarDefaultText].clear();
0931     }
0932 
0933     // set the javascript flags according to the current url
0934     d->m_bJScriptEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaScriptEnabled(url.host());
0935     setDebugScript(KHTMLGlobal::defaultHTMLSettings()->isJavaScriptDebugEnabled());
0936     d->m_bJavaEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaEnabled(url.host());
0937     d->m_bPluginsEnabled = KHTMLGlobal::defaultHTMLSettings()->isPluginsEnabled(url.host());
0938 
0939     connect(d->m_job, SIGNAL(speed(KJob*,ulong)),
0940             this, SLOT(slotJobSpeed(KJob*,ulong)));
0941 
0942     connect(d->m_job, SIGNAL(percent(KJob*,ulong)),
0943             this, SLOT(slotJobPercent(KJob*,ulong)));
0944 
0945     connect(d->m_job, SIGNAL(result(KJob*)),
0946             this, SLOT(slotJobDone(KJob*)));
0947 
0948     d->m_jobspeed = 0;
0949 
0950     // If this was an explicit reload and the user style sheet should be used,
0951     // do a stat to see whether the stylesheet was changed in the meanwhile.
0952     if (args.reload() && !settings()->userStyleSheet().isEmpty()) {
0953         QUrl userStyleSheetUrl(settings()->userStyleSheet());
0954         KIO::StatJob *job = KIO::stat(userStyleSheetUrl, KIO::HideProgressInfo);
0955         connect(job, SIGNAL(result(KJob*)),
0956                 this, SLOT(slotUserSheetStatDone(KJob*)));
0957     }
0958     startingJob(d->m_job);
0959     emit started(nullptr);
0960 
0961     return true;
0962 }
0963 
0964 bool KHTMLPart::closeUrl()
0965 {
0966     if (d->m_job) {
0967         KHTMLPageCache::self()->cancelEntry(d->m_cacheId);
0968         d->m_job->kill();
0969         d->m_job = nullptr;
0970     }
0971 
0972     if (d->m_doc && d->m_doc->isHTMLDocument()) {
0973         HTMLDocumentImpl *hdoc = static_cast<HTMLDocumentImpl *>(d->m_doc);
0974 
0975         if (hdoc->body() && d->m_bLoadEventEmitted) {
0976             hdoc->body()->dispatchWindowEvent(EventImpl::UNLOAD_EVENT, false, false);
0977             if (d->m_doc) {
0978                 d->m_doc->updateRendering();
0979             }
0980             d->m_bLoadEventEmitted = false;
0981         }
0982     }
0983 
0984     d->m_bComplete = true; // to avoid emitting completed() in slotFinishedParsing() (David)
0985     d->m_bLoadEventEmitted = true; // don't want that one either
0986     d->m_cachePolicy = KProtocolManager::cacheControl(); // reset cache policy
0987 
0988     disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
0989 
0990     KHTMLPageCache::self()->cancelFetch(this);
0991     if (d->m_doc && d->m_doc->parsing()) {
0992         // qCDebug(KHTML_LOG) << " was still parsing... calling end ";
0993         slotFinishedParsing();
0994         d->m_doc->setParsing(false);
0995     }
0996 
0997     if (!d->m_workingURL.isEmpty()) {
0998         // Aborted before starting to render
0999         // qCDebug(KHTML_LOG) << "Aborted before starting to render, reverting location bar to " << url();
1000         emit d->m_extension->setLocationBarUrl(url().toDisplayString());
1001     }
1002 
1003     d->m_workingURL = QUrl();
1004 
1005     if (d->m_doc && d->m_doc->docLoader()) {
1006         khtml::Cache::loader()->cancelRequests(d->m_doc->docLoader());
1007     }
1008 
1009     // tell all subframes to stop as well
1010     {
1011         ConstFrameIt it = d->m_frames.constBegin();
1012         const ConstFrameIt end = d->m_frames.constEnd();
1013         for (; it != end; ++it) {
1014             if ((*it)->m_run) {
1015                 (*it)->m_run.data()->abort();
1016             }
1017             if (!(*it)->m_part.isNull()) {
1018                 (*it)->m_part.data()->closeUrl();
1019             }
1020         }
1021     }
1022     // tell all objects to stop as well
1023     {
1024         ConstFrameIt it = d->m_objects.constBegin();
1025         const ConstFrameIt end = d->m_objects.constEnd();
1026         for (; it != end; ++it) {
1027             if (!(*it)->m_part.isNull()) {
1028                 (*it)->m_part.data()->closeUrl();
1029             }
1030         }
1031     }
1032     // Stop any started redirections as well!! (DA)
1033     if (d && d->m_redirectionTimer.isActive()) {
1034         d->m_redirectionTimer.stop();
1035     }
1036 
1037     // null node activated.
1038     emit nodeActivated(Node());
1039 
1040     // make sure before clear() runs, we pop out of a dialog's message loop
1041     if (d->m_view) {
1042         d->m_view->closeChildDialogs();
1043     }
1044 
1045     return true;
1046 }
1047 
1048 DOM::HTMLDocument KHTMLPart::htmlDocument() const
1049 {
1050     if (d->m_doc && d->m_doc->isHTMLDocument()) {
1051         return static_cast<HTMLDocumentImpl *>(d->m_doc);
1052     } else {
1053         return static_cast<HTMLDocumentImpl *>(nullptr);
1054     }
1055 }
1056 
1057 DOM::Document KHTMLPart::document() const
1058 {
1059     return d->m_doc;
1060 }
1061 
1062 QString KHTMLPart::documentSource() const
1063 {
1064     QString sourceStr;
1065     if (!(url().isLocalFile()) && KHTMLPageCache::self()->isComplete(d->m_cacheId)) {
1066         QByteArray sourceArray;
1067         QDataStream dataStream(&sourceArray, QIODevice::WriteOnly);
1068         KHTMLPageCache::self()->saveData(d->m_cacheId, &dataStream);
1069         QTextStream stream(sourceArray, QIODevice::ReadOnly);
1070         stream.setCodec(QTextCodec::codecForName(encoding().toLatin1().constData()));
1071         sourceStr = stream.readAll();
1072     } else {
1073         QTemporaryFile tmpFile;
1074         if (!tmpFile.open()) {
1075             return sourceStr;
1076         }
1077 
1078         KIO::FileCopyJob *job = KIO::file_copy(url(), QUrl::fromLocalFile(tmpFile.fileName()), KIO::Overwrite);
1079         if (job->exec()) {
1080             QTextStream stream(&tmpFile);
1081             stream.setCodec(QTextCodec::codecForName(encoding().toLatin1().constData()));
1082             sourceStr = stream.readAll();
1083         }
1084     }
1085 
1086     return sourceStr;
1087 }
1088 
1089 KParts::BrowserExtension *KHTMLPart::browserExtension() const
1090 {
1091     return d->m_extension;
1092 }
1093 
1094 KParts::BrowserHostExtension *KHTMLPart::browserHostExtension() const
1095 {
1096     return d->m_hostExtension;
1097 }
1098 
1099 KHTMLView *KHTMLPart::view() const
1100 {
1101     return d->m_view;
1102 }
1103 
1104 KHTMLViewBar *KHTMLPart::pTopViewBar() const
1105 {
1106     if (const_cast<KHTMLPart *>(this)->parentPart()) {
1107         return const_cast<KHTMLPart *>(this)->parentPart()->pTopViewBar();
1108     }
1109     return d->m_topViewBar;
1110 }
1111 
1112 KHTMLViewBar *KHTMLPart::pBottomViewBar() const
1113 {
1114     if (const_cast<KHTMLPart *>(this)->parentPart()) {
1115         return const_cast<KHTMLPart *>(this)->parentPart()->pBottomViewBar();
1116     }
1117     return d->m_bottomViewBar;
1118 }
1119 
1120 void KHTMLPart::setStatusMessagesEnabled(bool enable)
1121 {
1122     d->m_statusMessagesEnabled = enable;
1123 }
1124 
1125 KJS::Interpreter *KHTMLPart::jScriptInterpreter()
1126 {
1127     KJSProxy *proxy = jScript();
1128     if (!proxy || proxy->paused()) {
1129         return nullptr;
1130     }
1131 
1132     return proxy->interpreter();
1133 }
1134 
1135 bool KHTMLPart::statusMessagesEnabled() const
1136 {
1137     return d->m_statusMessagesEnabled;
1138 }
1139 
1140 void KHTMLPart::setJScriptEnabled(bool enable)
1141 {
1142     if (!enable && jScriptEnabled() && d->m_frame && d->m_frame->m_jscript) {
1143         d->m_frame->m_jscript->clear();
1144     }
1145     d->m_bJScriptForce = enable;
1146     d->m_bJScriptOverride = true;
1147 }
1148 
1149 bool KHTMLPart::jScriptEnabled() const
1150 {
1151     if (onlyLocalReferences()) {
1152         return false;
1153     }
1154 
1155     if (d->m_bJScriptOverride) {
1156         return d->m_bJScriptForce;
1157     }
1158     return d->m_bJScriptEnabled;
1159 }
1160 
1161 void KHTMLPart::setDNSPrefetch(DNSPrefetch pmode)
1162 {
1163     d->m_bDNSPrefetch = pmode;
1164     d->m_bDNSPrefetchIsDefault = false;
1165 }
1166 
1167 KHTMLPart::DNSPrefetch KHTMLPart::dnsPrefetch() const
1168 {
1169     if (onlyLocalReferences()) {
1170         return DNSPrefetchDisabled;
1171     }
1172     return d->m_bDNSPrefetch;
1173 }
1174 
1175 void KHTMLPart::setMetaRefreshEnabled(bool enable)
1176 {
1177     d->m_metaRefreshEnabled = enable;
1178 }
1179 
1180 bool KHTMLPart::metaRefreshEnabled() const
1181 {
1182     return d->m_metaRefreshEnabled;
1183 }
1184 
1185 KJSProxy *KHTMLPart::jScript()
1186 {
1187     if (!jScriptEnabled()) {
1188         return nullptr;
1189     }
1190 
1191     if (!d->m_frame) {
1192         KHTMLPart *p = parentPart();
1193         if (!p) {
1194             d->m_frame = new khtml::ChildFrame;
1195             d->m_frame->m_part = this;
1196         } else {
1197             ConstFrameIt it = p->d->m_frames.constBegin();
1198             const ConstFrameIt end = p->d->m_frames.constEnd();
1199             for (; it != end; ++it)
1200                 if ((*it)->m_part.data() == this) {
1201                     d->m_frame = *it;
1202                     break;
1203                 }
1204         }
1205         if (!d->m_frame) {
1206             return nullptr;
1207         }
1208     }
1209     if (!d->m_frame->m_jscript) {
1210         d->m_frame->m_jscript = new KJSProxy(d->m_frame);
1211     }
1212     d->m_frame->m_jscript->setDebugEnabled(d->m_bJScriptDebugEnabled);
1213 
1214     return d->m_frame->m_jscript;
1215 }
1216 
1217 QVariant KHTMLPart::crossFrameExecuteScript(const QString &target,  const QString &script)
1218 {
1219     KHTMLPart *destpart = this;
1220 
1221     QString trg = target.toLower();
1222 
1223     if (target == "_top") {
1224         while (destpart->parentPart()) {
1225             destpart = destpart->parentPart();
1226         }
1227     } else if (target == "_parent") {
1228         if (parentPart()) {
1229             destpart = parentPart();
1230         }
1231     } else if (target == "_self" || target == "_blank")  {
1232         // we always allow these
1233     } else {
1234         destpart = findFrame(target);
1235         if (!destpart) {
1236             destpart = this;
1237         }
1238     }
1239 
1240     // easy way out?
1241     if (destpart == this) {
1242         return executeScript(DOM::Node(), script);
1243     }
1244 
1245     // now compare the domains
1246     if (destpart->checkFrameAccess(this)) {
1247         return destpart->executeScript(DOM::Node(), script);
1248     }
1249 
1250     // eww, something went wrong. better execute it in our frame
1251     return executeScript(DOM::Node(), script);
1252 }
1253 
1254 //Enable this to see all JS scripts being executed
1255 //#define KJS_VERBOSE
1256 
1257 KJSErrorDlg *KHTMLPart::jsErrorExtension()
1258 {
1259     if (!d->m_settings->jsErrorsEnabled()) {
1260         return nullptr;
1261     }
1262 
1263     if (parentPart()) {
1264         return parentPart()->jsErrorExtension();
1265     }
1266 
1267     if (!d->m_statusBarJSErrorLabel) {
1268         d->m_statusBarJSErrorLabel = new KUrlLabel(d->m_statusBarExtension->statusBar());
1269         d->m_statusBarJSErrorLabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum));
1270         d->m_statusBarJSErrorLabel->setUseCursor(false);
1271         d->m_statusBarExtension->addStatusBarItem(d->m_statusBarJSErrorLabel, 0, false);
1272         d->m_statusBarJSErrorLabel->setToolTip(i18n("This web page contains coding errors."));
1273         d->m_statusBarJSErrorLabel->setPixmap(SmallIcon("script-error"));
1274         connect(d->m_statusBarJSErrorLabel, SIGNAL(leftClickedUrl()), SLOT(launchJSErrorDialog()));
1275         connect(d->m_statusBarJSErrorLabel, SIGNAL(rightClickedUrl()), SLOT(jsErrorDialogContextMenu()));
1276     }
1277     if (!d->m_jsedlg) {
1278         d->m_jsedlg = new KJSErrorDlg;
1279         d->m_jsedlg->setURL(url().toDisplayString());
1280     }
1281     return d->m_jsedlg;
1282 }
1283 
1284 void KHTMLPart::removeJSErrorExtension()
1285 {
1286     if (parentPart()) {
1287         parentPart()->removeJSErrorExtension();
1288         return;
1289     }
1290     if (d->m_statusBarJSErrorLabel != nullptr) {
1291         d->m_statusBarExtension->removeStatusBarItem(d->m_statusBarJSErrorLabel);
1292         delete d->m_statusBarJSErrorLabel;
1293         d->m_statusBarJSErrorLabel = nullptr;
1294     }
1295     delete d->m_jsedlg;
1296     d->m_jsedlg = nullptr;
1297 }
1298 
1299 void KHTMLPart::disableJSErrorExtension()
1300 {
1301     removeJSErrorExtension();
1302     // These two lines are really kind of hacky, and it sucks to do this inside
1303     // KHTML but I don't know of anything that's reasonably easy as an alternative
1304     // right now.  It makes me wonder if there should be a more clean way to
1305     // contact all running "KHTML" instance as opposed to Konqueror instances too.
1306     d->m_settings->setJSErrorsEnabled(false);
1307     emit configurationChanged();
1308 }
1309 
1310 void KHTMLPart::jsErrorDialogContextMenu()
1311 {
1312     QMenu *m = new QMenu(nullptr);
1313     m->addAction(i18n("&Hide Errors"), this, SLOT(removeJSErrorExtension()));
1314     m->addAction(i18n("&Disable Error Reporting"), this, SLOT(disableJSErrorExtension()));
1315     m->popup(QCursor::pos());
1316 }
1317 
1318 void KHTMLPart::launchJSErrorDialog()
1319 {
1320     KJSErrorDlg *dlg = jsErrorExtension();
1321     if (dlg) {
1322         dlg->show();
1323         dlg->raise();
1324     }
1325 }
1326 
1327 void KHTMLPart::launchJSConfigDialog()
1328 {
1329     QStringList args;
1330     args << "khtml_java_js";
1331     KToolInvocation::kdeinitExec("kcmshell5", args);
1332 }
1333 
1334 QVariant KHTMLPart::executeScript(const QString &filename, int baseLine, const DOM::Node &n, const QString &script)
1335 {
1336 #ifdef KJS_VERBOSE
1337     // The script is now printed by KJS's Parser::parse
1338     qCDebug(KHTML_LOG) << "executeScript: caller='" << objectName() << "' filename=" << filename << " baseLine=" << baseLine /*<< " script=" << script*/;
1339 #endif
1340     KJSProxy *proxy = jScript();
1341 
1342     if (!proxy || proxy->paused()) {
1343         return QVariant();
1344     }
1345 
1346     KJS::Completion comp;
1347     QVariant ret = proxy->evaluate(filename, baseLine, script, n, &comp);
1348 
1349     /*
1350      *  Error handling
1351      */
1352     if (comp.complType() == KJS::Throw && comp.value()) {
1353         KJSErrorDlg *dlg = jsErrorExtension();
1354         if (dlg) {
1355             QString msg = KJS::exceptionToString(
1356                               proxy->interpreter()->globalExec(), comp.value());
1357             dlg->addError(i18n("<qt><b>Error</b>: %1: %2</qt>",
1358                                filename.toHtmlEscaped(), msg.toHtmlEscaped()));
1359         }
1360     }
1361 
1362     // Handle immediate redirects now (e.g. location='foo')
1363     if (!d->m_redirectURL.isEmpty() && d->m_delayRedirect == -1) {
1364         // qCDebug(KHTML_LOG) << "executeScript done, handling immediate redirection NOW";
1365         // Must abort tokenizer, no further script must execute.
1366         khtml::Tokenizer *t = d->m_doc->tokenizer();
1367         if (t) {
1368             t->abort();
1369         }
1370         d->m_redirectionTimer.setSingleShot(true);
1371         d->m_redirectionTimer.start(0);
1372     }
1373 
1374     return ret;
1375 }
1376 
1377 QVariant KHTMLPart::executeScript(const QString &script)
1378 {
1379     return executeScript(DOM::Node(), script);
1380 }
1381 
1382 QVariant KHTMLPart::executeScript(const DOM::Node &n, const QString &script)
1383 {
1384 #ifdef KJS_VERBOSE
1385     qCDebug(KHTML_LOG) << "caller=" << objectName() << "node=" << n.nodeName().string().toLatin1().constData() << "(" << (n.isNull() ? 0 : n.nodeType()) << ") " /* << script */;
1386 #endif
1387     KJSProxy *proxy = jScript();
1388 
1389     if (!proxy || proxy->paused()) {
1390         return QVariant();
1391     }
1392 
1393     ++(d->m_runningScripts);
1394     KJS::Completion comp;
1395     const QVariant ret = proxy->evaluate(QString(), 1, script, n, &comp);
1396     --(d->m_runningScripts);
1397 
1398     /*
1399      *  Error handling
1400      */
1401     if (comp.complType() == KJS::Throw && comp.value()) {
1402         KJSErrorDlg *dlg = jsErrorExtension();
1403         if (dlg) {
1404             QString msg = KJS::exceptionToString(
1405                               proxy->interpreter()->globalExec(), comp.value());
1406             dlg->addError(i18n("<qt><b>Error</b>: node %1: %2</qt>",
1407                                n.nodeName().string(), msg.toHtmlEscaped()));
1408         }
1409     }
1410 
1411     if (!d->m_runningScripts && d->m_doc && !d->m_doc->parsing() && d->m_submitForm) {
1412         submitFormAgain();
1413     }
1414 
1415 #ifdef KJS_VERBOSE
1416     qCDebug(KHTML_LOG) << "done";
1417 #endif
1418     return ret;
1419 }
1420 
1421 void KHTMLPart::setJavaEnabled(bool enable)
1422 {
1423     d->m_bJavaForce = enable;
1424     d->m_bJavaOverride = true;
1425 }
1426 
1427 bool KHTMLPart::javaEnabled() const
1428 {
1429     if (onlyLocalReferences()) {
1430         return false;
1431     }
1432 
1433     if (d->m_bJavaOverride) {
1434         return d->m_bJavaForce;
1435     }
1436     return d->m_bJavaEnabled;
1437 }
1438 
1439 void KHTMLPart::setPluginsEnabled(bool enable)
1440 {
1441     d->m_bPluginsForce = enable;
1442     d->m_bPluginsOverride = true;
1443 }
1444 
1445 bool KHTMLPart::pluginsEnabled() const
1446 {
1447     if (onlyLocalReferences()) {
1448         return false;
1449     }
1450 
1451     if (d->m_bPluginsOverride) {
1452         return d->m_bPluginsForce;
1453     }
1454     return d->m_bPluginsEnabled;
1455 }
1456 
1457 static int s_DOMTreeIndentLevel = 0;
1458 
1459 void KHTMLPart::slotDebugDOMTree()
1460 {
1461     if (d->m_doc) {
1462         qDebug("%s", d->m_doc->toString().string().toLatin1().constData());
1463     }
1464 
1465     // Now print the contents of the frames that contain HTML
1466 
1467     const int indentLevel = s_DOMTreeIndentLevel++;
1468 
1469     ConstFrameIt it = d->m_frames.constBegin();
1470     const ConstFrameIt end = d->m_frames.constEnd();
1471     for (; it != end; ++it)
1472         if (!(*it)->m_part.isNull() && (*it)->m_part.data()->inherits("KHTMLPart")) {
1473             KParts::ReadOnlyPart *const p = (*it)->m_part.data();
1474             // qCDebug(KHTML_LOG) << QString().leftJustified(s_DOMTreeIndentLevel*4,' ') << "FRAME " << p->objectName() << " ";
1475             static_cast<KHTMLPart *>(p)->slotDebugDOMTree();
1476         }
1477     s_DOMTreeIndentLevel = indentLevel;
1478 }
1479 
1480 void KHTMLPart::slotDebugScript()
1481 {
1482     if (jScript()) {
1483         jScript()->showDebugWindow();
1484     }
1485 }
1486 
1487 void KHTMLPart::slotDebugRenderTree()
1488 {
1489 #ifndef NDEBUG
1490     if (d->m_doc) {
1491         d->m_doc->renderer()->printTree();
1492         // dump out the contents of the rendering & DOM trees
1493 //    QString dumps;
1494 //    QTextStream outputStream(&dumps,QIODevice::WriteOnly);
1495 //    d->m_doc->renderer()->layer()->dump( outputStream );
1496 //    qCDebug(KHTML_LOG) << "dump output:" << "\n" + dumps;
1497 //    d->m_doc->renderer()->printLineBoxTree();
1498     }
1499 #endif
1500 }
1501 
1502 void KHTMLPart::slotDebugFrameTree()
1503 {
1504     khtml::ChildFrame::dumpFrameTree(this);
1505 }
1506 
1507 void KHTMLPart::slotStopAnimations()
1508 {
1509     stopAnimations();
1510 }
1511 
1512 void KHTMLPart::setAutoloadImages(bool enable)
1513 {
1514     if (d->m_doc && d->m_doc->docLoader()->autoloadImages() == enable) {
1515         return;
1516     }
1517 
1518     if (d->m_doc) {
1519         d->m_doc->docLoader()->setAutoloadImages(enable);
1520     }
1521 
1522     unplugActionList("loadImages");
1523 
1524     if (enable) {
1525         delete d->m_paLoadImages;
1526         d->m_paLoadImages = nullptr;
1527     } else if (!d->m_paLoadImages) {
1528         d->m_paLoadImages = new QAction(i18n("Display Images on Page"), this);
1529         actionCollection()->addAction("loadImages", d->m_paLoadImages);
1530         d->m_paLoadImages->setIcon(QIcon::fromTheme("image-loading"));
1531         connect(d->m_paLoadImages, SIGNAL(triggered(bool)), this, SLOT(slotLoadImages()));
1532     }
1533 
1534     if (d->m_paLoadImages) {
1535         QList<QAction *> lst;
1536         lst.append(d->m_paLoadImages);
1537         plugActionList("loadImages", lst);
1538     }
1539 }
1540 
1541 bool KHTMLPart::autoloadImages() const
1542 {
1543     if (d->m_doc) {
1544         return d->m_doc->docLoader()->autoloadImages();
1545     }
1546 
1547     return true;
1548 }
1549 
1550 void KHTMLPart::clear()
1551 {
1552     if (d->m_bCleared) {
1553         return;
1554     }
1555 
1556     d->m_bCleared = true;
1557 
1558     d->m_bClearing = true;
1559 
1560     {
1561         ConstFrameIt it = d->m_frames.constBegin();
1562         const ConstFrameIt end = d->m_frames.constEnd();
1563         for (; it != end; ++it) {
1564             // Stop HTMLRun jobs for frames
1565             if ((*it)->m_run) {
1566                 (*it)->m_run.data()->abort();
1567             }
1568         }
1569     }
1570 
1571     {
1572         ConstFrameIt it = d->m_objects.constBegin();
1573         const ConstFrameIt end = d->m_objects.constEnd();
1574         for (; it != end; ++it) {
1575             // Stop HTMLRun jobs for objects
1576             if ((*it)->m_run) {
1577                 (*it)->m_run.data()->abort();
1578             }
1579         }
1580     }
1581 
1582     findTextBegin(); // resets d->m_findNode and d->m_findPos
1583     d->m_mousePressNode = DOM::Node();
1584 
1585     if (d->m_doc) {
1586         if (d->m_doc->attached()) { //the view may have detached it already
1587             d->m_doc->detach();
1588         }
1589     }
1590 
1591     // Moving past doc so that onUnload works.
1592     if (d->m_frame && d->m_frame->m_jscript) {
1593         d->m_frame->m_jscript->clear();
1594     }
1595 
1596     // stopping marquees
1597     if (d->m_doc && d->m_doc->renderer() && d->m_doc->renderer()->layer()) {
1598         d->m_doc->renderer()->layer()->suspendMarquees();
1599     }
1600 
1601     if (d->m_view) {
1602         d->m_view->clear();
1603     }
1604 
1605     // do not dereference the document before the jscript and view are cleared, as some destructors
1606     // might still try to access the document.
1607     if (d->m_doc) {
1608         d->m_doc->deref();
1609     }
1610     d->m_doc = nullptr;
1611 
1612     delete d->m_decoder;
1613     d->m_decoder = nullptr;
1614 
1615     // We don't want to change between parts if we are going to delete all of them anyway
1616     if (partManager()) {
1617         disconnect(partManager(), SIGNAL(activePartChanged(KParts::Part*)),
1618                    this, SLOT(slotActiveFrameChanged(KParts::Part*)));
1619     }
1620 
1621     if (d->m_frames.count()) {
1622         const KHTMLFrameList frames = d->m_frames;
1623         d->m_frames.clear();
1624         ConstFrameIt it = frames.begin();
1625         const ConstFrameIt end = frames.end();
1626         for (; it != end; ++it) {
1627             if ((*it)->m_part) {
1628                 partManager()->removePart((*it)->m_part.data());
1629                 delete(*it)->m_part.data();
1630             }
1631             delete *it;
1632         }
1633     }
1634     d->m_suppressedPopupOriginParts.clear();
1635 
1636     if (d->m_objects.count()) {
1637         KHTMLFrameList objects = d->m_objects;
1638         d->m_objects.clear();
1639         ConstFrameIt oi = objects.constBegin();
1640         const ConstFrameIt oiEnd = objects.constEnd();
1641 
1642         for (; oi != oiEnd; ++oi) {
1643             delete(*oi)->m_part.data();
1644             delete *oi;
1645         }
1646     }
1647 
1648     // Listen to part changes again
1649     if (partManager()) {
1650         connect(partManager(), SIGNAL(activePartChanged(KParts::Part*)),
1651                 this, SLOT(slotActiveFrameChanged(KParts::Part*)));
1652     }
1653 
1654     d->clearRedirection();
1655     d->m_redirectLockHistory = true;
1656     d->m_bClearing = false;
1657     d->m_frameNameId = 1;
1658     d->m_bFirstData = true;
1659 
1660     d->m_bMousePressed = false;
1661 
1662     if (d->editor_context.m_caretBlinkTimer >= 0) {
1663         killTimer(d->editor_context.m_caretBlinkTimer);
1664     }
1665     d->editor_context.reset();
1666 #ifndef QT_NO_CLIPBOARD
1667     connect(qApp->clipboard(), SIGNAL(selectionChanged()), SLOT(slotClearSelection()));
1668 #endif
1669 
1670     d->m_jobPercent = 0;
1671 
1672     if (!d->m_haveEncoding) {
1673         d->m_encoding.clear();
1674     }
1675 
1676     d->m_DNSPrefetchQueue.clear();
1677     if (d->m_DNSPrefetchTimer > 0) {
1678         killTimer(d->m_DNSPrefetchTimer);
1679     }
1680     d->m_DNSPrefetchTimer = -1;
1681     d->m_lookedupHosts.clear();
1682     if (d->m_DNSTTLTimer > 0) {
1683         killTimer(d->m_DNSTTLTimer);
1684     }
1685     d->m_DNSTTLTimer = -1;
1686     d->m_numDNSPrefetchedNames = 0;
1687 
1688 #ifdef SPEED_DEBUG
1689     d->m_parsetime.restart();
1690 #endif
1691 }
1692 
1693 bool KHTMLPart::openFile()
1694 {
1695     return true;
1696 }
1697 
1698 DOM::HTMLDocumentImpl *KHTMLPart::docImpl() const
1699 {
1700     if (d && d->m_doc && d->m_doc->isHTMLDocument()) {
1701         return static_cast<HTMLDocumentImpl *>(d->m_doc);
1702     }
1703     return nullptr;
1704 }
1705 
1706 DOM::DocumentImpl *KHTMLPart::xmlDocImpl() const
1707 {
1708     if (d) {
1709         return d->m_doc;
1710     }
1711     return nullptr;
1712 }
1713 
1714 void KHTMLPart::slotInfoMessage(KJob *kio_job, const QString &msg)
1715 {
1716     assert(d->m_job == kio_job);
1717     Q_ASSERT(kio_job);
1718     Q_UNUSED(kio_job);
1719 
1720     if (!parentPart()) {
1721         setStatusBarText(msg, BarDefaultText);
1722     }
1723 }
1724 
1725 void KHTMLPart::setPageSecurity(PageSecurity sec)
1726 {
1727     emit d->m_extension->setPageSecurity(sec);
1728 }
1729 
1730 void KHTMLPart::slotData(KIO::Job *kio_job, const QByteArray &data)
1731 {
1732     assert(d->m_job == kio_job);
1733     Q_ASSERT(kio_job);
1734     Q_UNUSED(kio_job);
1735 
1736     //qCDebug(KHTML_LOG) << "slotData: " << data.size();
1737     // The first data ?
1738     if (!d->m_workingURL.isEmpty()) {
1739         //qCDebug(KHTML_LOG) << "begin!";
1740 
1741         // We must suspend KIO while we're inside begin() because it can cause
1742         // crashes if a window (such as kjsdebugger) goes back into the event loop,
1743         // more data arrives, and begin() gets called again (re-entered).
1744         d->m_job->suspend();
1745         begin(d->m_workingURL, arguments().xOffset(), arguments().yOffset());
1746         d->m_job->resume();
1747 
1748         // CC_Refresh means : always send the server an If-Modified-Since conditional request.
1749         //                    This is the default cache setting and correspond to the KCM's "Keep cache in sync".
1750         // CC_Verify means :  only send a conditional request if the cache expiry date is passed.
1751         //                    It doesn't have a KCM setter.
1752         // We override the first to the second, except when doing a soft-reload.
1753         if (d->m_cachePolicy == KIO::CC_Refresh && !d->m_extension->browserArguments().softReload) {
1754             d->m_doc->docLoader()->setCachePolicy(KIO::CC_Verify);
1755         } else {
1756             d->m_doc->docLoader()->setCachePolicy(d->m_cachePolicy);
1757         }
1758 
1759         d->m_workingURL = QUrl();
1760 
1761         d->m_cacheId = KHTMLPageCache::self()->createCacheEntry();
1762 
1763         // When the first data arrives, the metadata has just been made available
1764         d->m_httpHeaders = d->m_job->queryMetaData("HTTP-Headers");
1765         QDateTime cacheCreationDate =  QDateTime::fromTime_t(d->m_job->queryMetaData("cache-creation-date").toLong());
1766         d->m_doc->docLoader()->setCacheCreationDate(cacheCreationDate);
1767 
1768         d->m_pageServices = d->m_job->queryMetaData("PageServices");
1769         d->m_pageReferrer = d->m_job->queryMetaData("referrer");
1770         d->m_ssl_in_use = (d->m_job->queryMetaData("ssl_in_use") == "TRUE");
1771 
1772         {
1773             KHTMLPart *p = parentPart();
1774             if (p && p->d->m_ssl_in_use != d->m_ssl_in_use) {
1775                 while (p->parentPart()) {
1776                     p = p->parentPart();
1777                 }
1778 
1779                 p->setPageSecurity(NotCrypted);
1780             }
1781         }
1782 
1783         setPageSecurity(d->m_ssl_in_use ? Encrypted : NotCrypted);
1784 
1785         // Shouldn't all of this be done only if ssl_in_use == true ? (DF)
1786         d->m_ssl_parent_ip = d->m_job->queryMetaData("ssl_parent_ip");
1787         d->m_ssl_parent_cert = d->m_job->queryMetaData("ssl_parent_cert");
1788         d->m_ssl_peer_chain = d->m_job->queryMetaData("ssl_peer_chain");
1789         d->m_ssl_peer_ip = d->m_job->queryMetaData("ssl_peer_ip");
1790         d->m_ssl_cipher = d->m_job->queryMetaData("ssl_cipher");
1791         d->m_ssl_protocol_version = d->m_job->queryMetaData("ssl_protocol_version");
1792         d->m_ssl_cipher_used_bits = d->m_job->queryMetaData("ssl_cipher_used_bits");
1793         d->m_ssl_cipher_bits = d->m_job->queryMetaData("ssl_cipher_bits");
1794         d->m_ssl_cert_errors = d->m_job->queryMetaData("ssl_cert_errors");
1795 
1796         // Check for charset meta-data
1797         QString qData = d->m_job->queryMetaData("charset");
1798         if (!qData.isEmpty() && !d->m_haveEncoding) { // only use information if the user didn't override the settings
1799             d->m_encoding = qData;
1800         }
1801 
1802         // Support for http-refresh
1803         qData = d->m_job->queryMetaData("http-refresh");
1804         if (!qData.isEmpty()) {
1805             d->m_doc->processHttpEquiv("refresh", qData);
1806         }
1807 
1808         // DISABLED: Support Content-Location per section 14.14 of RFC 2616.
1809         // See BR# 51185,BR# 82747
1810         /*
1811         QString baseURL = d->m_job->queryMetaData ("content-location");
1812         if (!baseURL.isEmpty())
1813           d->m_doc->setBaseURL(QUrl( d->m_doc->completeURL(baseURL) ));
1814         */
1815 
1816         // Support for Content-Language
1817         QString language = d->m_job->queryMetaData("content-language");
1818         if (!language.isEmpty()) {
1819             d->m_doc->setContentLanguage(language);
1820         }
1821 
1822         if (!url().isLocalFile()) {
1823             // Support for http last-modified
1824             d->m_lastModified = d->m_job->queryMetaData("modified");
1825         } else {
1826             d->m_lastModified.clear();    // done on-demand by lastModified()
1827         }
1828     }
1829 
1830     KHTMLPageCache::self()->addData(d->m_cacheId, data);
1831     write(data.data(), data.size());
1832 }
1833 
1834 void KHTMLPart::slotRestoreData(const QByteArray &data)
1835 {
1836     // The first data ?
1837     if (!d->m_workingURL.isEmpty()) {
1838         long saveCacheId = d->m_cacheId;
1839         QString savePageReferrer = d->m_pageReferrer;
1840         QString saveEncoding     = d->m_encoding;
1841         begin(d->m_workingURL, arguments().xOffset(), arguments().yOffset());
1842         d->m_encoding     = saveEncoding;
1843         d->m_pageReferrer = savePageReferrer;
1844         d->m_cacheId = saveCacheId;
1845         d->m_workingURL = QUrl();
1846     }
1847 
1848     //qCDebug(KHTML_LOG) << data.size();
1849     write(data.data(), data.size());
1850 
1851     if (data.size() == 0) {
1852         //qCDebug(KHTML_LOG) << "<<end of data>>";
1853         // End of data.
1854         if (d->m_doc && d->m_doc->parsing()) {
1855             end();    //will emit completed()
1856         }
1857     }
1858 }
1859 
1860 void KHTMLPart::showError(KJob *job)
1861 {
1862     // qCDebug(KHTML_LOG) << "d->m_bParsing=" << (d->m_doc && d->m_doc->parsing()) << " d->m_bComplete=" << d->m_bComplete
1863     //              << " d->m_bCleared=" << d->m_bCleared;
1864 
1865     if (job->error() == KIO::ERR_NO_CONTENT) {
1866         return;
1867     }
1868 
1869     if ((d->m_doc && d->m_doc->parsing()) || d->m_workingURL.isEmpty()) { // if we got any data already
1870         job->uiDelegate()->showErrorMessage();
1871     } else {
1872         htmlError(job->error(), job->errorText(), d->m_workingURL);
1873     }
1874 }
1875 
1876 // This is a protected method, placed here because of it's relevance to showError
1877 void KHTMLPart::htmlError(int errorCode, const QString &text, const QUrl &reqUrl)
1878 {
1879     // qCDebug(KHTML_LOG) << "errorCode" << errorCode << "text" << text;
1880     // make sure we're not executing any embedded JS
1881     bool bJSFO = d->m_bJScriptForce;
1882     bool bJSOO = d->m_bJScriptOverride;
1883     d->m_bJScriptForce = false;
1884     d->m_bJScriptOverride = true;
1885     begin();
1886 
1887     QString errorName, techName, description;
1888     QStringList causes, solutions;
1889 
1890     QByteArray raw = KIO::rawErrorDetail(errorCode, text, &reqUrl);
1891     QDataStream stream(raw);
1892 
1893     stream >> errorName >> techName >> description >> causes >> solutions;
1894 
1895     QString url, protocol, datetime;
1896 
1897     // This is somewhat confusing, but we have to escape the externally-
1898     // controlled URL twice: once for i18n, and once for HTML.
1899     url = reqUrl.toDisplayString().toHtmlEscaped().toHtmlEscaped();
1900     protocol = reqUrl.scheme();
1901     datetime = QDateTime::currentDateTime().toString(Qt::DefaultLocaleLongDate);
1902 
1903     QString filename(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kf5/khtml/error.html"));
1904     QFile file(filename);
1905     bool isOpened = file.open(QIODevice::ReadOnly);
1906     if (!isOpened) {
1907         qCWarning(KHTML_LOG) << "Could not open error html template:" << filename;
1908     }
1909 
1910     QString html = QString(QLatin1String(file.readAll()));
1911 
1912     html.replace(QLatin1String("TITLE"), i18n("Error: %1 - %2", errorName, url));
1913     html.replace(QLatin1String("DIRECTION"), QApplication::isRightToLeft() ? "rtl" : "ltr");
1914     html.replace(QLatin1String("ICON_PATH"), QUrl::fromLocalFile(KIconLoader::global()->iconPath("dialog-warning", -KIconLoader::SizeHuge)).url());
1915 
1916     QString doc = QLatin1String("<h1>");
1917     doc += i18n("The requested operation could not be completed");
1918     doc += QLatin1String("</h1><h2>");
1919     doc += errorName;
1920     doc += QLatin1String("</h2>");
1921     if (!techName.isNull()) {
1922         doc += QLatin1String("<h2>");
1923         doc += i18n("Technical Reason: ");
1924         doc += techName;
1925         doc += QLatin1String("</h2>");
1926     }
1927     doc += QLatin1String("<br clear=\"all\">");
1928     doc += QLatin1String("<h3>");
1929     doc += i18n("Details of the Request:");
1930     doc += QLatin1String("</h3><ul><li>");
1931     doc += i18n("URL: %1",  url);
1932     doc += QLatin1String("</li><li>");
1933     if (!protocol.isNull()) {
1934         doc += i18n("Protocol: %1", protocol);
1935         doc += QLatin1String("</li><li>");
1936     }
1937     doc += i18n("Date and Time: %1",  datetime);
1938     doc += QLatin1String("</li><li>");
1939     doc += i18n("Additional Information: %1",  text);
1940     doc += QLatin1String("</li></ul><h3>");
1941     doc += i18n("Description:");
1942     doc += QLatin1String("</h3><p>");
1943     doc += description;
1944     doc += QLatin1String("</p>");
1945     if (causes.count()) {
1946         doc += QLatin1String("<h3>");
1947         doc += i18n("Possible Causes:");
1948         doc += QLatin1String("</h3><ul><li>");
1949         doc += causes.join("</li><li>");
1950         doc += QLatin1String("</li></ul>");
1951     }
1952     if (solutions.count()) {
1953         doc += QLatin1String("<h3>");
1954         doc += i18n("Possible Solutions:");
1955         doc += QLatin1String("</h3><ul><li>");
1956         doc += solutions.join("</li><li>");
1957         doc += QLatin1String("</li></ul>");
1958     }
1959 
1960     html.replace(QLatin1String("TEXT"), doc);
1961 
1962     write(html);
1963     end();
1964 
1965     d->m_bJScriptForce = bJSFO;
1966     d->m_bJScriptOverride = bJSOO;
1967 
1968     // make the working url the current url, so that reload works and
1969     // emit the progress signals to advance one step in the history
1970     // (so that 'back' works)
1971     setUrl(reqUrl); // same as d->m_workingURL
1972     d->m_workingURL = QUrl();
1973     emit started(nullptr);
1974     emit completed();
1975 }
1976 
1977 void KHTMLPart::slotFinished(KJob *job)
1978 {
1979     d->m_job = nullptr;
1980     d->m_jobspeed = 0L;
1981 
1982     if (job->error()) {
1983         KHTMLPageCache::self()->cancelEntry(d->m_cacheId);
1984 
1985         // The following catches errors that occur as a result of HTTP
1986         // to FTP redirections where the FTP URL is a directory. Since
1987         // KIO cannot change a redirection request from GET to LISTDIR,
1988         // we have to take care of it here once we know for sure it is
1989         // a directory...
1990         if (job->error() == KIO::ERR_IS_DIRECTORY) {
1991             emit canceled(job->errorString());
1992             emit d->m_extension->openUrlRequest(d->m_workingURL);
1993         } else {
1994             emit canceled(job->errorString());
1995             // TODO: what else ?
1996             checkCompleted();
1997             showError(job);
1998         }
1999 
2000         return;
2001     }
2002     KIO::TransferJob *tjob = ::qobject_cast<KIO::TransferJob *>(job);
2003     if (tjob && tjob->isErrorPage()) {
2004         HTMLPartContainerElementImpl *elt = d->m_frame ?
2005                                             d->m_frame->m_partContainerElement.data() : nullptr;
2006 
2007         if (!elt) {
2008             return;
2009         }
2010 
2011         elt->partLoadingErrorNotify();
2012         checkCompleted();
2013         if (d->m_bComplete) {
2014             return;
2015         }
2016     }
2017 
2018     //qCDebug(KHTML_LOG) << "slotFinished";
2019 
2020     KHTMLPageCache::self()->endData(d->m_cacheId);
2021 
2022     if (d->m_doc && d->m_doc->docLoader()->expireDate().isValid() && url().scheme().startsWith("http")) {
2023         KIO::http_update_cache(url(), false, d->m_doc->docLoader()->expireDate());
2024     }
2025 
2026     d->m_workingURL = QUrl();
2027 
2028     if (d->m_doc && d->m_doc->parsing()) {
2029         end();    //will emit completed()
2030     }
2031 }
2032 
2033 MimeType KHTMLPartPrivate::classifyMimeType(const QString &mimeStr)
2034 {
2035     // See HTML5's "5.5.1 Navigating across documents" section.
2036     if (mimeStr == "application/xhtml+xml") {
2037         return MimeXHTML;
2038     }
2039     if (mimeStr == "image/svg+xml") {
2040         return MimeSVG;
2041     }
2042     if (mimeStr == "text/html" || mimeStr.isEmpty()) {
2043         return MimeHTML;
2044     }
2045 
2046     QMimeDatabase db;
2047     QMimeType mime = db.mimeTypeForName(mimeStr);
2048     if (mime.inherits("text/xml") || mimeStr.endsWith("+xml")) {
2049         return MimeXML;
2050     }
2051 
2052     if (mime.inherits("text/plain")) {
2053         return MimeText;
2054     }
2055 
2056     if (khtmlImLoad::ImageManager::loaderDatabase()->supportedMimeTypes().contains(mimeStr)) {
2057         return MimeImage;
2058     }
2059 
2060     // Sometimes our subclasses like to handle custom mimetypes. In that case,
2061     // we want to handle them as HTML. We do that in the following cases:
2062     // 1) We're at top-level, so we were forced to open something
2063     // 2) We're an object --- this again means we were forced to open something,
2064     //    as an iframe-generating-an-embed case would have us as an iframe
2065     if (!q->parentPart() || (m_frame && m_frame->m_type == khtml::ChildFrame::Object)) {
2066         return MimeHTML;
2067     }
2068 
2069     return MimeOther;
2070 }
2071 
2072 void KHTMLPart::begin(const QUrl &url, int xOffset, int yOffset)
2073 {
2074     if (d->m_view->underMouse()) {
2075         QToolTip::hideText();    // in case a previous tooltip is still shown
2076     }
2077 
2078     // No need to show this for a new page until an error is triggered
2079     if (!parentPart()) {
2080         removeJSErrorExtension();
2081         setSuppressedPopupIndicator(false);
2082         d->m_openableSuppressedPopups = 0;
2083         foreach (KHTMLPart *part, d->m_suppressedPopupOriginParts) {
2084             if (part) {
2085                 KJS::Window *w = KJS::Window::retrieveWindow(part);
2086                 if (w) {
2087                     w->forgetSuppressedWindows();
2088                 }
2089             }
2090         }
2091     }
2092 
2093     d->m_bCleared = false;
2094     d->m_cacheId = 0;
2095     d->m_bComplete = false;
2096     d->m_bLoadEventEmitted = false;
2097     clear();
2098     d->m_bCleared = false;
2099 
2100     if (url.isValid()) {
2101         QString urlString = url.toString();
2102         KHTMLGlobal::vLinks()->insert(urlString);
2103         QString urlString2 = url.toDisplayString();
2104         if (urlString != urlString2) {
2105             KHTMLGlobal::vLinks()->insert(urlString2);
2106         }
2107     }
2108 
2109     // ###
2110     //stopParser();
2111 
2112     KParts::OpenUrlArguments args = arguments();
2113     args.setXOffset(xOffset);
2114     args.setYOffset(yOffset);
2115     setArguments(args);
2116 
2117     d->m_pageReferrer.clear();
2118 
2119     d->m_referrer = url.scheme().startsWith("http") ? url.toString() : "";
2120 
2121     setUrl(url);
2122 
2123     // Note: by now, any special mimetype besides plaintext would have been
2124     // handled specially inside openURL, so we handle their cases the same
2125     // as HTML.
2126     MimeType type = d->classifyMimeType(args.mimeType());
2127     switch (type) {
2128     case MimeSVG:
2129         d->m_doc = DOMImplementationImpl::createSVGDocument(d->m_view);
2130         break;
2131     case MimeXML: // any XML derivative, except XHTML or SVG
2132         // ### not sure if XHTML documents served as text/xml should use DocumentImpl or HTMLDocumentImpl
2133         d->m_doc = DOMImplementationImpl::createXMLDocument(d->m_view);
2134         break;
2135     case MimeText:
2136         d->m_doc = new HTMLTextDocumentImpl(d->m_view);
2137         break;
2138     case MimeXHTML:
2139     case MimeHTML:
2140     default:
2141         d->m_doc = DOMImplementationImpl::createHTMLDocument(d->m_view);
2142         // HTML or XHTML? (#86446)
2143         static_cast<HTMLDocumentImpl *>(d->m_doc)->setHTMLRequested(type != MimeXHTML);
2144     }
2145 
2146     d->m_doc->ref();
2147     d->m_doc->setURL(url.toString());
2148     d->m_doc->open();
2149     if (!d->m_doc->attached()) {
2150         d->m_doc->attach();
2151     }
2152     d->m_doc->setBaseURL(QUrl());
2153     d->m_doc->docLoader()->setShowAnimations(KHTMLGlobal::defaultHTMLSettings()->showAnimations());
2154     emit docCreated();
2155 
2156     d->m_paUseStylesheet->setItems(QStringList());
2157     d->m_paUseStylesheet->setEnabled(false);
2158 
2159     setAutoloadImages(KHTMLGlobal::defaultHTMLSettings()->autoLoadImages());
2160     QString userStyleSheet = KHTMLGlobal::defaultHTMLSettings()->userStyleSheet();
2161     if (!userStyleSheet.isEmpty()) {
2162         setUserStyleSheet(QUrl(userStyleSheet));
2163     }
2164 
2165     d->m_doc->setRestoreState(d->m_extension->browserArguments().docState);
2166     connect(d->m_doc, SIGNAL(finishedParsing()), this, SLOT(slotFinishedParsing()));
2167 
2168     emit d->m_extension->enableAction("print", true);
2169 
2170     d->m_doc->setParsing(true);
2171 }
2172 
2173 void KHTMLPart::write(const char *data, int len)
2174 {
2175     if (!d->m_decoder) {
2176         d->m_decoder = createDecoder();
2177     }
2178 
2179     if (len == -1) {
2180         len = strlen(data);
2181     }
2182 
2183     if (len == 0) {
2184         return;
2185     }
2186 
2187     QString decoded = d->m_decoder->decodeWithBuffering(data, len);
2188 
2189     if (decoded.isEmpty()) {
2190         return;
2191     }
2192 
2193     if (d->m_bFirstData) {
2194         onFirstData();
2195     }
2196 
2197     khtml::Tokenizer *t = d->m_doc->tokenizer();
2198     if (t) {
2199         t->write(decoded, true);
2200     }
2201 }
2202 
2203 // ### KDE5: remove
2204 void KHTMLPart::setAlwaysHonourDoctype(bool b)
2205 {
2206     d->m_bStrictModeQuirk = !b;
2207 }
2208 
2209 void KHTMLPart::write(const QString &str)
2210 {
2211     if (str.isNull()) {
2212         return;
2213     }
2214 
2215     if (d->m_bFirstData) {
2216         // determine the parse mode
2217         if (d->m_bStrictModeQuirk) {
2218             d->m_doc->setParseMode(DocumentImpl::Strict);
2219             d->m_bFirstData = false;
2220         } else {
2221             onFirstData();
2222         }
2223     }
2224     khtml::Tokenizer *t = d->m_doc->tokenizer();
2225     if (t) {
2226         t->write(str, true);
2227     }
2228 }
2229 
2230 void KHTMLPart::end()
2231 {
2232     if (d->m_doc) {
2233         if (d->m_decoder) {
2234             QString decoded = d->m_decoder->flush();
2235             if (d->m_bFirstData) {
2236                 onFirstData();
2237             }
2238             if (!decoded.isEmpty()) {
2239                 write(decoded);
2240             }
2241         }
2242         d->m_doc->finishParsing();
2243     }
2244 }
2245 
2246 void KHTMLPart::onFirstData()
2247 {
2248     assert(d->m_bFirstData);
2249 
2250     // determine the parse mode
2251     d->m_doc->determineParseMode();
2252     d->m_bFirstData = false;
2253 
2254     // ### this is still quite hacky, but should work a lot better than the old solution
2255     // Note: decoder may be null if only write(QString) is used.
2256     if (d->m_decoder && d->m_decoder->visuallyOrdered()) {
2257         d->m_doc->setVisuallyOrdered();
2258     }
2259     // ensure part and view shares zoom-level before styling
2260     updateZoomFactor();
2261     d->m_doc->recalcStyle(NodeImpl::Force);
2262 }
2263 
2264 bool KHTMLPart::doOpenStream(const QString &mimeType)
2265 {
2266     QMimeDatabase db;
2267     QMimeType mime = db.mimeTypeForName(mimeType);
2268     if (mime.inherits("text/html") || mime.inherits("text/xml")) {
2269         begin(url());
2270         return true;
2271     }
2272     return false;
2273 }
2274 
2275 bool KHTMLPart::doWriteStream(const QByteArray &data)
2276 {
2277     write(data.data(), data.size());
2278     return true;
2279 }
2280 
2281 bool KHTMLPart::doCloseStream()
2282 {
2283     end();
2284     return true;
2285 }
2286 
2287 void KHTMLPart::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
2288 {
2289     if (!d->m_view) {
2290         return;
2291     }
2292     d->m_view->paint(p, rc, yOff, more);
2293 }
2294 
2295 void KHTMLPart::stopAnimations()
2296 {
2297     if (d->m_doc) {
2298         d->m_doc->docLoader()->setShowAnimations(KHTMLSettings::KAnimationDisabled);
2299     }
2300 
2301     ConstFrameIt it = d->m_frames.constBegin();
2302     const ConstFrameIt end = d->m_frames.constEnd();
2303     for (; it != end; ++it) {
2304         if (KHTMLPart *p = qobject_cast<KHTMLPart *>((*it)->m_part.data())) {
2305             p->stopAnimations();
2306         }
2307     }
2308 }
2309 
2310 void KHTMLPart::resetFromScript()
2311 {
2312     closeUrl();
2313     d->m_bComplete = false;
2314     d->m_bLoadEventEmitted = false;
2315     disconnect(d->m_doc, SIGNAL(finishedParsing()), this, SLOT(slotFinishedParsing()));
2316     connect(d->m_doc, SIGNAL(finishedParsing()), this, SLOT(slotFinishedParsing()));
2317     d->m_doc->setParsing(true);
2318 
2319     emit started(nullptr);
2320 }
2321 
2322 void KHTMLPart::slotFinishedParsing()
2323 {
2324     d->m_doc->setParsing(false);
2325     d->m_doc->dispatchHTMLEvent(EventImpl::KHTML_CONTENTLOADED_EVENT, true, false);
2326     checkEmitLoadEvent();
2327     disconnect(d->m_doc, SIGNAL(finishedParsing()), this, SLOT(slotFinishedParsing()));
2328 
2329     if (!d->m_view) {
2330         return;    // We are probably being destructed.
2331     }
2332 
2333     checkCompleted();
2334 }
2335 
2336 void KHTMLPart::slotLoaderRequestStarted(khtml::DocLoader *dl, khtml::CachedObject *obj)
2337 {
2338     if (obj && obj->type() == khtml::CachedObject::Image && d->m_doc && d->m_doc->docLoader() == dl) {
2339         KHTMLPart *p = this;
2340         while (p) {
2341             KHTMLPart *const op = p;
2342             ++(p->d->m_totalObjectCount);
2343             p = p->parentPart();
2344             if (!p && op->d->m_loadedObjects <= op->d->m_totalObjectCount
2345                     && !op->d->m_progressUpdateTimer.isActive()) {
2346                 op->d->m_progressUpdateTimer.setSingleShot(true);
2347                 op->d->m_progressUpdateTimer.start(200);
2348             }
2349         }
2350     }
2351 }
2352 
2353 static bool isAncestorOrSamePart(KHTMLPart *p1, KHTMLPart *p2)
2354 {
2355     KHTMLPart *p = p2;
2356     do {
2357         if (p == p1) {
2358             return true;
2359         }
2360     } while ((p = p->parentPart()));
2361     return false;
2362 }
2363 
2364 void KHTMLPart::slotLoaderRequestDone(khtml::DocLoader *dl, khtml::CachedObject *obj)
2365 {
2366     if (obj && obj->type() == khtml::CachedObject::Image && d->m_doc && d->m_doc->docLoader() == dl) {
2367         KHTMLPart *p = this;
2368         while (p) {
2369             KHTMLPart *const op = p;
2370             ++(p->d->m_loadedObjects);
2371             p = p->parentPart();
2372             if (!p && op->d->m_loadedObjects <= op->d->m_totalObjectCount && op->d->m_jobPercent <= 100
2373                     && !op->d->m_progressUpdateTimer.isActive()) {
2374                 op->d->m_progressUpdateTimer.setSingleShot(true);
2375                 op->d->m_progressUpdateTimer.start(200);
2376             }
2377         }
2378     }
2379     /// if we have no document, or the object is not a request of one of our children,
2380     //  then our loading state can't possibly be affected : don't waste time checking for completion.
2381     if (!d->m_doc || !dl->doc()->part() || !isAncestorOrSamePart(this, dl->doc()->part())) {
2382         return;
2383     }
2384     checkCompleted();
2385 }
2386 
2387 void KHTMLPart::slotProgressUpdate()
2388 {
2389     int percent;
2390     if (d->m_loadedObjects < d->m_totalObjectCount) {
2391         percent = d->m_jobPercent / 4 + (d->m_loadedObjects * 300) / (4 * d->m_totalObjectCount);
2392     } else {
2393         percent = d->m_jobPercent;
2394     }
2395 
2396     if (d->m_bComplete) {
2397         percent = 100;
2398     }
2399 
2400     if (d->m_statusMessagesEnabled) {
2401         if (d->m_bComplete) {
2402             emit d->m_extension->infoMessage(i18n("Page loaded."));
2403         } else if (d->m_loadedObjects < d->m_totalObjectCount && percent >= 75) {
2404             emit d->m_extension->infoMessage(i18np("%1 Image of %2 loaded.", "%1 Images of %2 loaded.", d->m_loadedObjects, d->m_totalObjectCount));
2405         }
2406     }
2407 
2408     emit d->m_extension->loadingProgress(percent);
2409 }
2410 
2411 void KHTMLPart::slotJobSpeed(KJob * /*job*/, unsigned long speed)
2412 {
2413     d->m_jobspeed = speed;
2414     if (!parentPart()) {
2415         setStatusBarText(jsStatusBarText(), BarOverrideText);
2416     }
2417 }
2418 
2419 void KHTMLPart::slotJobPercent(KJob * /*job*/, unsigned long percent)
2420 {
2421     d->m_jobPercent = percent;
2422 
2423     if (!parentPart()) {
2424         d->m_progressUpdateTimer.setSingleShot(true);
2425         d->m_progressUpdateTimer.start(0);
2426     }
2427 }
2428 
2429 void KHTMLPart::slotJobDone(KJob * /*job*/)
2430 {
2431     d->m_jobPercent = 100;
2432 
2433     if (!parentPart()) {
2434         d->m_progressUpdateTimer.setSingleShot(true);
2435         d->m_progressUpdateTimer.start(0);
2436     }
2437 }
2438 
2439 void KHTMLPart::slotUserSheetStatDone(KJob *_job)
2440 {
2441     using namespace KIO;
2442 
2443     if (_job->error()) {
2444         showError(_job);
2445         return;
2446     }
2447 
2448     const UDSEntry entry = dynamic_cast<KIO::StatJob *>(_job)->statResult();
2449     const QDateTime lastModified = QDateTime::fromTime_t(entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1));
2450 
2451     // If the filesystem supports modification times, only reload the
2452     // user-defined stylesheet if necessary - otherwise always reload.
2453     if (lastModified.isValid()) {
2454         if (d->m_userStyleSheetLastModified >= lastModified) {
2455             return;
2456         }
2457         d->m_userStyleSheetLastModified = lastModified;
2458     }
2459 
2460     setUserStyleSheet(QUrl(settings()->userStyleSheet()));
2461 }
2462 
2463 bool KHTMLPartPrivate::isFullyLoaded(bool *pendingRedirections) const
2464 {
2465     *pendingRedirections = false;
2466 
2467     // Any frame that hasn't completed yet ?
2468     ConstFrameIt it = m_frames.constBegin();
2469     const ConstFrameIt end = m_frames.constEnd();
2470     for (; it != end; ++it) {
2471         if (!(*it)->m_bCompleted || (*it)->m_run) {
2472             //qCDebug(KHTML_LOG) << this << " is waiting for " << (*it)->m_part;
2473             return false;
2474         }
2475         // Check for frames with pending redirections
2476         if ((*it)->m_bPendingRedirection) {
2477             *pendingRedirections = true;
2478         }
2479     }
2480 
2481     // Any object that hasn't completed yet ?
2482     {
2483         ConstFrameIt oi = m_objects.constBegin();
2484         const ConstFrameIt oiEnd = m_objects.constEnd();
2485 
2486         for (; oi != oiEnd; ++oi)
2487             if (!(*oi)->m_bCompleted) {
2488                 return false;
2489             }
2490     }
2491 
2492     // Are we still parsing
2493     if (m_doc && m_doc->parsing()) {
2494         return false;
2495     }
2496 
2497     // Still waiting for images/scripts from the loader ?
2498     int requests = 0;
2499     if (m_doc && m_doc->docLoader()) {
2500         requests = khtml::Cache::loader()->numRequests(m_doc->docLoader());
2501     }
2502 
2503     if (requests > 0) {
2504         //qCDebug(KHTML_LOG) << "still waiting for images/scripts from the loader - requests:" << requests;
2505         return false;
2506     }
2507 
2508     return true;
2509 }
2510 
2511 void KHTMLPart::checkCompleted()
2512 {
2513 //   qCDebug(KHTML_LOG) << this;
2514 //   qCDebug(KHTML_LOG) << "   parsing: " << (d->m_doc && d->m_doc->parsing());
2515 //   qCDebug(KHTML_LOG) << "   complete: " << d->m_bComplete;
2516 
2517     // restore the cursor position
2518     if (d->m_doc && !d->m_doc->parsing() && !d->m_focusNodeRestored) {
2519         if (d->m_focusNodeNumber >= 0) {
2520             d->m_doc->setFocusNode(d->m_doc->nodeWithAbsIndex(d->m_focusNodeNumber));
2521         }
2522 
2523         d->m_focusNodeRestored = true;
2524     }
2525 
2526     bool fullyLoaded, pendingChildRedirections;
2527     fullyLoaded = d->isFullyLoaded(&pendingChildRedirections);
2528 
2529     // Are we still loading, or already have done the relevant work?
2530     if (!fullyLoaded || d->m_bComplete) {
2531         return;
2532     }
2533 
2534     // OK, completed.
2535     // Now do what should be done when we are really completed.
2536     d->m_bComplete = true;
2537     d->m_cachePolicy = KProtocolManager::cacheControl(); // reset cache policy
2538     d->m_totalObjectCount = 0;
2539     d->m_loadedObjects = 0;
2540 
2541     KHTMLPart *p = this;
2542     while (p) {
2543         KHTMLPart *op = p;
2544         p = p->parentPart();
2545         if (!p && !op->d->m_progressUpdateTimer.isActive()) {
2546             op->d->m_progressUpdateTimer.setSingleShot(true);
2547             op->d->m_progressUpdateTimer.start(0);
2548         }
2549     }
2550 
2551     checkEmitLoadEvent(); // if we didn't do it before
2552 
2553     bool pendingAction = false;
2554 
2555     if (!d->m_redirectURL.isEmpty()) {
2556         // DA: Do not start redirection for frames here! That action is
2557         // deferred until the parent emits a completed signal.
2558         if (parentPart() == nullptr) {
2559             //qCDebug(KHTML_LOG) << this << " starting redirection timer";
2560             d->m_redirectionTimer.setSingleShot(true);
2561             d->m_redirectionTimer.start(qMax(0, 1000 * d->m_delayRedirect));
2562         } else {
2563             //qCDebug(KHTML_LOG) << this << " not toplevel -> not starting redirection timer. Waiting for slotParentCompleted.";
2564         }
2565 
2566         pendingAction = true;
2567     } else if (pendingChildRedirections) {
2568         pendingAction = true;
2569     }
2570 
2571     // the view will emit completed on our behalf,
2572     // either now or at next repaint if one is pending
2573 
2574     //qCDebug(KHTML_LOG) << this << " asks the view to emit completed. pendingAction=" << pendingAction;
2575     d->m_view->complete(pendingAction);
2576 
2577     // find the alternate stylesheets
2578     QStringList sheets;
2579     if (d->m_doc) {
2580         sheets = d->m_doc->availableStyleSheets();
2581     }
2582     sheets.prepend(i18n("Automatic Detection"));
2583     d->m_paUseStylesheet->setItems(sheets);
2584 
2585     d->m_paUseStylesheet->setEnabled(sheets.count() > 2);
2586     if (sheets.count() > 2) {
2587         d->m_paUseStylesheet->setCurrentItem(qMax(sheets.indexOf(d->m_sheetUsed), 0));
2588         slotUseStylesheet();
2589     }
2590 
2591     setJSDefaultStatusBarText(QString());
2592 
2593 #ifdef SPEED_DEBUG
2594     if (!parentPart()) {
2595         qCDebug(KHTML_LOG) << "DONE:" << d->m_parsetime.elapsed();
2596     }
2597 #endif
2598 }
2599 
2600 void KHTMLPart::checkEmitLoadEvent()
2601 {
2602     bool fullyLoaded, pendingChildRedirections;
2603     fullyLoaded = d->isFullyLoaded(&pendingChildRedirections);
2604 
2605     // ### might want to wait on pendingChildRedirections here, too
2606     if (d->m_bLoadEventEmitted || !d->m_doc || !fullyLoaded) {
2607         return;
2608     }
2609 
2610     d->m_bLoadEventEmitted = true;
2611     if (d->m_doc) {
2612         d->m_doc->close();
2613     }
2614 }
2615 
2616 const KHTMLSettings *KHTMLPart::settings() const
2617 {
2618     return d->m_settings;
2619 }
2620 
2621 #ifndef KDE_NO_COMPAT // KDE5: remove this ifndef, keep the method (renamed to baseUrl)
2622 QUrl KHTMLPart::baseURL() const
2623 {
2624     if (!d->m_doc) {
2625         return QUrl();
2626     }
2627 
2628     return d->m_doc->baseURL();
2629 }
2630 #endif
2631 
2632 QUrl KHTMLPart::completeURL(const QString &url)
2633 {
2634     if (!d->m_doc) {
2635         return QUrl(url);
2636     }
2637 
2638 #if 0
2639     if (d->m_decoder) {
2640         return QUrl(d->m_doc->completeURL(url), d->m_decoder->codec()->mibEnum());
2641     }
2642 #endif
2643 
2644     return QUrl(d->m_doc->completeURL(url));
2645 }
2646 
2647 QString KHTMLPartPrivate::codeForJavaScriptURL(const QString &u)
2648 {
2649     return QUrl::fromPercentEncoding(u.right(u.length() - 11).toUtf8());
2650 }
2651 
2652 void KHTMLPartPrivate::executeJavascriptURL(const QString &u)
2653 {
2654     QString script = codeForJavaScriptURL(u);
2655     // qCDebug(KHTML_LOG) << "script=" << script;
2656     QVariant res = q->executeScript(DOM::Node(), script);
2657     if (res.type() == QVariant::String) {
2658         q->begin(q->url());
2659         q->setAlwaysHonourDoctype(); // Disable public API compat; it messes with doctype
2660         q->write(res.toString());
2661         q->end();
2662     }
2663     emit q->completed();
2664 }
2665 
2666 bool KHTMLPartPrivate::isJavaScriptURL(const QString &url)
2667 {
2668     return url.indexOf(QLatin1String("javascript:"), 0, Qt::CaseInsensitive) == 0;
2669 }
2670 
2671 // Called by ecma/kjs_window in case of redirections from Javascript,
2672 // and by xml/dom_docimpl.cpp in case of http-equiv meta refresh.
2673 void KHTMLPart::scheduleRedirection(int delay, const QString &url, bool doLockHistory)
2674 {
2675     // qCDebug(KHTML_LOG) << "delay=" << delay << " url=" << url << " from=" << this->url() << "parent=" << parentPart();
2676     // qCDebug(KHTML_LOG) << "current redirectURL=" << d->m_redirectURL << " with delay " << d->m_delayRedirect;
2677 
2678     // In case of JS redirections, some, such as jump to anchors, and javascript:
2679     // evaluation should actually be handled immediately, and not waiting until
2680     // the end of the script. (Besides, we don't want to abort the tokenizer for those)
2681     if (delay == -1 && d->isInPageURL(url)) {
2682         d->executeInPageURL(url, doLockHistory);
2683         return;
2684     }
2685 
2686     if (delay < 24 * 60 * 60 &&
2687             (d->m_redirectURL.isEmpty() || delay <= d->m_delayRedirect)) {
2688         d->m_delayRedirect = delay;
2689         d->m_redirectURL = url;
2690         d->m_redirectLockHistory = doLockHistory;
2691         // qCDebug(KHTML_LOG) << " d->m_bComplete=" << d->m_bComplete;
2692 
2693         if (d->m_bComplete) {
2694             d->m_redirectionTimer.stop();
2695             d->m_redirectionTimer.setSingleShot(true);
2696             d->m_redirectionTimer.start(qMax(0, 1000 * d->m_delayRedirect));
2697         }
2698     }
2699 }
2700 
2701 void KHTMLPartPrivate::clearRedirection()
2702 {
2703     m_delayRedirect = 0;
2704     m_redirectURL.clear();
2705     m_redirectionTimer.stop();
2706 }
2707 
2708 void KHTMLPart::slotRedirect()
2709 {
2710     // qCDebug(KHTML_LOG) << this;
2711     QString u = d->m_redirectURL;
2712     QUrl url(u);
2713     d->clearRedirection();
2714 
2715     if (d->isInPageURL(u)) {
2716         d->executeInPageURL(u, d->m_redirectLockHistory);
2717         return;
2718     }
2719 
2720     KParts::OpenUrlArguments args;
2721     QUrl cUrl(this->url());
2722 
2723     // handle windows opened by JS
2724     if (openedByJS() && d->m_opener) {
2725         cUrl = d->m_opener->url();
2726     }
2727 
2728     if (!KUrlAuthorized::authorizeUrlAction("redirect", cUrl, url)) {
2729         qCWarning(KHTML_LOG) << "KHTMLPart::scheduleRedirection: Redirection from " << cUrl << " to " << url << " REJECTED!";
2730         emit completed();
2731         return;
2732     }
2733 
2734     if (areUrlsForSamePage(url, this->url())) {
2735         args.metaData().insert("referrer", d->m_pageReferrer);
2736     }
2737 
2738     // For javascript and META-tag based redirections:
2739     //   - We don't take cross-domain-ness in consideration if we are the
2740     //   toplevel frame because the new URL may be in a different domain as the current URL
2741     //   but that's ok.
2742     //   - If we are not the toplevel frame then we check against the toplevelURL()
2743     if (parentPart()) {
2744         args.metaData().insert("cross-domain", toplevelURL().toString());
2745     }
2746 
2747     KParts::BrowserArguments browserArgs;
2748     browserArgs.setLockHistory(d->m_redirectLockHistory);
2749     // _self: make sure we don't use any <base target=>'s
2750 
2751     if (!urlSelected(u, 0, 0, "_self", args, browserArgs)) {
2752         // urlSelected didn't open a url, so emit completed ourselves
2753         emit completed();
2754     }
2755 }
2756 
2757 void KHTMLPart::slotRedirection(KIO::Job *, const QUrl &url)
2758 {
2759     // the worker told us that we got redirected
2760     //qCDebug(KHTML_LOG) << "redirection by KIO to" << url;
2761     emit d->m_extension->setLocationBarUrl(url.toDisplayString());
2762     d->m_workingURL = url;
2763 }
2764 
2765 bool KHTMLPart::setEncoding(const QString &name, bool override)
2766 {
2767     d->m_encoding = name;
2768     d->m_haveEncoding = override;
2769 
2770     if (!url().isEmpty()) {
2771         // reload document
2772         closeUrl();
2773         QUrl oldUrl = url();
2774         setUrl(QUrl());
2775         d->m_restored = true;
2776         openUrl(oldUrl);
2777         d->m_restored = false;
2778     }
2779 
2780     return true;
2781 }
2782 
2783 QString KHTMLPart::encoding() const
2784 {
2785     if (d->m_haveEncoding && !d->m_encoding.isEmpty()) {
2786         return d->m_encoding;
2787     }
2788 
2789     if (d->m_decoder && d->m_decoder->encoding()) {
2790         return QString(d->m_decoder->encoding());
2791     }
2792 
2793     return defaultEncoding();
2794 }
2795 
2796 QString KHTMLPart::defaultEncoding() const
2797 {
2798     QString encoding = settings()->encoding();
2799     if (!encoding.isEmpty()) {
2800         return encoding;
2801     }
2802     // HTTP requires the default encoding to be latin1, when neither
2803     // the user nor the page requested a particular encoding.
2804     if (url().scheme().startsWith("http")) {
2805         return "iso-8859-1";
2806     } else {
2807         return QTextCodec::codecForLocale()->name().toLower();
2808     }
2809 }
2810 
2811 void KHTMLPart::setUserStyleSheet(const QUrl &url)
2812 {
2813     if (d->m_doc && d->m_doc->docLoader()) {
2814         (void) new khtml::PartStyleSheetLoader(this, url.toString(), d->m_doc->docLoader());
2815     }
2816 }
2817 
2818 void KHTMLPart::setUserStyleSheet(const QString &styleSheet)
2819 {
2820     if (d->m_doc) {
2821         d->m_doc->setUserStyleSheet(styleSheet);
2822     }
2823 }
2824 
2825 bool KHTMLPart::gotoAnchor(const QString &name)
2826 {
2827     if (!d->m_doc) {
2828         return false;
2829     }
2830 
2831     HTMLCollectionImpl *anchors = new HTMLCollectionImpl(d->m_doc, HTMLCollectionImpl::DOC_ANCHORS);
2832     anchors->ref();
2833     NodeImpl *n = anchors->namedItem(name);
2834     anchors->deref();
2835 
2836     if (!n) {
2837         n = d->m_doc->getElementById(name);
2838     }
2839 
2840     d->m_doc->setCSSTarget(n); // Setting to null will clear the current target.
2841 
2842     // Implement the rule that "" and "top" both mean top of page.
2843     bool top = !n && (name.isEmpty() || name.toLower() == "top");
2844 
2845     if (top) {
2846         d->m_view->setContentsPos(d->m_view->contentsX(), 0);
2847         return true;
2848     } else if (!n) {
2849         // qCDebug(KHTML_LOG) << name << "not found";
2850         return false;
2851     }
2852 
2853     int x = 0, y = 0;
2854     int gox, dummy;
2855     HTMLElementImpl *a = static_cast<HTMLElementImpl *>(n);
2856 
2857     a->getUpperLeftCorner(x, y);
2858     if (x <= d->m_view->contentsX()) {
2859         gox = x - 10;
2860     } else {
2861         gox = d->m_view->contentsX();
2862         if (x + 10 > d->m_view->contentsX() + d->m_view->visibleWidth()) {
2863             a->getLowerRightCorner(x, dummy);
2864             gox = x - d->m_view->visibleWidth() + 10;
2865         }
2866     }
2867 
2868     d->m_view->setContentsPos(gox, y);
2869 
2870     return true;
2871 }
2872 
2873 bool KHTMLPart::nextAnchor()
2874 {
2875     if (!d->m_doc) {
2876         return false;
2877     }
2878     d->m_view->focusNextPrevNode(true);
2879 
2880     return true;
2881 }
2882 
2883 bool KHTMLPart::prevAnchor()
2884 {
2885     if (!d->m_doc) {
2886         return false;
2887     }
2888     d->m_view->focusNextPrevNode(false);
2889 
2890     return true;
2891 }
2892 
2893 void KHTMLPart::setStandardFont(const QString &name)
2894 {
2895     d->m_settings->setStdFontName(name);
2896 }
2897 
2898 void KHTMLPart::setFixedFont(const QString &name)
2899 {
2900     d->m_settings->setFixedFontName(name);
2901 }
2902 
2903 void KHTMLPart::setURLCursor(const QCursor &c)
2904 {
2905     d->m_linkCursor = c;
2906 }
2907 
2908 QCursor KHTMLPart::urlCursor() const
2909 {
2910     return d->m_linkCursor;
2911 }
2912 
2913 bool KHTMLPart::onlyLocalReferences() const
2914 {
2915     return d->m_onlyLocalReferences;
2916 }
2917 
2918 void KHTMLPart::setOnlyLocalReferences(bool enable)
2919 {
2920     d->m_onlyLocalReferences = enable;
2921 }
2922 
2923 bool KHTMLPart::forcePermitLocalImages() const
2924 {
2925     return d->m_forcePermitLocalImages;
2926 }
2927 
2928 void KHTMLPart::setForcePermitLocalImages(bool enable)
2929 {
2930     d->m_forcePermitLocalImages = enable;
2931 }
2932 
2933 void KHTMLPartPrivate::setFlagRecursively(
2934     bool KHTMLPartPrivate::*flag, bool value)
2935 {
2936     // first set it on the current one
2937     this->*flag = value;
2938 
2939     // descend into child frames recursively
2940     {
2941         QList<khtml::ChildFrame *>::Iterator it = m_frames.begin();
2942         const QList<khtml::ChildFrame *>::Iterator itEnd = m_frames.end();
2943         for (; it != itEnd; ++it) {
2944             KHTMLPart *const part = qobject_cast<KHTMLPart *>((*it)->m_part.data());
2945             if (part) {
2946                 part->d->setFlagRecursively(flag, value);
2947             }
2948         }/*next it*/
2949     }
2950     // do the same again for objects
2951     {
2952         QList<khtml::ChildFrame *>::Iterator it = m_objects.begin();
2953         const QList<khtml::ChildFrame *>::Iterator itEnd = m_objects.end();
2954         for (; it != itEnd; ++it) {
2955             KHTMLPart *const part = qobject_cast<KHTMLPart *>((*it)->m_part.data());
2956             if (part) {
2957                 part->d->setFlagRecursively(flag, value);
2958             }
2959         }/*next it*/
2960     }
2961 }
2962 
2963 void KHTMLPart::initCaret()
2964 {
2965     // initialize caret if not used yet
2966     if (d->editor_context.m_selection.state() == Selection::NONE) {
2967         if (d->m_doc) {
2968             NodeImpl *node;
2969             if (d->m_doc->isHTMLDocument()) {
2970                 HTMLDocumentImpl *htmlDoc = static_cast<HTMLDocumentImpl *>(d->m_doc);
2971                 node = htmlDoc->body();
2972             } else {
2973                 node = d->m_doc;
2974             }
2975             if (!node) {
2976                 return;
2977             }
2978             d->editor_context.m_selection.moveTo(Position(node, 0));
2979             d->editor_context.m_selection.setNeedsLayout();
2980             d->editor_context.m_selection.needsCaretRepaint();
2981         }
2982     }
2983 }
2984 
2985 static void setCaretInvisibleIfNeeded(KHTMLPart *part)
2986 {
2987     // On contenteditable nodes, don't hide the caret
2988     if (!khtml::KHTMLPartAccessor::caret(part).caretPos().node()->isContentEditable()) {
2989         part->setCaretVisible(false);
2990     }
2991 }
2992 
2993 void KHTMLPart::setCaretMode(bool enable)
2994 {
2995     // qCDebug(KHTML_LOG) << enable;
2996     if (isCaretMode() == enable) {
2997         return;
2998     }
2999     d->setFlagRecursively(&KHTMLPartPrivate::m_caretMode, enable);
3000     // FIXME: this won't work on frames as expected
3001     if (!isEditable()) {
3002         if (enable) {
3003             initCaret();
3004             setCaretVisible(true);
3005 //       view()->ensureCaretVisible();
3006         } else {
3007             setCaretInvisibleIfNeeded(this);
3008         }
3009     }
3010 }
3011 
3012 bool KHTMLPart::isCaretMode() const
3013 {
3014     return d->m_caretMode;
3015 }
3016 
3017 void KHTMLPart::setEditable(bool enable)
3018 {
3019     if (isEditable() == enable) {
3020         return;
3021     }
3022     d->setFlagRecursively(&KHTMLPartPrivate::m_designMode, enable);
3023     // FIXME: this won't work on frames as expected
3024     if (!isCaretMode()) {
3025         if (enable) {
3026             initCaret();
3027             setCaretVisible(true);
3028 //       view()->ensureCaretVisible();
3029         } else {
3030             setCaretInvisibleIfNeeded(this);
3031         }
3032     }
3033 }
3034 
3035 bool KHTMLPart::isEditable() const
3036 {
3037     return d->m_designMode;
3038 }
3039 
3040 khtml::EditorContext *KHTMLPart::editorContext() const
3041 {
3042     return &d->editor_context;
3043 }
3044 
3045 void KHTMLPart::setCaretPosition(DOM::Node node, long offset, bool extendSelection)
3046 {
3047     Q_UNUSED(node);
3048     Q_UNUSED(offset);
3049     Q_UNUSED(extendSelection);
3050 #ifndef KHTML_NO_CARET
3051 #if 0
3052     qCDebug(KHTML_LOG) << "node: " << node.handle() << " nodeName: "
3053              << node.nodeName().string() << " offset: " << offset
3054              << " extendSelection " << extendSelection;
3055     if (view()->moveCaretTo(node.handle(), offset, !extendSelection)) {
3056         emitSelectionChanged();
3057     }
3058     view()->ensureCaretVisible();
3059 #endif
3060 #endif // KHTML_NO_CARET
3061 }
3062 
3063 KHTMLPart::CaretDisplayPolicy KHTMLPart::caretDisplayPolicyNonFocused() const
3064 {
3065 #if 0
3066 #ifndef KHTML_NO_CARET
3067     return (CaretDisplayPolicy)view()->caretDisplayPolicyNonFocused();
3068 #else // KHTML_NO_CARET
3069     return CaretInvisible;
3070 #endif // KHTML_NO_CARET
3071 #endif
3072     return CaretInvisible;
3073 }
3074 
3075 void KHTMLPart::setCaretDisplayPolicyNonFocused(CaretDisplayPolicy policy)
3076 {
3077     Q_UNUSED(policy);
3078 #if 0
3079 #ifndef KHTML_NO_CARET
3080     view()->setCaretDisplayPolicyNonFocused(policy);
3081 #endif // KHTML_NO_CARET
3082 #endif
3083 }
3084 
3085 void KHTMLPart::setCaretVisible(bool show)
3086 {
3087     if (show) {
3088         NodeImpl *caretNode = d->editor_context.m_selection.caretPos().node();
3089         if (isCaretMode() || (caretNode && caretNode->isContentEditable())) {
3090             invalidateSelection();
3091             enableFindAheadActions(false);
3092         }
3093     } else {
3094 
3095         if (d->editor_context.m_caretBlinkTimer >= 0) {
3096             killTimer(d->editor_context.m_caretBlinkTimer);
3097         }
3098         clearCaretRectIfNeeded();
3099 
3100     }
3101 }
3102 
3103 void KHTMLPart::findTextBegin()
3104 {
3105     d->m_find.findTextBegin();
3106 }
3107 
3108 bool KHTMLPart::initFindNode(bool selection, bool reverse, bool fromCursor)
3109 {
3110     return d->m_find.initFindNode(selection, reverse, fromCursor);
3111 }
3112 
3113 void KHTMLPart::slotFind()
3114 {
3115     KParts::ReadOnlyPart *part = currentFrame();
3116     if (!part) {
3117         return;
3118     }
3119     if (!part->inherits("KHTMLPart")) {
3120         qCCritical(KHTML_LOG) << "part is a" << part->metaObject()->className() << ", can't do a search into it";
3121         return;
3122     }
3123     static_cast<KHTMLPart *>(part)->findText();
3124 }
3125 
3126 void KHTMLPart::slotFindNext()
3127 {
3128     KParts::ReadOnlyPart *part = currentFrame();
3129     if (!part) {
3130         return;
3131     }
3132     if (!part->inherits("KHTMLPart")) {
3133         qCCritical(KHTML_LOG) << "part is a" << part->metaObject()->className() << ", can't do a search into it";
3134         return;
3135     }
3136     static_cast<KHTMLPart *>(part)->findTextNext();
3137 }
3138 
3139 void KHTMLPart::slotFindPrev()
3140 {
3141     KParts::ReadOnlyPart *part = currentFrame();
3142     if (!part) {
3143         return;
3144     }
3145     if (!part->inherits("KHTMLPart")) {
3146         qCCritical(KHTML_LOG) << "part is a" << part->metaObject()->className() << ", can't do a search into it";
3147         return;
3148     }
3149     static_cast<KHTMLPart *>(part)->findTextNext(true);     // reverse
3150 }
3151 
3152 void KHTMLPart::slotFindDone()
3153 {
3154     // ### remove me
3155 }
3156 
3157 void KHTMLPart::slotFindAheadText()
3158 {
3159     KHTMLPart *part = qobject_cast<KHTMLPart *>(currentFrame());
3160     if (!part) {
3161         return;
3162     }
3163     part->findText();
3164     KHTMLFindBar *findBar = part->d->m_find.findBar();
3165     findBar->setOptions(findBar->options() & ~FindLinksOnly);
3166 }
3167 
3168 void KHTMLPart::slotFindAheadLink()
3169 {
3170     KHTMLPart *part = qobject_cast<KHTMLPart *>(currentFrame());
3171     if (!part) {
3172         return;
3173     }
3174     part->findText();
3175     KHTMLFindBar *findBar = part->d->m_find.findBar();
3176     findBar->setOptions(findBar->options() | FindLinksOnly);
3177 }
3178 
3179 void KHTMLPart::enableFindAheadActions(bool)
3180 {
3181     // ### remove me
3182 }
3183 
3184 void KHTMLPart::slotFindDialogDestroyed()
3185 {
3186     // ### remove me
3187 }
3188 
3189 void KHTMLPart::findText()
3190 {
3191     if (parentPart()) {
3192         return parentPart()->findText();
3193     }
3194     d->m_find.activate();
3195 }
3196 
3197 void KHTMLPart::findText(const QString &str, long options, QWidget *parent, KFindDialog *findDialog)
3198 {
3199     if (parentPart()) {
3200         return parentPart()->findText(str, options, parent, findDialog);
3201     }
3202     d->m_find.createNewKFind(str, options, parent, findDialog);
3203 }
3204 
3205 // New method
3206 bool KHTMLPart::findTextNext(bool reverse)
3207 {
3208     if (parentPart()) {
3209         return parentPart()->findTextNext(reverse);
3210     }
3211     return d->m_find.findTextNext(reverse);
3212 }
3213 
3214 bool KHTMLPart::pFindTextNextInThisFrame(bool reverse)
3215 {
3216     return d->m_find.findTextNext(reverse);
3217 }
3218 
3219 QString KHTMLPart::selectedTextAsHTML() const
3220 {
3221     const Selection &sel = d->editor_context.m_selection;
3222     if (!hasSelection()) {
3223         // qCDebug(KHTML_LOG) << "Selection is not valid. Returning empty selection";
3224         return QString();
3225     }
3226     if (sel.start().offset() < 0 || sel.end().offset() < 0) {
3227         // qCDebug(KHTML_LOG) << "invalid values for end/startOffset " << sel.start().offset() << " " << sel.end().offset();
3228         return QString();
3229     }
3230     DOM::Range r = selection();
3231     if (r.isNull() || r.isDetached()) {
3232         return QString();
3233     }
3234     int exceptioncode = 0; //ignore the result
3235     return r.handle()->toHTML(exceptioncode).string();
3236 }
3237 
3238 QString KHTMLPart::selectedText() const
3239 {
3240     bool hasNewLine = true;
3241     bool seenTDTag = false;
3242     QString text;
3243     const Selection &sel = d->editor_context.m_selection;
3244     DOM::Node n = sel.start().node();
3245     while (!n.isNull()) {
3246         if (n.nodeType() == DOM::Node::TEXT_NODE && n.handle()->renderer()) {
3247             DOM::DOMStringImpl *dstr = static_cast<DOM::TextImpl *>(n.handle())->renderString();
3248             QString str(dstr->s, dstr->l);
3249             if (!str.isEmpty()) {
3250                 if (seenTDTag) {
3251                     text += "  ";
3252                     seenTDTag = false;
3253                 }
3254                 hasNewLine = false;
3255                 if (n == sel.start().node() && n == sel.end().node()) {
3256                     int s = khtml::RenderPosition::fromDOMPosition(sel.start()).renderedOffset();
3257                     int e = khtml::RenderPosition::fromDOMPosition(sel.end()).renderedOffset();
3258                     text = str.mid(s, e - s);
3259                 } else if (n == sel.start().node()) {
3260                     text = str.mid(khtml::RenderPosition::fromDOMPosition(sel.start()).renderedOffset());
3261                 } else if (n == sel.end().node()) {
3262                     text += str.left(khtml::RenderPosition::fromDOMPosition(sel.end()).renderedOffset());
3263                 } else {
3264                     text += str;
3265                 }
3266             }
3267         } else {
3268             // This is our simple HTML -> ASCII transformation:
3269             unsigned short id = n.elementId();
3270             switch (id) {
3271             case ID_TEXTAREA:
3272                 text += static_cast<HTMLTextAreaElementImpl *>(n.handle())->value().string();
3273                 break;
3274             case ID_INPUT:
3275                 if (static_cast<HTMLInputElementImpl *>(n.handle())->inputType() != HTMLInputElementImpl::PASSWORD) {
3276                     text += static_cast<HTMLInputElementImpl *>(n.handle())->value().string();
3277                 }
3278                 break;
3279             case ID_SELECT:
3280                 text += static_cast<HTMLSelectElementImpl *>(n.handle())->value().string();
3281                 break;
3282             case ID_BR:
3283                 text += "\n";
3284                 hasNewLine = true;
3285                 break;
3286             case ID_IMG:
3287                 text += static_cast<HTMLImageElementImpl *>(n.handle())->altText().string();
3288                 break;
3289             case ID_TD:
3290                 break;
3291             case ID_TH:
3292             case ID_HR:
3293             case ID_OL:
3294             case ID_UL:
3295             case ID_LI:
3296             case ID_DD:
3297             case ID_DL:
3298             case ID_DT:
3299             case ID_PRE:
3300             case ID_LISTING:
3301             case ID_BLOCKQUOTE:
3302             case ID_DIV:
3303                 if (!hasNewLine) {
3304                     text += "\n";
3305                 }
3306                 hasNewLine = true;
3307                 break;
3308             case ID_P:
3309             case ID_TR:
3310             case ID_H1:
3311             case ID_H2:
3312             case ID_H3:
3313             case ID_H4:
3314             case ID_H5:
3315             case ID_H6:
3316                 if (!hasNewLine) {
3317                     text += "\n";
3318                 }
3319                 hasNewLine = true;
3320                 break;
3321             }
3322         }
3323         if (n == sel.end().node()) {
3324             break;
3325         }
3326         DOM::Node next = n.firstChild();
3327         if (next.isNull()) {
3328             next = n.nextSibling();
3329         }
3330         while (next.isNull() && !n.parentNode().isNull()) {
3331             n = n.parentNode();
3332             next = n.nextSibling();
3333             unsigned short id = n.elementId();
3334             switch (id) {
3335             case ID_TD:
3336                 seenTDTag = true; //Add two spaces after a td if then followed by text.
3337                 break;
3338             case ID_TH:
3339             case ID_HR:
3340             case ID_OL:
3341             case ID_UL:
3342             case ID_LI:
3343             case ID_DD:
3344             case ID_DL:
3345             case ID_DT:
3346             case ID_PRE:
3347             case ID_LISTING:
3348             case ID_BLOCKQUOTE:
3349             case ID_DIV:
3350                 seenTDTag = false;
3351                 if (!hasNewLine) {
3352                     text += "\n";
3353                 }
3354                 hasNewLine = true;
3355                 break;
3356             case ID_P:
3357             case ID_TR:
3358             case ID_H1:
3359             case ID_H2:
3360             case ID_H3:
3361             case ID_H4:
3362             case ID_H5:
3363             case ID_H6:
3364                 if (!hasNewLine) {
3365                     text += "\n";
3366                 }
3367 //            text += "\n";
3368                 hasNewLine = true;
3369                 break;
3370             }
3371         }
3372 
3373         n = next;
3374     }
3375 
3376     if (text.isEmpty()) {
3377         return QString();
3378     }
3379 
3380     int start = 0;
3381     int end = text.length();
3382 
3383     // Strip leading LFs
3384     while ((start < end) && (text[start] == '\n')) {
3385         ++start;
3386     }
3387 
3388     // Strip excessive trailing LFs
3389     while ((start < (end - 1)) && (text[end - 1] == '\n') && (text[end - 2] == '\n')) {
3390         --end;
3391     }
3392 
3393     return text.mid(start, end - start);
3394 }
3395 
3396 QString KHTMLPart::simplifiedSelectedText() const
3397 {
3398     QString text = selectedText();
3399     text.replace(QChar(0xa0), ' ');
3400     // remove leading and trailing whitespace
3401     while (!text.isEmpty() && text[0].isSpace()) {
3402         text = text.mid(1);
3403     }
3404     while (!text.isEmpty() && text[text.length() - 1].isSpace()) {
3405         text.truncate(text.length() - 1);
3406     }
3407     return text;
3408 }
3409 
3410 bool KHTMLPart::hasSelection() const
3411 {
3412     return !d->editor_context.m_selection.isEmpty() && !d->editor_context.m_selection.isCollapsed();
3413 }
3414 
3415 DOM::Range KHTMLPart::selection() const
3416 {
3417     return d->editor_context.m_selection.toRange();
3418 }
3419 
3420 void KHTMLPart::selection(DOM::Node &s, long &so, DOM::Node &e, long &eo) const
3421 {
3422     DOM::Range r = d->editor_context.m_selection.toRange();
3423     s = r.startContainer();
3424     so = r.startOffset();
3425     e = r.endContainer();
3426     eo = r.endOffset();
3427 }
3428 
3429 void KHTMLPart::setSelection(const DOM::Range &r)
3430 {
3431     setCaret(r);
3432 }
3433 
3434 const Selection &KHTMLPart::caret() const
3435 {
3436     return d->editor_context.m_selection;
3437 }
3438 
3439 const Selection &KHTMLPart::dragCaret() const
3440 {
3441     return d->editor_context.m_dragCaret;
3442 }
3443 
3444 void KHTMLPart::setCaret(const Selection &s, bool closeTyping)
3445 {
3446     if (d->editor_context.m_selection != s) {
3447         clearCaretRectIfNeeded();
3448         setFocusNodeIfNeeded(s);
3449         d->editor_context.m_selection = s;
3450         notifySelectionChanged(closeTyping);
3451     }
3452 }
3453 
3454 void KHTMLPart::setDragCaret(const DOM::Selection &dragCaret)
3455 {
3456     if (d->editor_context.m_dragCaret != dragCaret) {
3457         d->editor_context.m_dragCaret.needsCaretRepaint();
3458         d->editor_context.m_dragCaret = dragCaret;
3459         d->editor_context.m_dragCaret.needsCaretRepaint();
3460     }
3461 }
3462 
3463 void KHTMLPart::clearSelection()
3464 {
3465     clearCaretRectIfNeeded();
3466     setFocusNodeIfNeeded(d->editor_context.m_selection);
3467 #ifdef APPLE_CHANGES
3468     d->editor_context.m_selection.clear();
3469 #else
3470     d->editor_context.m_selection.collapse();
3471 #endif
3472     notifySelectionChanged();
3473 }
3474 
3475 void KHTMLPart::invalidateSelection()
3476 {
3477     clearCaretRectIfNeeded();
3478     d->editor_context.m_selection.setNeedsLayout();
3479     selectionLayoutChanged();
3480 }
3481 
3482 void KHTMLPart::setSelectionVisible(bool flag)
3483 {
3484     if (d->editor_context.m_caretVisible == flag) {
3485         return;
3486     }
3487 
3488     clearCaretRectIfNeeded();
3489     setFocusNodeIfNeeded(d->editor_context.m_selection);
3490     d->editor_context.m_caretVisible = flag;
3491 //   notifySelectionChanged();
3492 }
3493 
3494 #if 1
3495 void KHTMLPart::slotClearSelection()
3496 {
3497     if (!isCaretMode()
3498             && d->editor_context.m_selection.state() != Selection::NONE
3499             && !d->editor_context.m_selection.caretPos().node()->isContentEditable()) {
3500         clearCaretRectIfNeeded();
3501     }
3502     bool hadSelection = hasSelection();
3503 #ifdef APPLE_CHANGES
3504     d->editor_context.m_selection.clear();
3505 #else
3506     d->editor_context.m_selection.collapse();
3507 #endif
3508     if (hadSelection) {
3509         notifySelectionChanged();
3510     }
3511 }
3512 #endif
3513 
3514 void KHTMLPart::clearCaretRectIfNeeded()
3515 {
3516     if (d->editor_context.m_caretPaint) {
3517         d->editor_context.m_caretPaint = false;
3518         d->editor_context.m_selection.needsCaretRepaint();
3519     }
3520 }
3521 
3522 void KHTMLPart::setFocusNodeIfNeeded(const Selection &s)
3523 {
3524     if (!xmlDocImpl() || s.state() == Selection::NONE) {
3525         return;
3526     }
3527 
3528     NodeImpl *n = s.start().node();
3529     NodeImpl *target = (n && n->isContentEditable()) ? n : nullptr;
3530     if (!target) {
3531         while (n && n != s.end().node()) {
3532             if (n->isContentEditable()) {
3533                 target = n;
3534                 break;
3535             }
3536             n = n->traverseNextNode();
3537         }
3538     }
3539     assert(target == nullptr || target->isContentEditable());
3540 
3541     if (target) {
3542         for (; target && !target->isFocusable(); target = target->parentNode()) {
3543         }
3544         if (target && target->isMouseFocusable()) {
3545             xmlDocImpl()->setFocusNode(target);
3546         } else if (!target || !target->focused()) {
3547             xmlDocImpl()->setFocusNode(nullptr);
3548         }
3549     }
3550 }
3551 
3552 void KHTMLPart::selectionLayoutChanged()
3553 {
3554     // kill any caret blink timer now running
3555     if (d->editor_context.m_caretBlinkTimer >= 0) {
3556         killTimer(d->editor_context.m_caretBlinkTimer);
3557         d->editor_context.m_caretBlinkTimer = -1;
3558     }
3559 
3560     // see if a new caret blink timer needs to be started
3561     if (d->editor_context.m_caretVisible
3562             && d->editor_context.m_selection.state() != Selection::NONE) {
3563         d->editor_context.m_caretPaint = isCaretMode()
3564                                          || d->editor_context.m_selection.caretPos().node()->isContentEditable();
3565         if (d->editor_context.m_caretBlinks && d->editor_context.m_caretPaint) {
3566             d->editor_context.m_caretBlinkTimer = startTimer(qApp->cursorFlashTime() / 2);
3567         }
3568         d->editor_context.m_selection.needsCaretRepaint();
3569         // make sure that caret is visible
3570         QRect r(d->editor_context.m_selection.getRepaintRect());
3571         if (d->editor_context.m_caretPaint) {
3572             d->m_view->ensureVisible(r.x(), r.y());
3573         }
3574     }
3575 
3576     if (d->m_doc) {
3577         d->m_doc->updateSelection();
3578     }
3579 
3580     // Always clear the x position used for vertical arrow navigation.
3581     // It will be restored by the vertical arrow navigation code if necessary.
3582     d->editor_context.m_xPosForVerticalArrowNavigation = d->editor_context.NoXPosForVerticalArrowNavigation;
3583 }
3584 
3585 void KHTMLPart::notifySelectionChanged(bool closeTyping)
3586 {
3587     Editor *ed = d->editor_context.m_editor;
3588     selectionLayoutChanged();
3589     if (ed) {
3590         ed->clearTypingStyle();
3591 
3592         if (closeTyping) {
3593             ed->closeTyping();
3594         }
3595     }
3596 
3597     emitSelectionChanged();
3598 }
3599 
3600 void KHTMLPart::timerEvent(QTimerEvent *e)
3601 {
3602     if (e->timerId() == d->editor_context.m_caretBlinkTimer) {
3603         if (d->editor_context.m_caretBlinks &&
3604                 d->editor_context.m_selection.state() != Selection::NONE) {
3605             d->editor_context.m_caretPaint = !d->editor_context.m_caretPaint;
3606             d->editor_context.m_selection.needsCaretRepaint();
3607         }
3608     } else if (e->timerId() == d->m_DNSPrefetchTimer) {
3609         // qCDebug(KHTML_LOG) << "will lookup " << d->m_DNSPrefetchQueue.head() << d->m_numDNSPrefetchedNames;
3610         KIO::HostInfo::prefetchHost(d->m_DNSPrefetchQueue.dequeue());
3611         if (d->m_DNSPrefetchQueue.isEmpty()) {
3612             killTimer(d->m_DNSPrefetchTimer);
3613             d->m_DNSPrefetchTimer = -1;
3614         }
3615     } else if (e->timerId() == d->m_DNSTTLTimer) {
3616         foreach (const QString &name, d->m_lookedupHosts) {
3617             d->m_DNSPrefetchQueue.enqueue(name);
3618         }
3619         if (d->m_DNSPrefetchTimer <= 0) {
3620             d->m_DNSPrefetchTimer = startTimer(sDNSPrefetchTimerDelay);
3621         }
3622     }
3623 }
3624 
3625 bool KHTMLPart::mayPrefetchHostname(const QString &name)
3626 {
3627     if (d->m_bDNSPrefetch == DNSPrefetchDisabled) {
3628         return false;
3629     }
3630 
3631     if (d->m_numDNSPrefetchedNames >= sMaxDNSPrefetchPerPage) {
3632         return false;
3633     }
3634 
3635     if (d->m_bDNSPrefetch == DNSPrefetchOnlyWWWAndSLD) {
3636         int dots = name.count('.');
3637         if (dots > 2 || (dots == 2 &&  !name.startsWith("www."))) {
3638             return false;
3639         }
3640     }
3641 
3642     if (d->m_lookedupHosts.contains(name)) {
3643         return false;
3644     }
3645 
3646     d->m_DNSPrefetchQueue.enqueue(name);
3647     d->m_lookedupHosts.insert(name);
3648     d->m_numDNSPrefetchedNames++;
3649 
3650     if (d->m_DNSPrefetchTimer < 1) {
3651         d->m_DNSPrefetchTimer = startTimer(sDNSPrefetchTimerDelay);
3652     }
3653     if (d->m_DNSTTLTimer < 1) {
3654         d->m_DNSTTLTimer = startTimer(sDNSTTLSeconds * 1000 + 1);
3655     }
3656 
3657     return true;
3658 }
3659 
3660 void KHTMLPart::paintCaret(QPainter *p, const QRect &rect) const
3661 {
3662     if (d->editor_context.m_caretPaint) {
3663         d->editor_context.m_selection.paintCaret(p, rect);
3664     }
3665 }
3666 
3667 void KHTMLPart::paintDragCaret(QPainter *p, const QRect &rect) const
3668 {
3669     d->editor_context.m_dragCaret.paintCaret(p, rect);
3670 }
3671 
3672 DOM::Editor *KHTMLPart::editor() const
3673 {
3674     if (!d->editor_context.m_editor) {
3675         const_cast<KHTMLPart *>(this)->d->editor_context.m_editor = new DOM::Editor(const_cast<KHTMLPart *>(this));
3676     }
3677     return d->editor_context.m_editor;
3678 }
3679 
3680 void KHTMLPart::resetHoverText()
3681 {
3682     if (!d->m_overURL.isEmpty()) { // Only if we were showing a link
3683         d->m_overURL.clear();
3684         d->m_overURLTarget.clear();
3685         emit onURL(QString());
3686         // revert to default statusbar text
3687         setStatusBarText(QString(), BarHoverText);
3688         emit d->m_extension->mouseOverInfo(KFileItem());
3689     }
3690 }
3691 
3692 void KHTMLPart::overURL(const QString &url, const QString &target, bool /*shiftPressed*/)
3693 {
3694     QUrl u = completeURL(url);
3695 
3696     // special case for <a href="">
3697     if (url.isEmpty()) {
3698         u = u.adjusted(QUrl::RemoveFilename);
3699     }
3700 
3701     emit onURL(url);
3702 
3703     if (url.isEmpty()) {
3704         setStatusBarText(u.toDisplayString().toHtmlEscaped(), BarHoverText);
3705         return;
3706     }
3707 
3708     if (d->isJavaScriptURL(url)) {
3709         QString jscode = d->codeForJavaScriptURL(url);
3710         jscode = KStringHandler::rsqueeze(jscode, 80);   // truncate if too long
3711         if (url.startsWith("javascript:window.open")) {
3712             jscode += i18n(" (In new window)");
3713         }
3714         setStatusBarText(jscode.toHtmlEscaped(), BarHoverText);
3715         return;
3716     }
3717 
3718     KFileItem item(u, QString(), KFileItem::Unknown);
3719     emit d->m_extension->mouseOverInfo(item);
3720     const QString com = item.mimeComment();
3721 
3722     if (!u.isValid()) {
3723         setStatusBarText(u.toDisplayString().toHtmlEscaped(), BarHoverText);
3724         return;
3725     }
3726 
3727     if (u.isLocalFile()) {
3728         // TODO : use KIO::stat() and create a KFileItem out of its result,
3729         // to use KFileItem::statusBarText()
3730 
3731         QFileInfo info(u.toLocalFile());
3732         bool ok = info.exists();
3733 
3734         QString text = u.toDisplayString().toHtmlEscaped();
3735         QString text2 = text;
3736 
3737         if (info.isSymLink()) {
3738             QString tmp;
3739             if (com.isEmpty()) {
3740                 tmp = i18n("Symbolic Link");
3741             } else {
3742                 tmp = i18n("%1 (Link)", com);
3743             }
3744             text += " -> ";
3745             QString target = info.symLinkTarget();
3746             if (target.isEmpty()) {
3747                 text2 += "  ";
3748                 text2 += tmp;
3749                 setStatusBarText(text2, BarHoverText);
3750                 return;
3751             }
3752 
3753             text += target;
3754             text += "  ";
3755             text += tmp;
3756         } else if (ok && info.isFile()) {
3757             if (info.size() < 1024) {
3758                 text = i18np("%2 (%1 byte)", "%2 (%1 bytes)", (long) info.size(), text2);    // always put the URL last, in case it contains '%'
3759             } else {
3760                 float d = (float) info.size() / 1024.0;
3761                 text = i18n("%2 (%1 K)", QLocale().toString(d, 'f', 2), text2); // was %.2f
3762             }
3763             text += "  ";
3764             text += com;
3765         } else if (ok && info.isDir()) {
3766             text += "  ";
3767             text += com;
3768         } else {
3769             text += "  ";
3770             text += com;
3771         }
3772         setStatusBarText(text, BarHoverText);
3773     } else {
3774         QString extra;
3775         if (target.toLower() == "_blank") {
3776             extra = i18n(" (In new window)");
3777         } else if (!target.isEmpty() &&
3778                    (target.toLower() != "_top") &&
3779                    (target.toLower() != "_self") &&
3780                    (target.toLower() != "_parent")) {
3781             KHTMLPart *p = this;
3782             while (p->parentPart()) {
3783                 p = p->parentPart();
3784             }
3785             if (!p->frameExists(target)) {
3786                 extra = i18n(" (In new window)");
3787             } else {
3788                 extra = i18n(" (In other frame)");
3789             }
3790         }
3791 
3792         if (u.scheme() == QLatin1String("mailto")) {
3793             QString mailtoMsg /* = QString::fromLatin1("<img src=%1>").arg(locate("icon", QString::fromLatin1("locolor/16x16/actions/mail_send.png")))*/;
3794             mailtoMsg += i18n("Email to: ") + QUrl::fromPercentEncoding(u.path().toLatin1());
3795             const QStringList queries = u.query().mid(1).split('&');
3796             QStringList::ConstIterator it = queries.begin();
3797             const QStringList::ConstIterator itEnd = queries.end();
3798             for (; it != itEnd; ++it)
3799                 if ((*it).startsWith(QLatin1String("subject="))) {
3800                     mailtoMsg += i18n(" - Subject: ") + QUrl::fromPercentEncoding((*it).mid(8).toLatin1());
3801                 } else if ((*it).startsWith(QLatin1String("cc="))) {
3802                     mailtoMsg += i18n(" - CC: ") + QUrl::fromPercentEncoding((*it).mid(3).toLatin1());
3803                 } else if ((*it).startsWith(QLatin1String("bcc="))) {
3804                     mailtoMsg += i18n(" - BCC: ") + QUrl::fromPercentEncoding((*it).mid(4).toLatin1());
3805                 }
3806             mailtoMsg = mailtoMsg.toHtmlEscaped();
3807             mailtoMsg.replace(QRegExp("([\n\r\t]|[ ]{10})"), QString());
3808             setStatusBarText("<qt>" + mailtoMsg, BarHoverText);
3809             return;
3810         }
3811         // Is this check necessary at all? (Frerich)
3812 #if 0
3813         else if (u.scheme() == QLatin1String("http")) {
3814             DOM::Node hrefNode = nodeUnderMouse().parentNode();
3815             while (hrefNode.nodeName().string() != QLatin1String("A") && !hrefNode.isNull()) {
3816                 hrefNode = hrefNode.parentNode();
3817             }
3818 
3819             if (!hrefNode.isNull()) {
3820                 DOM::Node hreflangNode = hrefNode.attributes().getNamedItem("HREFLANG");
3821                 if (!hreflangNode.isNull()) {
3822                     QString countryCode = hreflangNode.nodeValue().string().toLower();
3823                     // Map the language code to an appropriate country code.
3824                     if (countryCode == QLatin1String("en")) {
3825                         countryCode = QLatin1String("gb");
3826                     }
3827                     QString flagImg = QLatin1String("<img src=%1>").arg(
3828                                           locate("locale", QLatin1String("l10n/")
3829                                                  + countryCode
3830                                                  + QLatin1String("/kf5_flag.png")));
3831                     emit setStatusBarText(flagImg + u.toDisplayString() + extra);
3832                 }
3833             }
3834         }
3835 #endif
3836         setStatusBarText(u.toDisplayString().toHtmlEscaped() + extra, BarHoverText);
3837     }
3838 }
3839 
3840 //
3841 // This executes in the active part on a click or other url selection action in
3842 // that active part.
3843 //
3844 bool KHTMLPart::urlSelected(const QString &url, int button, int state, const QString &_target, const KParts::OpenUrlArguments &_args, const KParts::BrowserArguments &_browserArgs)
3845 {
3846     KParts::OpenUrlArguments args = _args;
3847     KParts::BrowserArguments browserArgs = _browserArgs;
3848     bool hasTarget = false;
3849 
3850     QString target = _target;
3851     if (target.isEmpty() && d->m_doc) {
3852         target = d->m_doc->baseTarget();
3853     }
3854     if (!target.isEmpty()) {
3855         hasTarget = true;
3856     }
3857 
3858     if (d->isJavaScriptURL(url)) {
3859         crossFrameExecuteScript(target, d->codeForJavaScriptURL(url));
3860         return false;
3861     }
3862 
3863     QUrl cURL = completeURL(url);
3864     // special case for <a href="">  (IE removes filename, mozilla doesn't)
3865     if (url.isEmpty()) {
3866         cURL = cURL.adjusted(QUrl::RemoveFilename);
3867     }
3868 
3869     if (!cURL.isValid())
3870         // ### ERROR HANDLING
3871     {
3872         return false;
3873     }
3874 
3875     // qCDebug(KHTML_LOG) << this << "complete URL:" << cURL << "target=" << target;
3876 
3877     if (state & Qt::ControlModifier) {
3878         emit d->m_extension->createNewWindow(cURL, args, browserArgs);
3879         return true;
3880     }
3881 
3882     if (button == Qt::LeftButton && (state & Qt::ShiftModifier)) {
3883         KIO::MetaData metaData;
3884         metaData.insert("referrer", d->m_referrer);
3885         KHTMLPopupGUIClient::saveURL(d->m_view, i18n("Save As"), cURL, metaData);
3886         return false;
3887     }
3888 
3889     if (!checkLinkSecurity(cURL,
3890                            ki18n("<qt>This untrusted page links to<br /><b>%1</b>.<br />Do you want to follow the link?</qt>"),
3891                            i18n("Follow"))) {
3892         return false;
3893     }
3894 
3895     browserArgs.frameName = target;
3896 
3897     args.metaData().insert("main_frame_request",
3898                            parentPart() == nullptr ? "TRUE" : "FALSE");
3899     args.metaData().insert("ssl_parent_ip", d->m_ssl_parent_ip);
3900     args.metaData().insert("ssl_parent_cert", d->m_ssl_parent_cert);
3901     args.metaData().insert("PropagateHttpHeader", "true");
3902     args.metaData().insert("ssl_was_in_use", d->m_ssl_in_use ? "TRUE" : "FALSE");
3903     args.metaData().insert("ssl_activate_warnings", "TRUE");
3904 
3905     if (hasTarget && target != "_self" && target != "_top" && target != "_blank" && target != "_parent") {
3906         // unknown frame names should open in a new window.
3907         khtml::ChildFrame *frame = recursiveFrameRequest(this, cURL, args, browserArgs, false);
3908         if (frame) {
3909             args.metaData()["referrer"] = d->m_referrer;
3910             requestObject(frame, cURL, args, browserArgs);
3911             return true;
3912         }
3913     }
3914 
3915     if (!d->m_referrer.isEmpty() && !args.metaData().contains("referrer")) {
3916         args.metaData()["referrer"] = d->m_referrer;
3917     }
3918 
3919     if (button == Qt::NoButton && (state & Qt::ShiftModifier) && (state & Qt::ControlModifier)) {
3920         emit d->m_extension->createNewWindow(cURL, args, browserArgs);
3921         return true;
3922     }
3923 
3924     if (state & Qt::ShiftModifier) {
3925         KParts::WindowArgs winArgs;
3926         winArgs.setLowerWindow(true);
3927         emit d->m_extension->createNewWindow(cURL, args, browserArgs, winArgs);
3928         return true;
3929     }
3930 
3931     //If we're asked to open up an anchor in the current URL, in current window,
3932     //merely gotoanchor, and do not reload the new page. Note that this does
3933     //not apply if the URL is the same page, but without a ref
3934     if (cURL.hasFragment() && (!hasTarget || target == "_self")) {
3935         if (d->isLocalAnchorJump(cURL)) {
3936             d->executeAnchorJump(cURL, browserArgs.lockHistory());
3937             return false; // we jumped, but we didn't open a URL
3938         }
3939     }
3940 
3941     if (!d->m_bComplete && !hasTarget) {
3942         closeUrl();
3943     }
3944 
3945     view()->viewport()->unsetCursor();
3946     emit d->m_extension->openUrlRequest(cURL, args, browserArgs);
3947     return true;
3948 }
3949 
3950 void KHTMLPart::slotViewDocumentSource()
3951 {
3952     QUrl currentUrl(this->url());
3953     KRun::RunFlags runFlags;
3954     if (!(currentUrl.isLocalFile()) && KHTMLPageCache::self()->isComplete(d->m_cacheId)) {
3955         QTemporaryFile sourceFile(QDir::tempPath() + QLatin1String("/XXXXXX") + defaultExtension());
3956         sourceFile.setAutoRemove(false);
3957         if (sourceFile.open()) {
3958             QDataStream stream(&sourceFile);
3959             KHTMLPageCache::self()->saveData(d->m_cacheId, &stream);
3960             currentUrl = QUrl::fromLocalFile(sourceFile.fileName());
3961             runFlags |= KRun::DeleteTemporaryFiles;
3962         }
3963     }
3964 
3965     (void) KRun::runUrl(currentUrl, QLatin1String("text/plain"), view(), runFlags);
3966 }
3967 
3968 void KHTMLPart::slotViewPageInfo()
3969 {
3970     Ui_KHTMLInfoDlg ui;
3971 
3972     QDialog *dlg = new QDialog(nullptr);
3973     dlg->setAttribute(Qt::WA_DeleteOnClose);
3974     dlg->setObjectName("KHTML Page Info Dialog");
3975     ui.setupUi(dlg);
3976 
3977     KGuiItem::assign(ui._close, KStandardGuiItem::close());
3978     connect(ui._close, SIGNAL(clicked()), dlg, SLOT(accept()));
3979 
3980     if (d->m_doc) {
3981         ui._title->setText(d->m_doc->title().string().trimmed());
3982     }
3983 
3984     // If it's a frame, set the caption to "Frame Information"
3985     if (parentPart() && d->m_doc && d->m_doc->isHTMLDocument()) {
3986         dlg->setWindowTitle(i18n("Frame Information"));
3987     }
3988 
3989     QString editStr;
3990 
3991     if (!d->m_pageServices.isEmpty()) {
3992         editStr = i18n("   <a href=\"%1\">[Properties]</a>", d->m_pageServices);
3993     }
3994 
3995     QString squeezedURL = KStringHandler::csqueeze(url().toDisplayString(), 80);
3996     ui._url->setText("<a href=\"" + url().toString() + "\">" + squeezedURL + "</a>" + editStr);
3997     if (lastModified().isEmpty()) {
3998         ui._lastModified->hide();
3999         ui._lmLabel->hide();
4000     } else {
4001         ui._lastModified->setText(lastModified());
4002     }
4003 
4004     const QString &enc = encoding();
4005     if (enc.isEmpty()) {
4006         ui._eLabel->hide();
4007         ui._encoding->hide();
4008     } else {
4009         ui._encoding->setText(enc);
4010     }
4011 
4012     if (!xmlDocImpl() || xmlDocImpl()->parseMode() == DOM::DocumentImpl::Unknown) {
4013         ui._mode->hide();
4014         ui._modeLabel->hide();
4015     } else {
4016         switch (xmlDocImpl()->parseMode()) {
4017         case DOM::DocumentImpl::Compat:
4018             ui._mode->setText(i18nc("HTML rendering mode (see https://en.wikipedia.org/wiki/Quirks_mode)", "Quirks"));
4019             break;
4020         case DOM::DocumentImpl::Transitional:
4021             ui._mode->setText(i18nc("HTML rendering mode (see https://en.wikipedia.org/wiki/Quirks_mode)", "Almost standards"));
4022             break;
4023         case DOM::DocumentImpl::Strict:
4024         default: // others handled above
4025             ui._mode->setText(i18nc("HTML rendering mode (see https://en.wikipedia.org/wiki/Quirks_mode)", "Strict"));
4026             break;
4027         }
4028     }
4029 
4030     /* populate the list view now */
4031     const QStringList headers = d->m_httpHeaders.split("\n");
4032 
4033     QStringList::ConstIterator it = headers.begin();
4034     const QStringList::ConstIterator itEnd = headers.end();
4035 
4036     for (; it != itEnd; ++it) {
4037         const QStringList header = (*it).split(QRegExp(":[ ]+"));
4038         if (header.count() != 2) {
4039             continue;
4040         }
4041         QTreeWidgetItem *item = new QTreeWidgetItem(ui._headers);
4042         item->setText(0, header[0]);
4043         item->setText(1, header[1]);
4044     }
4045 
4046     dlg->show();
4047     /* put no code here */
4048 }
4049 
4050 void KHTMLPart::slotViewFrameSource()
4051 {
4052     KParts::ReadOnlyPart *frame = currentFrame();
4053     if (!frame) {
4054         return;
4055     }
4056 
4057     QUrl url = frame->url();
4058     KRun::RunFlags runFlags;
4059     if (!(url.isLocalFile()) && frame->inherits("KHTMLPart")) {
4060         long cacheId = static_cast<KHTMLPart *>(frame)->d->m_cacheId;
4061 
4062         if (KHTMLPageCache::self()->isComplete(cacheId)) {
4063             QTemporaryFile sourceFile(QDir::tempPath() + QLatin1String("/XXXXXX") + defaultExtension());
4064             sourceFile.setAutoRemove(false);
4065             if (sourceFile.open()) {
4066                 QDataStream stream(&sourceFile);
4067                 KHTMLPageCache::self()->saveData(cacheId, &stream);
4068                 url = QUrl();
4069                 url.setPath(sourceFile.fileName());
4070                 runFlags |= KRun::DeleteTemporaryFiles;
4071             }
4072         }
4073     }
4074 
4075     (void) KRun::runUrl(url, QLatin1String("text/plain"), view(), runFlags);
4076 }
4077 
4078 QUrl KHTMLPart::backgroundURL() const
4079 {
4080     // ### what about XML documents? get from CSS?
4081     if (!d->m_doc || !d->m_doc->isHTMLDocument()) {
4082         return QUrl();
4083     }
4084 
4085     QString relURL = static_cast<HTMLDocumentImpl *>(d->m_doc)->body()->getAttribute(ATTR_BACKGROUND).string();
4086 
4087     return url().resolved(QUrl(relURL));
4088 }
4089 
4090 void KHTMLPart::slotSaveBackground()
4091 {
4092     KIO::MetaData metaData;
4093     metaData["referrer"] = d->m_referrer;
4094     KHTMLPopupGUIClient::saveURL(d->m_view, i18n("Save Background Image As"), backgroundURL(), metaData);
4095 }
4096 
4097 void KHTMLPart::slotSaveDocument()
4098 {
4099     QUrl srcURL(url());
4100 
4101     if (srcURL.fileName().isEmpty()) {
4102         srcURL.setPath(srcURL.path() + "index" + defaultExtension());
4103     }
4104 
4105     KIO::MetaData metaData;
4106     // Referre unknown?
4107     KHTMLPopupGUIClient::saveURL(d->m_view, i18n("Save As"), srcURL, metaData, "text/html", d->m_cacheId);
4108 }
4109 
4110 void KHTMLPart::slotSecurity()
4111 {
4112 //   qCDebug(KHTML_LOG) << "Meta Data:" << endl
4113 //                   << d->m_ssl_peer_cert_subject
4114 //                   << endl
4115 //                   << d->m_ssl_peer_cert_issuer
4116 //                   << endl
4117 //                   << d->m_ssl_cipher
4118 //                   << endl
4119 //                   << d->m_ssl_cipher_desc
4120 //                   << endl
4121 //                   << d->m_ssl_cipher_version
4122 //                   << endl
4123 //                   << d->m_ssl_good_from
4124 //                   << endl
4125 //                   << d->m_ssl_good_until
4126 //                   << endl
4127 //                   << d->m_ssl_cert_state
4128 //                   << endl;
4129 
4130     //### reenable with new signature
4131 #if 0
4132     KSslInfoDialog *kid = new KSslInfoDialog(d->m_ssl_in_use, widget(), "kssl_info_dlg", true);
4133 
4134     const QStringList sl = d->m_ssl_peer_chain.split('\n', QString::SkipEmptyParts);
4135     QList<QSslCertificate> certChain;
4136     bool certChainOk = d->m_ssl_in_use;
4137     if (certChainOk) {
4138         foreach (const QString &s, sl) {
4139             certChain.append(QSslCertificate(s.toLatin1())); //or is it toLocal8Bit or whatever?
4140             if (certChain.last().isNull()) {
4141                 certChainOk = false;
4142                 break;
4143             }
4144         }
4145     }
4146     if (certChainOk) {
4147         kid->setup(certChain,
4148                    d->m_ssl_peer_ip,
4149                    url().toString(),
4150                    d->m_ssl_cipher,
4151                    d->m_ssl_cipher_desc,
4152                    d->m_ssl_cipher_version,
4153                    d->m_ssl_cipher_used_bits.toInt(),
4154                    d->m_ssl_cipher_bits.toInt(),
4155                    (KSSLCertificate::KSSLValidation) d->m_ssl_cert_state.toInt());
4156     }
4157     kid->exec();
4158     //the dialog deletes itself on close
4159 #endif
4160 
4161     KSslInfoDialog *kid = new KSslInfoDialog(nullptr);
4162     //### This is boilerplate code and it's copied from SlaveInterface.
4163     QStringList sl = d->m_ssl_peer_chain.split('\x01', QString::SkipEmptyParts);
4164     QList<QSslCertificate> certChain;
4165     bool decodedOk = true;
4166     foreach (const QString &s, sl) {
4167         certChain.append(QSslCertificate(s.toLatin1())); //or is it toLocal8Bit or whatever?
4168         if (certChain.last().isNull()) {
4169             decodedOk = false;
4170             break;
4171         }
4172     }
4173 
4174     if (decodedOk || true /*H4X*/) {
4175         kid->setSslInfo(certChain,
4176                         d->m_ssl_peer_ip,
4177                         url().host(),
4178                         d->m_ssl_protocol_version,
4179                         d->m_ssl_cipher,
4180                         d->m_ssl_cipher_used_bits.toInt(),
4181                         d->m_ssl_cipher_bits.toInt(),
4182                         KSslInfoDialog::errorsFromString(d->m_ssl_cert_errors));
4183         // qCDebug(KHTML_LOG) << "Showing SSL Info dialog";
4184         kid->exec();
4185         // qCDebug(KHTML_LOG) << "SSL Info dialog closed";
4186     } else {
4187         KMessageBox::information(nullptr, i18n("The peer SSL certificate chain "
4188                                          "appears to be corrupt."),
4189                                  i18n("SSL"));
4190     }
4191 }
4192 
4193 void KHTMLPart::slotSaveFrame()
4194 {
4195     KParts::ReadOnlyPart *frame = currentFrame();
4196     if (!frame) {
4197         return;
4198     }
4199 
4200     QUrl srcURL(frame->url());
4201 
4202     if (srcURL.fileName().isEmpty()) {
4203         srcURL.setPath(srcURL.path() + "index" + defaultExtension());
4204     }
4205 
4206     KIO::MetaData metaData;
4207     // Referrer unknown?
4208     KHTMLPopupGUIClient::saveURL(d->m_view, i18n("Save Frame As"), srcURL, metaData, "text/html");
4209 }
4210 
4211 void KHTMLPart::slotSetEncoding(const QString &enc)
4212 {
4213     d->m_autoDetectLanguage = KEncodingProber::None;
4214     setEncoding(enc, true);
4215 }
4216 
4217 void KHTMLPart::slotAutomaticDetectionLanguage(KEncodingProber::ProberType scri)
4218 {
4219     d->m_autoDetectLanguage = scri;
4220     setEncoding(QString(), false);
4221 }
4222 
4223 void KHTMLPart::slotUseStylesheet()
4224 {
4225     if (d->m_doc) {
4226         bool autoselect = (d->m_paUseStylesheet->currentItem() == 0);
4227         d->m_sheetUsed = autoselect ? QString() : d->m_paUseStylesheet->currentText();
4228         d->m_doc->updateStyleSelector();
4229     }
4230 }
4231 
4232 void KHTMLPart::updateActions()
4233 {
4234     bool frames = false;
4235 
4236     QList<khtml::ChildFrame *>::ConstIterator it = d->m_frames.constBegin();
4237     const QList<khtml::ChildFrame *>::ConstIterator end = d->m_frames.constEnd();
4238     for (; it != end; ++it)
4239         if ((*it)->m_type == khtml::ChildFrame::Frame) {
4240             frames = true;
4241             break;
4242         }
4243 
4244     if (d->m_paViewFrame) {
4245         d->m_paViewFrame->setEnabled(frames);
4246     }
4247     if (d->m_paSaveFrame) {
4248         d->m_paSaveFrame->setEnabled(frames);
4249     }
4250 
4251     if (frames) {
4252         d->m_paFind->setText(i18n("&Find in Frame..."));
4253     } else {
4254         d->m_paFind->setText(i18n("&Find..."));
4255     }
4256 
4257     KParts::Part *frame = nullptr;
4258 
4259     if (frames) {
4260         frame = currentFrame();
4261     }
4262 
4263     bool enableFindAndSelectAll = true;
4264 
4265     if (frame) {
4266         enableFindAndSelectAll = frame->inherits("KHTMLPart");
4267     }
4268 
4269     d->m_paFind->setEnabled(enableFindAndSelectAll);
4270     d->m_paSelectAll->setEnabled(enableFindAndSelectAll);
4271 
4272     bool enablePrintFrame = false;
4273 
4274     if (frame) {
4275         QObject *ext = KParts::BrowserExtension::childObject(frame);
4276         if (ext) {
4277             enablePrintFrame = ext->metaObject()->indexOfSlot("print()") != -1;
4278         }
4279     }
4280 
4281     d->m_paPrintFrame->setEnabled(enablePrintFrame);
4282 
4283     QString bgURL;
4284 
4285     // ### frames
4286     if (d->m_doc && d->m_doc->isHTMLDocument() && static_cast<HTMLDocumentImpl *>(d->m_doc)->body() && !d->m_bClearing) {
4287         bgURL = static_cast<HTMLDocumentImpl *>(d->m_doc)->body()->getAttribute(ATTR_BACKGROUND).string();
4288     }
4289 
4290     if (d->m_paSaveBackground) {
4291         d->m_paSaveBackground->setEnabled(!bgURL.isEmpty());
4292     }
4293 
4294     if (d->m_paDebugScript) {
4295         d->m_paDebugScript->setEnabled(d->m_frame ? d->m_frame->m_jscript : nullptr);
4296     }
4297 }
4298 
4299 KParts::ScriptableExtension *KHTMLPart::scriptableExtension(const DOM::NodeImpl *frame)
4300 {
4301     const ConstFrameIt end = d->m_objects.constEnd();
4302     for (ConstFrameIt it = d->m_objects.constBegin(); it != end; ++it)
4303         if ((*it)->m_partContainerElement.data() == frame) {
4304             return (*it)->m_scriptable.data();
4305         }
4306     return nullptr;
4307 }
4308 
4309 void KHTMLPart::loadFrameElement(DOM::HTMLPartContainerElementImpl *frame, const QString &url,
4310                                  const QString &frameName, const QStringList &params, bool isIFrame)
4311 {
4312     //qCDebug(KHTML_LOG) << this << " requestFrame( ..., " << url << ", " << frameName << " )";
4313     khtml::ChildFrame *child;
4314 
4315     FrameIt it = d->m_frames.find(frameName);
4316     if (it == d->m_frames.end()) {
4317         child = new khtml::ChildFrame;
4318         //qCDebug(KHTML_LOG) << "inserting new frame into frame map " << frameName;
4319         child->m_name = frameName;
4320         d->m_frames.insert(d->m_frames.end(), child);
4321     } else {
4322         child = *it;
4323     }
4324 
4325     child->m_type = isIFrame ? khtml::ChildFrame::IFrame : khtml::ChildFrame::Frame;
4326     child->m_partContainerElement = frame;
4327     child->m_params = params;
4328 
4329     // If we do not have a part, make sure we create one.
4330     if (!child->m_part) {
4331         QStringList dummy; // the list of servicetypes handled by the part is now unused.
4332         QString     khtml = QString::fromLatin1("khtml");
4333         KParts::ReadOnlyPart *part = createPart(d->m_view->viewport(), this,
4334                                                 QString::fromLatin1("text/html"),
4335                                                 khtml, dummy, QStringList());
4336         // We navigate it to about:blank to setup an empty one, but we do it
4337         // before hooking up the signals and extensions, so that any sync emit
4338         // of completed by the kid doesn't cause us to be marked as completed.
4339         // (async ones are discovered by the presence of the KHTMLRun)
4340         // ### load event on the kid?
4341         navigateLocalProtocol(child, part, QUrl("about:blank"));
4342         connectToChildPart(child, part, "text/html" /* mimetype of the part, not what's being loaded */);
4343     }
4344 
4345     QUrl u = url.isEmpty() ? QUrl() : completeURL(url);
4346 
4347     // Since we don't specify args here a KHTMLRun will be used to determine the
4348     // mimetype, which will then be  passed down at the bottom of processObjectRequest
4349     // inside URLArgs to the part. In our particular case, this means that we can
4350     // use that inside KHTMLPart::openUrl to route things appropriately.
4351     child->m_bCompleted = false;
4352     if (!requestObject(child, u) && !child->m_run) {
4353         child->m_bCompleted = true;
4354     }
4355 }
4356 
4357 QString KHTMLPart::requestFrameName()
4358 {
4359     return QString::fromLatin1("<!--frame %1-->").arg(d->m_frameNameId++);
4360 }
4361 
4362 bool KHTMLPart::loadObjectElement(DOM::HTMLPartContainerElementImpl *frame, const QString &url,
4363                                   const QString &serviceType, const QStringList &params)
4364 {
4365     //qCDebug(KHTML_LOG) << this << "frame=" << frame;
4366     khtml::ChildFrame *child = new khtml::ChildFrame;
4367     FrameIt it = d->m_objects.insert(d->m_objects.end(), child);
4368     (*it)->m_partContainerElement = frame;
4369     (*it)->m_type = khtml::ChildFrame::Object;
4370     (*it)->m_params = params;
4371 
4372     KParts::OpenUrlArguments args;
4373     args.setMimeType(serviceType);
4374     if (!requestObject(*it, completeURL(url), args) && !(*it)->m_run) {
4375         (*it)->m_bCompleted = true;
4376         return false;
4377     }
4378     return true;
4379 }
4380 
4381 bool KHTMLPart::requestObject(khtml::ChildFrame *child, const QUrl &url, const KParts::OpenUrlArguments &_args,
4382                               const KParts::BrowserArguments &browserArgs)
4383 {
4384     // we always permit javascript: URLs here since they're basically just
4385     // empty pages (and checkLinkSecurity/KAuthorized doesn't know what to do with them)
4386     if (!d->isJavaScriptURL(url.toString()) && !checkLinkSecurity(url)) {
4387         // qCDebug(KHTML_LOG) << this << "checkLinkSecurity refused";
4388         return false;
4389     }
4390 
4391     if (d->m_bClearing) {
4392         return false;
4393     }
4394 
4395     if (child->m_bPreloaded) {
4396         if (child->m_partContainerElement && child->m_part) {
4397             child->m_partContainerElement.data()->setWidget(child->m_part.data()->widget());
4398         }
4399 
4400         child->m_bPreloaded = false;
4401         return true;
4402     }
4403 
4404     //qCDebug(KHTML_LOG) << "child=" << child << "child->m_part=" << child->m_part;
4405 
4406     KParts::OpenUrlArguments args(_args);
4407 
4408     if (child->m_run) {
4409         // qCDebug(KHTML_LOG) << "navigating ChildFrame while mimetype resolution was in progress...";
4410         child->m_run.data()->abort();
4411     }
4412 
4413     // ### Dubious -- the whole dir/ vs. img thing
4414     if (child->m_part && !args.reload() && areUrlsForSamePage(child->m_part.data()->url(), url)) {
4415         args.setMimeType(child->m_serviceType);
4416     }
4417 
4418     child->m_browserArgs = browserArgs;
4419     child->m_args = args;
4420 
4421     // reload/soft-reload arguments are always inherited from parent
4422     child->m_args.setReload(arguments().reload());
4423     child->m_browserArgs.softReload = d->m_extension->browserArguments().softReload;
4424 
4425     child->m_serviceName.clear();
4426     if (!d->m_referrer.isEmpty() && !child->m_args.metaData().contains("referrer")) {
4427         child->m_args.metaData()["referrer"] = d->m_referrer;
4428     }
4429 
4430     child->m_args.metaData().insert("PropagateHttpHeader", "true");
4431     child->m_args.metaData().insert("ssl_parent_ip", d->m_ssl_parent_ip);
4432     child->m_args.metaData().insert("ssl_parent_cert", d->m_ssl_parent_cert);
4433     child->m_args.metaData().insert("main_frame_request",
4434                                     parentPart() == nullptr ? "TRUE" : "FALSE");
4435     child->m_args.metaData().insert("ssl_was_in_use",
4436                                     d->m_ssl_in_use ? "TRUE" : "FALSE");
4437     child->m_args.metaData().insert("ssl_activate_warnings", "TRUE");
4438     child->m_args.metaData().insert("cross-domain", toplevelURL().toString());
4439 
4440     // We know the frame will be text/html if the HTML says <frame src=""> or <frame src="about:blank">,
4441     // no need to KHTMLRun to figure out the mimetype"
4442     // ### What if we're inside an XML document?
4443     if ((url.isEmpty() || url.toString() == "about:blank" || url.scheme() == "javascript") && args.mimeType().isEmpty()) {
4444         args.setMimeType(QLatin1String("text/html"));
4445     }
4446 
4447     if (args.mimeType().isEmpty()) {
4448         // qCDebug(KHTML_LOG) << "Running new KHTMLRun for" << this << "and child=" << child;
4449         child->m_run = new KHTMLRun(this, child, url, child->m_args, child->m_browserArgs, true);
4450         d->m_bComplete = false; // ensures we stop it in checkCompleted...
4451         return false;
4452     } else {
4453         return processObjectRequest(child, url, args.mimeType());
4454     }
4455 }
4456 
4457 void KHTMLPart::childLoadFailure(khtml::ChildFrame *child)
4458 {
4459     child->m_bCompleted = true;
4460     if (child->m_partContainerElement) {
4461         child->m_partContainerElement.data()->partLoadingErrorNotify();
4462     }
4463 
4464     checkCompleted();
4465 }
4466 
4467 bool KHTMLPart::processObjectRequest(khtml::ChildFrame *child, const QUrl &_url, const QString &mimetype)
4468 {
4469     // qCDebug(KHTML_LOG) << "trying to create part for" << mimetype << _url;
4470 
4471     // IMPORTANT: create a copy of the url here, because it is just a reference, which was likely to be given
4472     // by an emitting frame part (emit openUrlRequest( blahurl, ... ) . A few lines below we delete the part
4473     // though -> the reference becomes invalid -> crash is likely
4474     QUrl url(_url);
4475 
4476     // khtmlrun called us with empty url + mimetype to indicate a loading error,
4477     // we obviosuly failed; but we can return true here since we don't want it
4478     // doing anything more, while childLoadFailure is enough to notify our kid.
4479     if (d->m_onlyLocalReferences || (url.isEmpty() && mimetype.isEmpty())) {
4480         childLoadFailure(child);
4481         return true;
4482     }
4483 
4484     // we also want to ignore any spurious requests due to closing when parser is being cleared. These should be
4485     // ignored entirely  --- the tail end of ::clear will clean things up.
4486     if (d->m_bClearing) {
4487         return false;
4488     }
4489 
4490     if (child->m_bNotify) {
4491         child->m_bNotify = false;
4492         if (!child->m_browserArgs.lockHistory()) {
4493             emit d->m_extension->openUrlNotify();
4494         }
4495     }
4496 
4497     QMimeDatabase db;
4498 
4499     // Now, depending on mimetype and current state of the world, we may have
4500     // to create a new part or ask the user to save things, etc.
4501     //
4502     // We need a new part if there isn't one at all (doh) or the one that's there
4503     // is not for the mimetype we're loading.
4504     //
4505     // For these new types, we may have to ask the user to save it or not
4506     // (we don't if it's navigating the same type).
4507     // Further, we will want to ask if content-disposition suggests we ask for
4508     // saving, even if we're re-navigating.
4509     if (!child->m_part || child->m_serviceType != mimetype ||
4510             (child->m_run && child->m_run.data()->serverSuggestsSave())) {
4511         // We often get here if we didn't know the mimetype in advance, and had to rely
4512         // on KRun to figure it out. In this case, we let the element check if it wants to
4513         // handle this mimetype itself, for e.g. objects containing images.
4514         if (child->m_partContainerElement &&
4515                 child->m_partContainerElement.data()->mimetypeHandledInternally(mimetype)) {
4516             child->m_bCompleted = true;
4517             checkCompleted();
4518             return true;
4519         }
4520 
4521         // Before attempting to load a part, check if the user wants that.
4522         // Many don't like getting ZIP files embedded.
4523         // However we don't want to ask for flash and other plugin things.
4524         //
4525         // Note: this is fine for frames, since we will merely effectively ignore
4526         // the navigation if this happens
4527         if (child->m_type != khtml::ChildFrame::Object && child->m_type != khtml::ChildFrame::IFrame) {
4528             QString suggestedFileName;
4529             int disposition = 0;
4530             if (KHTMLRun *run = child->m_run.data()) {
4531                 suggestedFileName = run->suggestedFileName();
4532                 disposition = run->serverSuggestsSave() ?
4533                               KParts::BrowserRun::AttachmentDisposition :
4534                               KParts::BrowserRun::InlineDisposition;
4535             }
4536 
4537             KParts::BrowserOpenOrSaveQuestion dlg(widget(), url, mimetype);
4538             dlg.setSuggestedFileName(suggestedFileName);
4539             const KParts::BrowserOpenOrSaveQuestion::Result res = dlg.askEmbedOrSave(disposition);
4540 
4541             switch (res) {
4542             case KParts::BrowserOpenOrSaveQuestion::Save:
4543                 KHTMLPopupGUIClient::saveURL(widget(), i18n("Save As"), url, child->m_args.metaData(), QString(), 0, suggestedFileName);
4544             // fall-through
4545             case KParts::BrowserOpenOrSaveQuestion::Cancel:
4546                 child->m_bCompleted = true;
4547                 checkCompleted();
4548                 return true; // done
4549             default: // Embed
4550                 break;
4551             }
4552         }
4553 
4554         // Now, for frames and iframes, we always create a KHTMLPart anyway,
4555         // doing it in advance when registering the frame. So we want the
4556         // actual creation only for objects here.
4557         if (child->m_type == khtml::ChildFrame::Object) {
4558             QMimeType mime = db.mimeTypeForName(mimetype);
4559             if (mime.isValid()) {
4560                 // Even for objects, however, we want to force a KHTMLPart for
4561                 // html & xml, even  if the normally preferred part is another one,
4562                 // so that we can script the target natively via contentDocument method.
4563                 if (mime.inherits("text/html")
4564                         || mime.inherits("application/xml")) { // this includes xhtml and svg
4565                     child->m_serviceName = "khtml";
4566                 } else {
4567                     if (!pluginsEnabled()) {
4568                         childLoadFailure(child);
4569                         return false;
4570                     }
4571                 }
4572             }
4573 
4574             QStringList dummy; // the list of servicetypes handled by the part is now unused.
4575             KParts::ReadOnlyPart *part = createPart(d->m_view->viewport(), this, mimetype, child->m_serviceName, dummy, child->m_params);
4576 
4577             if (!part) {
4578                 childLoadFailure(child);
4579                 return false;
4580             }
4581 
4582             connectToChildPart(child, part, mimetype);
4583         }
4584     }
4585 
4586     checkEmitLoadEvent();
4587 
4588     // Some JS code in the load event may have destroyed the part
4589     // In that case, abort
4590     if (!child->m_part) {
4591         return false;
4592     }
4593 
4594     if (child->m_bPreloaded) {
4595         if (child->m_partContainerElement && child->m_part) {
4596             child->m_partContainerElement.data()->setWidget(child->m_part.data()->widget());
4597         }
4598 
4599         child->m_bPreloaded = false;
4600         return true;
4601     }
4602 
4603     // reload/soft-reload arguments are always inherited from parent
4604     child->m_args.setReload(arguments().reload());
4605     child->m_browserArgs.softReload = d->m_extension->browserArguments().softReload;
4606 
4607     // make sure the part has a way to find out about the mimetype.
4608     // we actually set it in child->m_args in requestObject already,
4609     // but it's useless if we had to use a KHTMLRun instance, as the
4610     // point the run object is to find out exactly the mimetype.
4611     child->m_args.setMimeType(mimetype);
4612     child->m_part.data()->setArguments(child->m_args);
4613 
4614     // if not a frame set child as completed
4615     // ### dubious.
4616     child->m_bCompleted = child->m_type == khtml::ChildFrame::Object;
4617 
4618     if (child->m_extension) {
4619         child->m_extension.data()->setBrowserArguments(child->m_browserArgs);
4620     }
4621 
4622     return navigateChild(child, url);
4623 }
4624 
4625 bool KHTMLPart::navigateLocalProtocol(khtml::ChildFrame * /*child*/, KParts::ReadOnlyPart *inPart,
4626                                       const QUrl &url)
4627 {
4628     if (!qobject_cast<KHTMLPart *>(inPart)) {
4629         return false;
4630     }
4631 
4632     KHTMLPart *p = static_cast<KHTMLPart *>(static_cast<KParts::ReadOnlyPart *>(inPart));
4633 
4634     p->begin();
4635 
4636     // We may have to re-propagate the domain here if we go here due to navigation
4637     d->propagateInitialDomainAndBaseTo(p);
4638 
4639     // Support for javascript: sources
4640     if (d->isJavaScriptURL(url.toString())) {
4641         // See if we want to replace content with javascript: output..
4642         QVariant res = p->executeScript(DOM::Node(),
4643                                         d->codeForJavaScriptURL(url.toString()));
4644         if (res.type() == QVariant::String && p->d->m_redirectURL.isEmpty()) {
4645             p->begin();
4646             p->setAlwaysHonourDoctype(); // Disable public API compat; it messes with doctype
4647             // We recreated the document, so propagate domain again.
4648             d->propagateInitialDomainAndBaseTo(p);
4649             p->write(res.toString());
4650             p->end();
4651         }
4652     } else {
4653         p->setUrl(url);
4654         // we need a body element. testcase: <iframe id="a"></iframe><script>alert(a.document.body);</script>
4655         p->write("<HTML><TITLE></TITLE><BODY></BODY></HTML>");
4656     }
4657     p->end();
4658     // we don't need to worry about child completion explicitly for KHTMLPart...
4659     // or do we?
4660     return true;
4661 }
4662 
4663 bool KHTMLPart::navigateChild(khtml::ChildFrame *child, const QUrl &url)
4664 {
4665     if (url.scheme() == "javascript" || url.toString() == "about:blank") {
4666         return navigateLocalProtocol(child, child->m_part.data(), url);
4667     } else if (!url.isEmpty()) {
4668         // qCDebug(KHTML_LOG) << "opening" << url << "in frame" << child->m_part;
4669         bool b = child->m_part.data()->openUrl(url);
4670         if (child->m_bCompleted) {
4671             checkCompleted();
4672         }
4673         return b;
4674     } else {
4675         // empty URL -> no need to navigate
4676         child->m_bCompleted = true;
4677         checkCompleted();
4678         return true;
4679     }
4680 }
4681 
4682 void KHTMLPart::connectToChildPart(khtml::ChildFrame *child, KParts::ReadOnlyPart *part,
4683                                    const QString &mimetype)
4684 {
4685     // qCDebug(KHTML_LOG) << "we:" << this << "kid:" << child << part << mimetype;
4686 
4687     part->setObjectName(child->m_name);
4688 
4689     // Cleanup any previous part for this childframe and its connections
4690     if (KParts::ReadOnlyPart *p = child->m_part.data()) {
4691         if (!qobject_cast<KHTMLPart *>(p) && child->m_jscript) {
4692             child->m_jscript->clear();
4693         }
4694         partManager()->removePart(p);
4695         delete p;
4696         child->m_scriptable.clear();
4697     }
4698 
4699     child->m_part = part;
4700 
4701     child->m_serviceType = mimetype;
4702     if (child->m_partContainerElement && part->widget()) {
4703         child->m_partContainerElement.data()->setWidget(part->widget());
4704     }
4705 
4706     if (child->m_type != khtml::ChildFrame::Object) {
4707         partManager()->addPart(part, false);
4708     }
4709 //  else
4710 //      qCDebug(KHTML_LOG) << "AH! NO FRAME!!!!!";
4711 
4712     if (qobject_cast<KHTMLPart *>(part)) {
4713         static_cast<KHTMLPart *>(part)->d->m_frame = child;
4714     } else if (child->m_partContainerElement) {
4715         // See if this can be scripted..
4716         KParts::ScriptableExtension *scriptExt = KParts::ScriptableExtension::childObject(part);
4717         if (!scriptExt) {
4718             // Try to fall back to LiveConnectExtension compat
4719             KParts::LiveConnectExtension *lc = KParts::LiveConnectExtension::childObject(part);
4720             if (lc) {
4721                 scriptExt = KParts::ScriptableExtension::adapterFromLiveConnect(part, lc);
4722             }
4723         }
4724 
4725         if (scriptExt) {
4726             scriptExt->setHost(d->m_scriptableExtension);
4727         }
4728         child->m_scriptable = scriptExt;
4729     }
4730     KParts::StatusBarExtension *sb = KParts::StatusBarExtension::childObject(part);
4731     if (sb) {
4732         sb->setStatusBar(d->m_statusBarExtension->statusBar());
4733     }
4734 
4735     connect(part, SIGNAL(started(KIO::Job*)),
4736             this, SLOT(slotChildStarted(KIO::Job*)));
4737     connect(part, SIGNAL(completed()),
4738             this, SLOT(slotChildCompleted()));
4739     connect(part, SIGNAL(completed(bool)),
4740             this, SLOT(slotChildCompleted(bool)));
4741     connect(part, SIGNAL(setStatusBarText(QString)),
4742             this, SIGNAL(setStatusBarText(QString)));
4743     if (part->inherits("KHTMLPart")) {
4744         connect(this, SIGNAL(completed()),
4745                 part, SLOT(slotParentCompleted()));
4746         connect(this, SIGNAL(completed(bool)),
4747                 part, SLOT(slotParentCompleted()));
4748         // As soon as the child's document is created, we need to set its domain
4749         // (but we do so only once, so it can't be simply done in the child)
4750         connect(part, SIGNAL(docCreated()),
4751                 this, SLOT(slotChildDocCreated()));
4752     }
4753 
4754     child->m_extension = KParts::BrowserExtension::childObject(part);
4755 
4756     if (KParts::BrowserExtension *kidBrowserExt = child->m_extension.data()) {
4757         connect(kidBrowserExt, SIGNAL(openUrlNotify()),
4758                 d->m_extension, SIGNAL(openUrlNotify()));
4759 
4760         connect(kidBrowserExt, SIGNAL(openUrlRequestDelayed(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)),
4761                 this, SLOT(slotChildURLRequest(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)));
4762 
4763         connect(kidBrowserExt, SIGNAL(createNewWindow(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::WindowArgs,KParts::ReadOnlyPart**)),
4764                 d->m_extension, SIGNAL(createNewWindow(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::WindowArgs,KParts::ReadOnlyPart**)));
4765 
4766         connect(kidBrowserExt, SIGNAL(popupMenu(QPoint,KFileItemList,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)),
4767                 d->m_extension, SIGNAL(popupMenu(QPoint,KFileItemList,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)));
4768         connect(kidBrowserExt, SIGNAL(popupMenu(QPoint,QUrl,mode_t,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)),
4769                 d->m_extension, SIGNAL(popupMenu(QPoint,QUrl,mode_t,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)));
4770 
4771         connect(kidBrowserExt, SIGNAL(infoMessage(QString)),
4772                 d->m_extension, SIGNAL(infoMessage(QString)));
4773 
4774         connect(kidBrowserExt, SIGNAL(requestFocus(KParts::ReadOnlyPart*)),
4775                 this, SLOT(slotRequestFocus(KParts::ReadOnlyPart*)));
4776 
4777         kidBrowserExt->setBrowserInterface(d->m_extension->browserInterface());
4778     }
4779 }
4780 
4781 KParts::ReadOnlyPart *KHTMLPart::createPart(QWidget *parentWidget,
4782         QObject *parent, const QString &mimetype,
4783         QString &serviceName, QStringList &serviceTypes,
4784         const QStringList &params)
4785 {
4786     QString constr;
4787     if (!serviceName.isEmpty()) {
4788         constr.append(QString::fromLatin1("DesktopEntryName == '%1'").arg(serviceName));
4789     }
4790 
4791     KService::List offers = KMimeTypeTrader::self()->query(mimetype, "KParts/ReadOnlyPart", constr);
4792 
4793     if (offers.isEmpty()) {
4794         int pos = mimetype.indexOf("-plugin");
4795         if (pos < 0) {
4796             return nullptr;
4797         }
4798         QString stripped_mime = mimetype.left(pos);
4799         offers = KMimeTypeTrader::self()->query(stripped_mime, "KParts/ReadOnlyPart", constr);
4800         if (offers.isEmpty()) {
4801             return nullptr;
4802         }
4803     }
4804 
4805     KService::List::ConstIterator it = offers.constBegin();
4806     const KService::List::ConstIterator itEnd = offers.constEnd();
4807     for (; it != itEnd; ++it) {
4808         KService::Ptr service = (*it);
4809 
4810         KPluginLoader loader(*service);
4811         KPluginFactory *const factory = loader.factory();
4812         if (factory) {
4813             // Turn params into a QVariantList as expected by KPluginFactory
4814             QVariantList variantlist;
4815             Q_FOREACH (const QString &str, params) {
4816                 variantlist << QVariant(str);
4817             }
4818 
4819             if (service->serviceTypes().contains("Browser/View")) {
4820                 variantlist << QString("Browser/View");
4821             }
4822 
4823             KParts::ReadOnlyPart *part = factory->create<KParts::ReadOnlyPart>(parentWidget, parent, QString(), variantlist);
4824             if (part) {
4825                 serviceTypes = service->serviceTypes();
4826                 serviceName = service->name();
4827                 return part;
4828             }
4829         } else {
4830             // TODO KMessageBox::error and i18n, like in KonqFactory::createView?
4831             qCWarning(KHTML_LOG) << QString("There was an error loading the module %1.\nThe diagnostics is:\n%2")
4832                        .arg(service->name()).arg(loader.errorString());
4833         }
4834     }
4835     return nullptr;
4836 }
4837 
4838 KParts::PartManager *KHTMLPart::partManager()
4839 {
4840     if (!d->m_manager && d->m_view) {
4841         d->m_manager = new KParts::PartManager(d->m_view->topLevelWidget(), this);
4842         d->m_manager->setObjectName("khtml part manager");
4843         d->m_manager->setAllowNestedParts(true);
4844         connect(d->m_manager, SIGNAL(activePartChanged(KParts::Part*)),
4845                 this, SLOT(slotActiveFrameChanged(KParts::Part*)));
4846         connect(d->m_manager, SIGNAL(partRemoved(KParts::Part*)),
4847                 this, SLOT(slotPartRemoved(KParts::Part*)));
4848     }
4849 
4850     return d->m_manager;
4851 }
4852 
4853 void KHTMLPart::submitFormAgain()
4854 {
4855     disconnect(this, SIGNAL(completed()), this, SLOT(submitFormAgain()));
4856     if (d->m_doc && !d->m_doc->parsing() && d->m_submitForm) {
4857         KHTMLPart::submitForm(d->m_submitForm->submitAction, d->m_submitForm->submitUrl, d->m_submitForm->submitFormData, d->m_submitForm->target, d->m_submitForm->submitContentType, d->m_submitForm->submitBoundary);
4858     }
4859 
4860     delete d->m_submitForm;
4861     d->m_submitForm = nullptr;
4862 }
4863 
4864 void KHTMLPart::submitFormProxy(const char *action, const QString &url, const QByteArray &formData, const QString &_target, const QString &contentType, const QString &boundary)
4865 {
4866     submitForm(action, url, formData, _target, contentType, boundary);
4867 }
4868 
4869 void KHTMLPart::submitForm(const char *action, const QString &url, const QByteArray &formData, const QString &_target, const QString &contentType, const QString &boundary)
4870 {
4871     // qCDebug(KHTML_LOG) << this << "target=" << _target << "url=" << url;
4872     if (d->m_formNotification == KHTMLPart::Only) {
4873         emit formSubmitNotification(action, url, formData, _target, contentType, boundary);
4874         return;
4875     } else if (d->m_formNotification == KHTMLPart::Before) {
4876         emit formSubmitNotification(action, url, formData, _target, contentType, boundary);
4877     }
4878 
4879     QUrl u = completeURL(url);
4880 
4881     if (!u.isValid()) {
4882         // ### ERROR HANDLING!
4883         return;
4884     }
4885 
4886     // Form security checks
4887     //
4888     /*
4889      * If these form security checks are still in this place in a month or two
4890      * I'm going to simply delete them.
4891      */
4892 
4893     /* This is separate for a reason.  It has to be _before_ all script, etc,
4894      * AND I don't want to break anything that uses checkLinkSecurity() in
4895      * other places.
4896      */
4897 
4898     if (!d->m_submitForm) {
4899         if (u.scheme() != "https" && u.scheme() != "mailto") {
4900             if (d->m_ssl_in_use) {    // Going from SSL -> nonSSL
4901                 int rc = KMessageBox::warningContinueCancel(nullptr, i18n("Warning:  This is a secure form but it is attempting to send your data back unencrypted."
4902                          "\nA third party may be able to intercept and view this information."
4903                          "\nAre you sure you wish to continue?"),
4904                          i18n("Network Transmission"), KGuiItem(i18n("&Send Unencrypted")));
4905                 if (rc == KMessageBox::Cancel) {
4906                     return;
4907                 }
4908             } else {                  // Going from nonSSL -> nonSSL
4909                 KSSLSettings kss(true);
4910                 if (kss.warnOnUnencrypted()) {
4911                     int rc = KMessageBox::warningContinueCancel(nullptr,
4912                              i18n("Warning: Your data is about to be transmitted across the network unencrypted."
4913                                   "\nAre you sure you wish to continue?"),
4914                              i18n("Network Transmission"),
4915                              KGuiItem(i18n("&Send Unencrypted")),
4916                              KStandardGuiItem::cancel(),
4917                              "WarnOnUnencryptedForm");
4918                     // Move this setting into KSSL instead
4919                     QString grpNotifMsgs = QLatin1String("Notification Messages");
4920                     KConfigGroup cg(KSharedConfig::openConfig(), grpNotifMsgs);
4921 
4922                     if (!cg.readEntry("WarnOnUnencryptedForm", true)) {
4923                         cg.deleteEntry("WarnOnUnencryptedForm");
4924                         cg.sync();
4925                         kss.setWarnOnUnencrypted(false);
4926                         kss.save();
4927                     }
4928                     if (rc == KMessageBox::Cancel) {
4929                         return;
4930                     }
4931                 }
4932             }
4933         }
4934 
4935         if (u.scheme() == "mailto") {
4936             int rc = KMessageBox::warningContinueCancel(nullptr,
4937                      i18n("This site is attempting to submit form data via email.\n"
4938                           "Do you want to continue?"),
4939                      i18n("Network Transmission"),
4940                      KGuiItem(i18n("&Send Email")),
4941                      KStandardGuiItem::cancel(),
4942                      "WarnTriedEmailSubmit");
4943 
4944             if (rc == KMessageBox::Cancel) {
4945                 return;
4946             }
4947         }
4948     }
4949 
4950     // End form security checks
4951     //
4952 
4953     QString urlstring = u.toString();
4954 
4955     if (d->isJavaScriptURL(urlstring)) {
4956         crossFrameExecuteScript(_target, d->codeForJavaScriptURL(urlstring));
4957         return;
4958     }
4959 
4960     if (!checkLinkSecurity(u,
4961                            ki18n("<qt>The form will be submitted to <br /><b>%1</b><br />on your local filesystem.<br />Do you want to submit the form?</qt>"),
4962                            i18n("Submit"))) {
4963         return;
4964     }
4965 
4966     // OK. We're actually going to submit stuff. Clear any redirections,
4967     // we should win over them
4968     d->clearRedirection();
4969 
4970     KParts::OpenUrlArguments args;
4971 
4972     if (!d->m_referrer.isEmpty()) {
4973         args.metaData()["referrer"] = d->m_referrer;
4974     }
4975 
4976     args.metaData().insert("PropagateHttpHeader", "true");
4977     args.metaData().insert("ssl_parent_ip", d->m_ssl_parent_ip);
4978     args.metaData().insert("ssl_parent_cert", d->m_ssl_parent_cert);
4979     args.metaData().insert("main_frame_request",
4980                            parentPart() == nullptr ? "TRUE" : "FALSE");
4981     args.metaData().insert("ssl_was_in_use", d->m_ssl_in_use ? "TRUE" : "FALSE");
4982     args.metaData().insert("ssl_activate_warnings", "TRUE");
4983 //WABA: When we post a form we should treat it as the main url
4984 //the request should never be considered cross-domain
4985 //args.metaData().insert("cross-domain", toplevelURL().toString());
4986     KParts::BrowserArguments browserArgs;
4987     browserArgs.frameName = _target.isEmpty() ? d->m_doc->baseTarget() : _target;
4988 
4989     // Handle mailto: forms
4990     if (u.scheme() == "mailto") {
4991         // 1)  Check for attach= and strip it
4992         QString q = u.query().mid(1);
4993         QStringList nvps = q.split("&");
4994         bool triedToAttach = false;
4995 
4996         QStringList::Iterator nvp = nvps.begin();
4997         const QStringList::Iterator nvpEnd = nvps.end();
4998 
4999 // cannot be a for loop as if something is removed we don't want to do ++nvp, as
5000 // remove returns an iterator pointing to the next item
5001 
5002         while (nvp != nvpEnd) {
5003             const QStringList pair = (*nvp).split("=");
5004             if (pair.count() >= 2) {
5005                 if (pair.first().toLower() == "attach") {
5006                     nvp = nvps.erase(nvp);
5007                     triedToAttach = true;
5008                 } else {
5009                     ++nvp;
5010                 }
5011             } else {
5012                 ++nvp;
5013             }
5014         }
5015 
5016         if (triedToAttach) {
5017             KMessageBox::information(nullptr, i18n("This site attempted to attach a file from your computer in the form submission. The attachment was removed for your protection."), i18n("KDE"), "WarnTriedAttach");
5018         }
5019 
5020         // 2)  Append body=
5021         QString bodyEnc;
5022         if (contentType.toLower() == "multipart/form-data") {
5023             // FIXME: is this correct?  I suspect not
5024             bodyEnc = QLatin1String(QUrl::toPercentEncoding(QString::fromLatin1(formData.data(),
5025                                     formData.size())));
5026         } else if (contentType.toLower() == "text/plain") {
5027             // Convention seems to be to decode, and s/&/\n/
5028             QString tmpbody = QString::fromLatin1(formData.data(),
5029                                                   formData.size());
5030             tmpbody.replace(QRegExp("[&]"), "\n");
5031             tmpbody.replace(QRegExp("[+]"), " ");
5032             tmpbody = QUrl::fromPercentEncoding(tmpbody.toLatin1());  // Decode the rest of it
5033             bodyEnc = QLatin1String(QUrl::toPercentEncoding(tmpbody));    // Recode for the URL
5034         } else {
5035             bodyEnc = QLatin1String(QUrl::toPercentEncoding(QString::fromLatin1(formData.data(),
5036                                     formData.size())));
5037         }
5038 
5039         nvps.append(QString("body=%1").arg(bodyEnc));
5040         q = nvps.join("&");
5041         u.setQuery(q);
5042     }
5043 
5044     if (strcmp(action, "get") == 0) {
5045         if (u.scheme() != "mailto") {
5046             u.setQuery(QString::fromLatin1(formData.data(), formData.size()));
5047         }
5048         browserArgs.setDoPost(false);
5049     } else {
5050         browserArgs.postData = formData;
5051         browserArgs.setDoPost(true);
5052 
5053         // construct some user headers if necessary
5054         if (contentType.isNull() || contentType == "application/x-www-form-urlencoded") {
5055             browserArgs.setContentType("Content-Type: application/x-www-form-urlencoded");
5056         } else { // contentType must be "multipart/form-data"
5057             browserArgs.setContentType("Content-Type: " + contentType + "; boundary=" + boundary);
5058         }
5059     }
5060 
5061     if (d->m_doc->parsing() || d->m_runningScripts > 0) {
5062         if (d->m_submitForm) {
5063             // qCDebug(KHTML_LOG) << "ABORTING!";
5064             return;
5065         }
5066         d->m_submitForm = new KHTMLPartPrivate::SubmitForm;
5067         d->m_submitForm->submitAction = action;
5068         d->m_submitForm->submitUrl = url;
5069         d->m_submitForm->submitFormData = formData;
5070         d->m_submitForm->target = _target;
5071         d->m_submitForm->submitContentType = contentType;
5072         d->m_submitForm->submitBoundary = boundary;
5073         connect(this, SIGNAL(completed()), this, SLOT(submitFormAgain()));
5074     } else {
5075         emit d->m_extension->openUrlRequest(u, args, browserArgs);
5076     }
5077 }
5078 
5079 void KHTMLPart::popupMenu(const QString &linkUrl)
5080 {
5081     QUrl popupURL;
5082     QUrl linkKUrl;
5083     KParts::OpenUrlArguments args;
5084     KParts::BrowserArguments browserArgs;
5085     QString referrer;
5086     KParts::BrowserExtension::PopupFlags itemflags = KParts::BrowserExtension::ShowBookmark | KParts::BrowserExtension::ShowReload;
5087 
5088     if (linkUrl.isEmpty()) {   // click on background
5089         KHTMLPart *khtmlPart = this;
5090         while (khtmlPart->parentPart()) {
5091             khtmlPart = khtmlPart->parentPart();
5092         }
5093         popupURL = khtmlPart->url();
5094         referrer = khtmlPart->pageReferrer();
5095         if (hasSelection()) {
5096             itemflags = KParts::BrowserExtension::ShowTextSelectionItems;
5097         } else {
5098             itemflags |= KParts::BrowserExtension::ShowNavigationItems;
5099         }
5100     } else {               // click on link
5101         popupURL = completeURL(linkUrl);
5102         linkKUrl = popupURL;
5103         referrer = this->referrer();
5104         itemflags |= KParts::BrowserExtension::IsLink;
5105 
5106         if (!(d->m_strSelectedURLTarget).isEmpty() &&
5107                 (d->m_strSelectedURLTarget.toLower() != "_top") &&
5108                 (d->m_strSelectedURLTarget.toLower() != "_self") &&
5109                 (d->m_strSelectedURLTarget.toLower() != "_parent")) {
5110             if (d->m_strSelectedURLTarget.toLower() == "_blank") {
5111                 browserArgs.setForcesNewWindow(true);
5112             } else {
5113                 KHTMLPart *p = this;
5114                 while (p->parentPart()) {
5115                     p = p->parentPart();
5116                 }
5117                 if (!p->frameExists(d->m_strSelectedURLTarget)) {
5118                     browserArgs.setForcesNewWindow(true);
5119                 }
5120             }
5121         }
5122     }
5123 
5124     QMimeDatabase db;
5125 
5126     // Danger, Will Robinson. The Popup might stay around for a much
5127     // longer time than KHTMLPart. Deal with it.
5128     KHTMLPopupGUIClient *client = new KHTMLPopupGUIClient(this, linkKUrl);
5129     QPointer<QObject> guard(client);
5130 
5131     QString mimetype = QLatin1String("text/html");
5132     args.metaData()["referrer"] = referrer;
5133 
5134     if (!linkUrl.isEmpty()) {                              // over a link
5135         if (popupURL.isLocalFile()) {                              // safe to do this
5136             mimetype = db.mimeTypeForUrl(popupURL).name();
5137         } else {                                            // look at "extension" of link
5138             const QString fname(popupURL.fileName());
5139             if (!fname.isEmpty() && !popupURL.hasFragment() && popupURL.query().isEmpty()) {
5140                 QMimeType pmt = db.mimeTypeForFile(fname, QMimeDatabase::MatchExtension);
5141 
5142                 // Further check for mime types guessed from the extension which,
5143                 // on a web page, are more likely to be a script delivering content
5144                 // of undecidable type. If the mime type from the extension is one
5145                 // of these, don't use it.  Retain the original type 'text/html'.
5146                 if (!pmt.isDefault() &&
5147                         !pmt.inherits("application/x-perl") &&
5148                         !pmt.inherits("application/x-perl-module") &&
5149                         !pmt.inherits("application/x-php") &&
5150                         !pmt.inherits("application/x-python-bytecode") &&
5151                         !pmt.inherits("application/x-python") &&
5152                         !pmt.inherits("application/x-shellscript")) {
5153                     mimetype = pmt.name();
5154                 }
5155             }
5156         }
5157     }
5158 
5159     args.setMimeType(mimetype);
5160 
5161     emit d->m_extension->popupMenu(QCursor::pos(), popupURL, S_IFREG /*always a file*/,
5162                                    args, browserArgs, itemflags,
5163                                    client->actionGroups());
5164 
5165     if (!guard.isNull()) {
5166         delete client;
5167         emit popupMenu(linkUrl, QCursor::pos());
5168         d->m_strSelectedURL.clear();
5169         d->m_strSelectedURLTarget.clear();
5170     }
5171 }
5172 
5173 void KHTMLPart::slotParentCompleted()
5174 {
5175     //qCDebug(KHTML_LOG) << this;
5176     if (!d->m_redirectURL.isEmpty() && !d->m_redirectionTimer.isActive()) {
5177         //qCDebug(KHTML_LOG) << this << ": starting timer for child redirection -> " << d->m_redirectURL;
5178         d->m_redirectionTimer.setSingleShot(true);
5179         d->m_redirectionTimer.start(qMax(0, 1000 * d->m_delayRedirect));
5180     }
5181 }
5182 
5183 void KHTMLPart::slotChildStarted(KIO::Job *job)
5184 {
5185     khtml::ChildFrame *child = frame(sender());
5186 
5187     assert(child);
5188 
5189     child->m_bCompleted = false;
5190 
5191     if (d->m_bComplete) {
5192 #if 0
5193         // WABA: Looks like this belongs somewhere else
5194         if (!parentPart()) { // "toplevel" html document? if yes, then notify the hosting browser about the document (url) changes
5195             emit d->m_extension->openURLNotify();
5196         }
5197 #endif
5198         d->m_bComplete = false;
5199         emit started(job);
5200     }
5201 }
5202 
5203 void KHTMLPart::slotChildCompleted()
5204 {
5205     slotChildCompleted(false);
5206 }
5207 
5208 void KHTMLPart::slotChildCompleted(bool pendingAction)
5209 {
5210     khtml::ChildFrame *child = frame(sender());
5211 
5212     if (child) {
5213         // qCDebug(KHTML_LOG) << this << "child=" << child << "m_partContainerElement=" << child->m_partContainerElement;
5214         child->m_bCompleted = true;
5215         child->m_bPendingRedirection = pendingAction;
5216         child->m_args = KParts::OpenUrlArguments();
5217         child->m_browserArgs = KParts::BrowserArguments();
5218         // dispatch load event. We don't do that for KHTMLPart's since their internal
5219         // load will be forwarded inside NodeImpl::dispatchWindowEvent
5220         if (!qobject_cast<KHTMLPart *>(child->m_part)) {
5221             QTimer::singleShot(0, child->m_partContainerElement.data(), SLOT(slotEmitLoadEvent()));
5222         }
5223     }
5224     checkCompleted();
5225 }
5226 
5227 void KHTMLPart::slotChildDocCreated()
5228 {
5229     // Set domain to the frameset's domain
5230     // This must only be done when loading the frameset initially (#22039),
5231     // not when following a link in a frame (#44162).
5232     if (KHTMLPart *htmlFrame = qobject_cast<KHTMLPart *>(sender())) {
5233         d->propagateInitialDomainAndBaseTo(htmlFrame);
5234     }
5235 
5236     // So it only happens once
5237     disconnect(sender(), SIGNAL(docCreated()), this, SLOT(slotChildDocCreated()));
5238 }
5239 
5240 void KHTMLPartPrivate::propagateInitialDomainAndBaseTo(KHTMLPart *kid)
5241 {
5242     // This method is used to propagate our domain and base information for
5243     // child frames, to provide them for about: or JavaScript: URLs
5244     if (m_doc && kid->d->m_doc) {
5245         DocumentImpl *kidDoc = kid->d->m_doc;
5246         if (kidDoc->origin()->isEmpty()) {
5247             kidDoc->setOrigin(m_doc->origin());
5248             kidDoc->setBaseURL(m_doc->baseURL());
5249         }
5250     }
5251 }
5252 
5253 void KHTMLPart::slotChildURLRequest(const QUrl &url, const KParts::OpenUrlArguments &args, const KParts::BrowserArguments &browserArgs)
5254 {
5255     khtml::ChildFrame *child = frame(sender()->parent());
5256     KHTMLPart *callingHtmlPart = const_cast<KHTMLPart *>(dynamic_cast<const KHTMLPart *>(sender()->parent()));
5257 
5258     // TODO: handle child target correctly! currently the script are always executed for the parent
5259     QString urlStr = url.toString();
5260     if (d->isJavaScriptURL(urlStr)) {
5261         executeScript(DOM::Node(), d->codeForJavaScriptURL(urlStr));
5262         return;
5263     }
5264 
5265     QString frameName = browserArgs.frameName.toLower();
5266     if (!frameName.isEmpty()) {
5267         if (frameName == QLatin1String("_top")) {
5268             emit d->m_extension->openUrlRequest(url, args, browserArgs);
5269             return;
5270         } else if (frameName == QLatin1String("_blank")) {
5271             emit d->m_extension->createNewWindow(url, args, browserArgs);
5272             return;
5273         } else if (frameName == QLatin1String("_parent")) {
5274             KParts::BrowserArguments newBrowserArgs(browserArgs);
5275             newBrowserArgs.frameName.clear();
5276             emit d->m_extension->openUrlRequest(url, args, newBrowserArgs);
5277             return;
5278         } else if (frameName != QLatin1String("_self")) {
5279             khtml::ChildFrame *_frame = recursiveFrameRequest(callingHtmlPart, url, args, browserArgs);
5280 
5281             if (!_frame) {
5282                 emit d->m_extension->openUrlRequest(url, args, browserArgs);
5283                 return;
5284             }
5285 
5286             child = _frame;
5287         }
5288     }
5289 
5290     if (child && child->m_type != khtml::ChildFrame::Object) {
5291         // Inform someone that we are about to show something else.
5292         child->m_bNotify = true;
5293         requestObject(child, url, args, browserArgs);
5294     }  else if (frameName == "_self") { // this is for embedded objects (via <object>) which want to replace the current document
5295         KParts::BrowserArguments newBrowserArgs(browserArgs);
5296         newBrowserArgs.frameName.clear();
5297         emit d->m_extension->openUrlRequest(url, args, newBrowserArgs);
5298     }
5299 }
5300 
5301 void KHTMLPart::slotRequestFocus(KParts::ReadOnlyPart *)
5302 {
5303     emit d->m_extension->requestFocus(this);
5304 }
5305 
5306 khtml::ChildFrame *KHTMLPart::frame(const QObject *obj)
5307 {
5308     assert(obj->inherits("KParts::ReadOnlyPart"));
5309     const KParts::ReadOnlyPart *const part = static_cast<const KParts::ReadOnlyPart *>(obj);
5310 
5311     FrameIt it = d->m_frames.begin();
5312     const FrameIt end = d->m_frames.end();
5313     for (; it != end; ++it) {
5314         if ((*it)->m_part.data() == part) {
5315             return *it;
5316         }
5317     }
5318 
5319     FrameIt oi = d->m_objects.begin();
5320     const FrameIt oiEnd = d->m_objects.end();
5321     for (; oi != oiEnd; ++oi) {
5322         if ((*oi)->m_part.data() == part) {
5323             return *oi;
5324         }
5325     }
5326 
5327     return nullptr;
5328 }
5329 
5330 //#define DEBUG_FINDFRAME
5331 
5332 bool KHTMLPart::checkFrameAccess(KHTMLPart *callingHtmlPart)
5333 {
5334     if (callingHtmlPart == this) {
5335         return true;    // trivial
5336     }
5337 
5338     if (!xmlDocImpl()) {
5339 #ifdef DEBUG_FINDFRAME
5340         qCDebug(KHTML_LOG) << "Empty part" << this << "URL = " << url();
5341 #endif
5342         return false; // we are empty?
5343     }
5344 
5345     // now compare the domains
5346     if (callingHtmlPart && callingHtmlPart->xmlDocImpl() && xmlDocImpl())  {
5347         khtml::SecurityOrigin *actDomain = callingHtmlPart->xmlDocImpl()->origin();
5348         khtml::SecurityOrigin *destDomain = xmlDocImpl()->origin();
5349 
5350         if (actDomain->canAccess(destDomain)) {
5351             return true;
5352         }
5353     }
5354 #ifdef DEBUG_FINDFRAME
5355     else {
5356         qCDebug(KHTML_LOG) << "Unknown part/domain" << callingHtmlPart << "tries to access part" << this;
5357     }
5358 #endif
5359     return false;
5360 }
5361 
5362 KHTMLPart *
5363 KHTMLPart::findFrameParent(KParts::ReadOnlyPart *callingPart, const QString &f, khtml::ChildFrame **childFrame)
5364 {
5365     return d->findFrameParent(callingPart, f, childFrame, false);
5366 }
5367 
5368 KHTMLPart *KHTMLPartPrivate::findFrameParent(KParts::ReadOnlyPart *callingPart,
5369         const QString &f, khtml::ChildFrame **childFrame, bool checkForNavigation)
5370 {
5371 #ifdef DEBUG_FINDFRAME
5372     qCDebug(KHTML_LOG) << q << "URL =" << q->url() << "name =" << q->objectName() << "findFrameParent(" << f << ")";
5373 #endif
5374     // Check access
5375     KHTMLPart *const callingHtmlPart = qobject_cast<KHTMLPart *>(callingPart);
5376 
5377     if (!callingHtmlPart) {
5378         return nullptr;
5379     }
5380 
5381     if (!checkForNavigation && !q->checkFrameAccess(callingHtmlPart)) {
5382         return nullptr;
5383     }
5384 
5385     if (!childFrame && !q->parentPart() && (q->objectName() == f)) {
5386         if (!checkForNavigation || callingHtmlPart->d->canNavigate(q)) {
5387             return q;
5388         }
5389     }
5390 
5391     FrameIt it = m_frames.find(f);
5392     const FrameIt end = m_frames.end();
5393     if (it != end) {
5394 #ifdef DEBUG_FINDFRAME
5395         qCDebug(KHTML_LOG) << "FOUND!";
5396 #endif
5397         if (!checkForNavigation || callingHtmlPart->d->canNavigate((*it)->m_part.data())) {
5398             if (childFrame) {
5399                 *childFrame = *it;
5400             }
5401             return q;
5402         }
5403     }
5404 
5405     it = m_frames.begin();
5406     for (; it != end; ++it) {
5407         if (KHTMLPart *p = qobject_cast<KHTMLPart *>((*it)->m_part.data())) {
5408             KHTMLPart *const frameParent = p->d->findFrameParent(callingPart, f, childFrame, checkForNavigation);
5409             if (frameParent) {
5410                 return frameParent;
5411             }
5412         }
5413     }
5414     return nullptr;
5415 }
5416 
5417 KHTMLPart *KHTMLPartPrivate::top()
5418 {
5419     KHTMLPart *t = q;
5420     while (t->parentPart()) {
5421         t = t->parentPart();
5422     }
5423     return t;
5424 }
5425 
5426 bool KHTMLPartPrivate::canNavigate(KParts::ReadOnlyPart *bCand)
5427 {
5428     if (!bCand) { // No part here (e.g. invalid url), reuse that frame
5429         return true;
5430     }
5431 
5432     KHTMLPart *b = qobject_cast<KHTMLPart *>(bCand);
5433     if (!b) { // Another kind of part? Not sure what to do...
5434         return false;
5435     }
5436 
5437     // HTML5 gives conditions for this (a) being able to navigate b
5438 
5439     // 1) Same domain
5440     if (q->checkFrameAccess(b)) {
5441         return true;
5442     }
5443 
5444     // 2) A is nested, with B its top
5445     if (q->parentPart() && top() == b) {
5446         return true;
5447     }
5448 
5449     // 3) B is 'auxilary' -- window.open with opener,
5450     // and A can navigate B's opener
5451     if (b->opener() && canNavigate(b->opener())) {
5452         return true;
5453     }
5454 
5455     // 4) B is not top-level, but an ancestor of it has same origin as A
5456     for (KHTMLPart *anc = b->parentPart(); anc; anc = anc->parentPart()) {
5457         if (anc->checkFrameAccess(q)) {
5458             return true;
5459         }
5460     }
5461 
5462     return false;
5463 }
5464 
5465 KHTMLPart *KHTMLPart::findFrame(const QString &f)
5466 {
5467     khtml::ChildFrame *childFrame;
5468     KHTMLPart *parentFrame = findFrameParent(this, f, &childFrame);
5469     if (parentFrame) {
5470         return qobject_cast<KHTMLPart *>(childFrame->m_part.data());
5471     }
5472 
5473     return nullptr;
5474 }
5475 
5476 KParts::ReadOnlyPart *KHTMLPart::findFramePart(const QString &f)
5477 {
5478     khtml::ChildFrame *childFrame;
5479     return findFrameParent(this, f, &childFrame) ? childFrame->m_part.data() : nullptr;
5480 }
5481 
5482 KParts::ReadOnlyPart *KHTMLPart::currentFrame() const
5483 {
5484     KParts::ReadOnlyPart *part = (KParts::ReadOnlyPart *)(this);
5485     // Find active part in our frame manager, in case we are a frameset
5486     // and keep doing that (in case of nested framesets).
5487     // Just realized we could also do this recursively, calling part->currentFrame()...
5488     while (part && part->inherits("KHTMLPart") &&
5489             static_cast<KHTMLPart *>(part)->d->m_frames.count() > 0) {
5490         KHTMLPart *frameset = static_cast<KHTMLPart *>(part);
5491         part = static_cast<KParts::ReadOnlyPart *>(frameset->partManager()->activePart());
5492         if (!part) {
5493             return frameset;
5494         }
5495     }
5496     return part;
5497 }
5498 
5499 bool KHTMLPart::frameExists(const QString &frameName)
5500 {
5501     FrameIt it = d->m_frames.find(frameName);
5502     if (it == d->m_frames.end()) {
5503         return false;
5504     }
5505 
5506     // WABA: We only return true if the child actually has a frame
5507     // set. Otherwise we might find our preloaded-selve.
5508     // This happens when we restore the frameset.
5509     return (!(*it)->m_partContainerElement.isNull());
5510 }
5511 
5512 void KHTMLPartPrivate::renameFrameForContainer(DOM::HTMLPartContainerElementImpl *cont,
5513         const QString &newName)
5514 {
5515     for (int i = 0; i < m_frames.size(); ++i) {
5516         khtml::ChildFrame *f = m_frames[i];
5517         if (f->m_partContainerElement.data() == cont) {
5518             f->m_name = newName;
5519         }
5520     }
5521 }
5522 
5523 KJSProxy *KHTMLPart::framejScript(KParts::ReadOnlyPart *framePart)
5524 {
5525     KHTMLPart *const kp = qobject_cast<KHTMLPart *>(framePart);
5526     if (kp) {
5527         return kp->jScript();
5528     }
5529 
5530     FrameIt it = d->m_frames.begin();
5531     const FrameIt itEnd = d->m_frames.end();
5532 
5533     for (; it != itEnd; ++it) {
5534         khtml::ChildFrame *frame = *it;
5535         if (framePart == frame->m_part.data()) {
5536             if (!frame->m_jscript) {
5537                 frame->m_jscript = new KJSProxy(frame);
5538             }
5539             return frame->m_jscript;
5540         }
5541     }
5542     return nullptr;
5543 }
5544 
5545 KHTMLPart *KHTMLPart::parentPart()
5546 {
5547     return qobject_cast<KHTMLPart *>(parent());
5548 }
5549 
5550 khtml::ChildFrame *KHTMLPart::recursiveFrameRequest(KHTMLPart *callingHtmlPart, const QUrl &url,
5551         const KParts::OpenUrlArguments &args,
5552         const KParts::BrowserArguments &browserArgs, bool callParent)
5553 {
5554 #ifdef DEBUG_FINDFRAME
5555     qCDebug(KHTML_LOG) << this << "frame = " << browserArgs.frameName << "url = " << url;
5556 #endif
5557     khtml::ChildFrame *childFrame;
5558     KHTMLPart *childPart = findFrameParent(callingHtmlPart, browserArgs.frameName, &childFrame);
5559     if (childPart) {
5560         if (childPart == this) {
5561             return childFrame;
5562         }
5563 
5564         childPart->requestObject(childFrame, url, args, browserArgs);
5565         return nullptr;
5566     }
5567 
5568     if (parentPart() && callParent) {
5569         khtml::ChildFrame *res = parentPart()->recursiveFrameRequest(callingHtmlPart, url, args, browserArgs, callParent);
5570 
5571         if (res) {
5572             parentPart()->requestObject(res, url, args, browserArgs);
5573         }
5574     }
5575 
5576     return nullptr;
5577 }
5578 
5579 #ifdef DEBUG_SAVESTATE
5580 static int s_saveStateIndentLevel = 0;
5581 #endif
5582 
5583 void KHTMLPart::saveState(QDataStream &stream)
5584 {
5585 #ifdef DEBUG_SAVESTATE
5586     QString indent = QString().leftJustified(s_saveStateIndentLevel * 4, ' ');
5587     const int indentLevel = s_saveStateIndentLevel++;
5588     qCDebug(KHTML_LOG) << indent << "saveState this=" << this << " '" << objectName() << "' saving URL " << url();
5589 #endif
5590 
5591     stream << url() << (qint32)d->m_view->contentsX() << (qint32)d->m_view->contentsY()
5592            << (qint32) d->m_view->contentsWidth() << (qint32) d->m_view->contentsHeight() << (qint32) d->m_view->marginWidth() << (qint32) d->m_view->marginHeight();
5593 
5594     // save link cursor position
5595     int focusNodeNumber;
5596     if (!d->m_focusNodeRestored) {
5597         focusNodeNumber = d->m_focusNodeNumber;
5598     } else if (d->m_doc && d->m_doc->focusNode()) {
5599         focusNodeNumber = d->m_doc->nodeAbsIndex(d->m_doc->focusNode());
5600     } else {
5601         focusNodeNumber = -1;
5602     }
5603     stream << focusNodeNumber;
5604 
5605     // Save the doc's cache id.
5606     stream << d->m_cacheId;
5607 
5608     // Save the state of the document (Most notably the state of any forms)
5609     QStringList docState;
5610     if (d->m_doc) {
5611         docState = d->m_doc->docState();
5612     }
5613     stream << d->m_encoding << d->m_sheetUsed << docState;
5614 
5615     stream << d->m_zoomFactor;
5616     stream << d->m_fontScaleFactor;
5617 
5618     stream << d->m_httpHeaders;
5619     stream << d->m_pageServices;
5620     stream << d->m_pageReferrer;
5621 
5622     // Save ssl data
5623     stream << d->m_ssl_in_use
5624            << d->m_ssl_peer_chain
5625            << d->m_ssl_peer_ip
5626            << d->m_ssl_cipher
5627            << d->m_ssl_protocol_version
5628            << d->m_ssl_cipher_used_bits
5629            << d->m_ssl_cipher_bits
5630            << d->m_ssl_cert_errors
5631            << d->m_ssl_parent_ip
5632            << d->m_ssl_parent_cert;
5633 
5634     QStringList frameNameLst, frameServiceTypeLst, frameServiceNameLst;
5635     QList<QUrl> frameURLLst;
5636     QList<QByteArray> frameStateBufferLst;
5637     QList<int> frameTypeLst;
5638 
5639     ConstFrameIt it = d->m_frames.constBegin();
5640     const ConstFrameIt end = d->m_frames.constEnd();
5641     for (; it != end; ++it) {
5642         if (!(*it)->m_part) {
5643             continue;
5644         }
5645 
5646         frameNameLst << (*it)->m_name;
5647         frameServiceTypeLst << (*it)->m_serviceType;
5648         frameServiceNameLst << (*it)->m_serviceName;
5649         frameURLLst << (*it)->m_part.data()->url();
5650 
5651         QByteArray state;
5652         QDataStream frameStream(&state, QIODevice::WriteOnly);
5653 
5654         if ((*it)->m_extension) {
5655             (*it)->m_extension.data()->saveState(frameStream);
5656         }
5657 
5658         frameStateBufferLst << state;
5659 
5660         frameTypeLst << int((*it)->m_type);
5661     }
5662 
5663     // Save frame data
5664     stream << (quint32) frameNameLst.count();
5665     stream << frameNameLst << frameServiceTypeLst << frameServiceNameLst << frameURLLst << frameStateBufferLst << frameTypeLst;
5666 #ifdef DEBUG_SAVESTATE
5667     s_saveStateIndentLevel = indentLevel;
5668 #endif
5669 }
5670 
5671 void KHTMLPart::restoreState(QDataStream &stream)
5672 {
5673     QUrl u;
5674     qint32 xOffset, yOffset, wContents, hContents, mWidth, mHeight;
5675     quint32 frameCount;
5676     QStringList frameNames, frameServiceTypes, docState, frameServiceNames;
5677     QList<int> frameTypes;
5678     QList<QUrl> frameURLs;
5679     QList<QByteArray> frameStateBuffers;
5680     QList<int> fSizes;
5681     QString encoding, sheetUsed;
5682     long old_cacheId = d->m_cacheId;
5683 
5684     stream >> u >> xOffset >> yOffset >> wContents >> hContents >> mWidth >> mHeight;
5685 
5686     d->m_view->setMarginWidth(mWidth);
5687     d->m_view->setMarginHeight(mHeight);
5688 
5689     // restore link cursor position
5690     // nth node is active. value is set in checkCompleted()
5691     stream >> d->m_focusNodeNumber;
5692     d->m_focusNodeRestored = false;
5693 
5694     stream >> d->m_cacheId;
5695 
5696     stream >> encoding >> sheetUsed >> docState;
5697 
5698     d->m_encoding = encoding;
5699     d->m_sheetUsed = sheetUsed;
5700 
5701     int zoomFactor;
5702     stream >> zoomFactor;
5703     setZoomFactor(zoomFactor);
5704 
5705     int fontScaleFactor;
5706     stream >> fontScaleFactor;
5707     setFontScaleFactor(fontScaleFactor);
5708 
5709     stream >> d->m_httpHeaders;
5710     stream >> d->m_pageServices;
5711     stream >> d->m_pageReferrer;
5712 
5713     // Restore ssl data
5714     stream >> d->m_ssl_in_use
5715            >> d->m_ssl_peer_chain
5716            >> d->m_ssl_peer_ip
5717            >> d->m_ssl_cipher
5718            >> d->m_ssl_protocol_version
5719            >> d->m_ssl_cipher_used_bits
5720            >> d->m_ssl_cipher_bits
5721            >> d->m_ssl_cert_errors
5722            >> d->m_ssl_parent_ip
5723            >> d->m_ssl_parent_cert;
5724 
5725     setPageSecurity(d->m_ssl_in_use ? Encrypted : NotCrypted);
5726 
5727     stream >> frameCount >> frameNames >> frameServiceTypes >> frameServiceNames
5728            >> frameURLs >> frameStateBuffers >> frameTypes;
5729 
5730     d->m_bComplete = false;
5731     d->m_bLoadEventEmitted = false;
5732 
5733 //   qCDebug(KHTML_LOG) << "docState.count() = " << docState.count();
5734 //   qCDebug(KHTML_LOG) << "m_url " << url() << " <-> " << u;
5735 //   qCDebug(KHTML_LOG) << "m_frames.count() " << d->m_frames.count() << " <-> " << frameCount;
5736 
5737     if (d->m_cacheId == old_cacheId && signed(frameCount) == d->m_frames.count()) {
5738         // Partial restore
5739         d->m_redirectionTimer.stop();
5740 
5741         FrameIt fIt = d->m_frames.begin();
5742         const FrameIt fEnd = d->m_frames.end();
5743 
5744         for (; fIt != fEnd; ++fIt) {
5745             (*fIt)->m_bCompleted = false;
5746         }
5747 
5748         fIt = d->m_frames.begin();
5749 
5750         QStringList::ConstIterator fNameIt = frameNames.constBegin();
5751         QStringList::ConstIterator fServiceTypeIt = frameServiceTypes.constBegin();
5752         QStringList::ConstIterator fServiceNameIt = frameServiceNames.constBegin();
5753         QList<QUrl>::ConstIterator fURLIt = frameURLs.constBegin();
5754         QList<QByteArray>::ConstIterator fBufferIt = frameStateBuffers.constBegin();
5755         QList<int>::ConstIterator fFrameTypeIt = frameTypes.constBegin();
5756 
5757         for (; fIt != fEnd; ++fIt, ++fNameIt, ++fServiceTypeIt, ++fServiceNameIt, ++fURLIt, ++fBufferIt, ++fFrameTypeIt) {
5758             khtml::ChildFrame *const child = *fIt;
5759 
5760 //      qCDebug(KHTML_LOG) <<  *fNameIt  << " ---- " <<  *fServiceTypeIt;
5761 
5762             if (child->m_name != *fNameIt || child->m_serviceType != *fServiceTypeIt) {
5763                 child->m_bPreloaded = true;
5764                 child->m_name = *fNameIt;
5765                 child->m_serviceName = *fServiceNameIt;
5766                 child->m_type = static_cast<khtml::ChildFrame::Type>(*fFrameTypeIt);
5767                 processObjectRequest(child, *fURLIt, *fServiceTypeIt);
5768             }
5769             if (child->m_part) {
5770                 child->m_bCompleted = false;
5771                 if (child->m_extension && !(*fBufferIt).isEmpty()) {
5772                     QDataStream frameStream(*fBufferIt);
5773                     child->m_extension.data()->restoreState(frameStream);
5774                 } else {
5775                     child->m_part.data()->openUrl(*fURLIt);
5776                 }
5777             }
5778         }
5779 
5780         KParts::OpenUrlArguments args(arguments());
5781         args.setXOffset(xOffset);
5782         args.setYOffset(yOffset);
5783         setArguments(args);
5784 
5785         KParts::BrowserArguments browserArgs(d->m_extension->browserArguments());
5786         browserArgs.docState = docState;
5787         d->m_extension->setBrowserArguments(browserArgs);
5788 
5789         d->m_view->resizeContents(wContents, hContents);
5790         d->m_view->setContentsPos(xOffset, yOffset);
5791 
5792         setUrl(u);
5793     } else {
5794         // Full restore.
5795         closeUrl();
5796         // We must force a clear because we want to be sure to delete all
5797         // frames.
5798         d->m_bCleared = false;
5799         clear();
5800         d->m_encoding = encoding;
5801         d->m_sheetUsed = sheetUsed;
5802 
5803         QStringList::ConstIterator fNameIt = frameNames.constBegin();
5804         const QStringList::ConstIterator fNameEnd = frameNames.constEnd();
5805 
5806         QStringList::ConstIterator fServiceTypeIt = frameServiceTypes.constBegin();
5807         QStringList::ConstIterator fServiceNameIt = frameServiceNames.constBegin();
5808         QList<QUrl>::ConstIterator fURLIt = frameURLs.constBegin();
5809         QList<QByteArray>::ConstIterator fBufferIt = frameStateBuffers.constBegin();
5810         QList<int>::ConstIterator fFrameTypeIt = frameTypes.constBegin();
5811 
5812         for (; fNameIt != fNameEnd; ++fNameIt, ++fServiceTypeIt, ++fServiceNameIt, ++fURLIt, ++fBufferIt, ++fFrameTypeIt) {
5813             khtml::ChildFrame *const newChild = new khtml::ChildFrame;
5814             newChild->m_bPreloaded = true;
5815             newChild->m_name = *fNameIt;
5816             newChild->m_serviceName = *fServiceNameIt;
5817             newChild->m_type = static_cast<khtml::ChildFrame::Type>(*fFrameTypeIt);
5818 
5819 //      qCDebug(KHTML_LOG) << *fNameIt << " ---- " << *fServiceTypeIt;
5820 
5821             const FrameIt childFrame = d->m_frames.insert(d->m_frames.end(), newChild);
5822 
5823             processObjectRequest(*childFrame, *fURLIt, *fServiceTypeIt);
5824 
5825             (*childFrame)->m_bPreloaded = true;
5826 
5827             if ((*childFrame)->m_part) {
5828                 if ((*childFrame)->m_extension && !(*fBufferIt).isEmpty()) {
5829                     QDataStream frameStream(*fBufferIt);
5830                     (*childFrame)->m_extension.data()->restoreState(frameStream);
5831                 } else {
5832                     (*childFrame)->m_part.data()->openUrl(*fURLIt);
5833                 }
5834             }
5835         }
5836 
5837         KParts::OpenUrlArguments args(arguments());
5838         args.setXOffset(xOffset);
5839         args.setYOffset(yOffset);
5840         setArguments(args);
5841 
5842         KParts::BrowserArguments browserArgs(d->m_extension->browserArguments());
5843         browserArgs.docState = docState;
5844         d->m_extension->setBrowserArguments(browserArgs);
5845 
5846         if (!KHTMLPageCache::self()->isComplete(d->m_cacheId)) {
5847             d->m_restored = true;
5848             openUrl(u);
5849             d->m_restored = false;
5850         } else {
5851             restoreURL(u);
5852         }
5853     }
5854 
5855 }
5856 
5857 void KHTMLPart::show()
5858 {
5859     if (widget()) {
5860         widget()->show();
5861     }
5862 }
5863 
5864 void KHTMLPart::hide()
5865 {
5866     if (widget()) {
5867         widget()->hide();
5868     }
5869 }
5870 
5871 DOM::Node KHTMLPart::nodeUnderMouse() const
5872 {
5873     return d->m_view->nodeUnderMouse();
5874 }
5875 
5876 DOM::Node KHTMLPart::nonSharedNodeUnderMouse() const
5877 {
5878     return d->m_view->nonSharedNodeUnderMouse();
5879 }
5880 
5881 void KHTMLPart::emitSelectionChanged()
5882 {
5883     // Don't emit signals about our selection if this is a frameset;
5884     // the active frame has the selection (#187403)
5885     if (!d->m_activeFrame) {
5886         emit d->m_extension->enableAction("copy", hasSelection());
5887         emit d->m_extension->selectionInfo(selectedText());
5888         emit selectionChanged();
5889     }
5890 }
5891 
5892 int KHTMLPart::zoomFactor() const
5893 {
5894     return d->m_zoomFactor;
5895 }
5896 
5897 // ### make the list configurable ?
5898 static const int zoomSizes[] = { 20, 40, 60, 80, 90, 95, 100, 105, 110, 120, 140, 160, 180, 200, 250, 300 };
5899 static const int zoomSizeCount = (sizeof(zoomSizes) / sizeof(int));
5900 static const int minZoom = 20;
5901 static const int maxZoom = 300;
5902 
5903 // My idea of useful stepping ;-) (LS)
5904 extern const int KHTML_NO_EXPORT fastZoomSizes[] = { 20, 50, 75, 90, 100, 120, 150, 200, 300 };
5905 extern const int KHTML_NO_EXPORT fastZoomSizeCount = sizeof fastZoomSizes / sizeof fastZoomSizes[0];
5906 
5907 void KHTMLPart::slotIncZoom()
5908 {
5909     zoomIn(zoomSizes, zoomSizeCount);
5910 }
5911 
5912 void KHTMLPart::slotDecZoom()
5913 {
5914     zoomOut(zoomSizes, zoomSizeCount);
5915 }
5916 
5917 void KHTMLPart::slotIncZoomFast()
5918 {
5919     zoomIn(fastZoomSizes, fastZoomSizeCount);
5920 }
5921 
5922 void KHTMLPart::slotDecZoomFast()
5923 {
5924     zoomOut(fastZoomSizes, fastZoomSizeCount);
5925 }
5926 
5927 void KHTMLPart::zoomIn(const int stepping[], int count)
5928 {
5929     int zoomFactor = d->m_zoomFactor;
5930 
5931     if (zoomFactor < maxZoom) {
5932         // find the entry nearest to the given zoomsizes
5933         for (int i = 0; i < count; ++i)
5934             if (stepping[i] > zoomFactor) {
5935                 zoomFactor = stepping[i];
5936                 break;
5937             }
5938         setZoomFactor(zoomFactor);
5939     }
5940 }
5941 
5942 void KHTMLPart::zoomOut(const int stepping[], int count)
5943 {
5944     int zoomFactor = d->m_zoomFactor;
5945     if (zoomFactor > minZoom) {
5946         // find the entry nearest to the given zoomsizes
5947         for (int i = count - 1; i >= 0; --i)
5948             if (stepping[i] < zoomFactor) {
5949                 zoomFactor = stepping[i];
5950                 break;
5951             }
5952         setZoomFactor(zoomFactor);
5953     }
5954 }
5955 
5956 void KHTMLPart::setZoomFactor(int percent)
5957 {
5958     // ### zooming under 100% is majorly botched,
5959     //     so disable that for now.
5960     if (percent < 100) {
5961         percent = 100;
5962     }
5963     // ### if (percent < minZoom) percent = minZoom;
5964 
5965     if (percent > maxZoom) {
5966         percent = maxZoom;
5967     }
5968     if (d->m_zoomFactor == percent) {
5969         return;
5970     }
5971     d->m_zoomFactor = percent;
5972 
5973     updateZoomFactor();
5974 }
5975 
5976 void KHTMLPart::updateZoomFactor()
5977 {
5978     if (d->m_view) {
5979         QApplication::setOverrideCursor(Qt::WaitCursor);
5980         d->m_view->setZoomLevel(d->m_zoomFactor);
5981         QApplication::restoreOverrideCursor();
5982     }
5983 
5984     ConstFrameIt it = d->m_frames.constBegin();
5985     const ConstFrameIt end = d->m_frames.constEnd();
5986     for (; it != end; ++it) {
5987         if (KHTMLPart *p = qobject_cast<KHTMLPart *>((*it)->m_part.data())) {
5988             p->setZoomFactor(d->m_zoomFactor);
5989         }
5990     }
5991 
5992     if (d->m_guiProfile == BrowserViewGUI) {
5993         d->m_paDecZoomFactor->setEnabled(d->m_zoomFactor > minZoom);
5994         d->m_paIncZoomFactor->setEnabled(d->m_zoomFactor < maxZoom);
5995     }
5996 }
5997 
5998 void KHTMLPart::slotIncFontSize()
5999 {
6000     incFontSize(zoomSizes, zoomSizeCount);
6001 }
6002 
6003 void KHTMLPart::slotDecFontSize()
6004 {
6005     decFontSize(zoomSizes, zoomSizeCount);
6006 }
6007 
6008 void KHTMLPart::slotIncFontSizeFast()
6009 {
6010     incFontSize(fastZoomSizes, fastZoomSizeCount);
6011 }
6012 
6013 void KHTMLPart::slotDecFontSizeFast()
6014 {
6015     decFontSize(fastZoomSizes, fastZoomSizeCount);
6016 }
6017 
6018 void KHTMLPart::incFontSize(const int stepping[], int count)
6019 {
6020     int zoomFactor = d->m_fontScaleFactor;
6021 
6022     if (zoomFactor < maxZoom) {
6023         // find the entry nearest to the given zoomsizes
6024         for (int i = 0; i < count; ++i)
6025             if (stepping[i] > zoomFactor) {
6026                 zoomFactor = stepping[i];
6027                 break;
6028             }
6029         setFontScaleFactor(zoomFactor);
6030     }
6031 }
6032 
6033 void KHTMLPart::decFontSize(const int stepping[], int count)
6034 {
6035     int zoomFactor = d->m_fontScaleFactor;
6036     if (zoomFactor > minZoom) {
6037         // find the entry nearest to the given zoomsizes
6038         for (int i = count - 1; i >= 0; --i)
6039             if (stepping[i] < zoomFactor) {
6040                 zoomFactor = stepping[i];
6041                 break;
6042             }
6043         setFontScaleFactor(zoomFactor);
6044     }
6045 }
6046 
6047 void KHTMLPart::setFontScaleFactor(int percent)
6048 {
6049     if (percent < minZoom) {
6050         percent = minZoom;
6051     }
6052     if (percent > maxZoom) {
6053         percent = maxZoom;
6054     }
6055     if (d->m_fontScaleFactor == percent) {
6056         return;
6057     }
6058     d->m_fontScaleFactor = percent;
6059 
6060     if (d->m_view && d->m_doc) {
6061         QApplication::setOverrideCursor(Qt::WaitCursor);
6062         if (d->m_doc->styleSelector()) {
6063             d->m_doc->styleSelector()->computeFontSizes(d->m_doc->logicalDpiY(), d->m_fontScaleFactor);
6064         }
6065         d->m_doc->recalcStyle(NodeImpl::Force);
6066         QApplication::restoreOverrideCursor();
6067     }
6068 
6069     ConstFrameIt it = d->m_frames.constBegin();
6070     const ConstFrameIt end = d->m_frames.constEnd();
6071     for (; it != end; ++it) {
6072         if (KHTMLPart *p = qobject_cast<KHTMLPart *>((*it)->m_part.data())) {
6073             p->setFontScaleFactor(d->m_fontScaleFactor);
6074         }
6075     }
6076 }
6077 
6078 int KHTMLPart::fontScaleFactor() const
6079 {
6080     return d->m_fontScaleFactor;
6081 }
6082 
6083 void KHTMLPart::slotZoomView(int delta)
6084 {
6085     if (delta < 0) {
6086         slotIncZoom();
6087     } else {
6088         slotDecZoom();
6089     }
6090 }
6091 
6092 void KHTMLPart::setStatusBarText(const QString &text, StatusBarPriority p)
6093 {
6094     if (!d->m_statusMessagesEnabled) {
6095         return;
6096     }
6097 
6098     d->m_statusBarText[p] = text;
6099 
6100     // shift handling ?
6101     QString tobe = d->m_statusBarText[BarHoverText];
6102     if (tobe.isEmpty()) {
6103         tobe = d->m_statusBarText[BarOverrideText];
6104     }
6105     if (tobe.isEmpty()) {
6106         tobe = d->m_statusBarText[BarDefaultText];
6107         if (!tobe.isEmpty() && d->m_jobspeed) {
6108             tobe += " ";
6109         }
6110         if (d->m_jobspeed) {
6111             tobe += i18n("(%1/s)",  KIO::convertSize(d->m_jobspeed));
6112         }
6113     }
6114     tobe = "<qt>" + tobe;
6115 
6116     emit ReadOnlyPart::setStatusBarText(tobe);
6117 }
6118 
6119 void KHTMLPart::setJSStatusBarText(const QString &text)
6120 {
6121     setStatusBarText(text, BarOverrideText);
6122 }
6123 
6124 void KHTMLPart::setJSDefaultStatusBarText(const QString &text)
6125 {
6126     setStatusBarText(text, BarDefaultText);
6127 }
6128 
6129 QString KHTMLPart::jsStatusBarText() const
6130 {
6131     return d->m_statusBarText[BarOverrideText];
6132 }
6133 
6134 QString KHTMLPart::jsDefaultStatusBarText() const
6135 {
6136     return d->m_statusBarText[BarDefaultText];
6137 }
6138 
6139 QString KHTMLPart::referrer() const
6140 {
6141     return d->m_referrer;
6142 }
6143 
6144 QString KHTMLPart::pageReferrer() const
6145 {
6146     QUrl referrerURL = QUrl(d->m_pageReferrer);
6147     if (referrerURL.isValid()) {
6148         QString protocol = referrerURL.scheme();
6149 
6150         if ((protocol == "http") ||
6151                 ((protocol == "https") && (url().scheme() == "https"))) {
6152             referrerURL.setFragment(QString());
6153             referrerURL.setUserName(QString());
6154             referrerURL.setPassword(QString());
6155             return referrerURL.toString();
6156         }
6157     }
6158 
6159     return QString();
6160 }
6161 
6162 QString KHTMLPart::lastModified() const
6163 {
6164     if (d->m_lastModified.isEmpty() && url().isLocalFile()) {
6165         // Local file: set last-modified from the file's mtime.
6166         // Done on demand to save time when this isn't needed - but can lead
6167         // to slightly wrong results if updating the file on disk w/o reloading.
6168         QDateTime lastModif = QFileInfo(url().toLocalFile()).lastModified();
6169         d->m_lastModified = lastModif.toString(Qt::LocalDate);
6170     }
6171     //qCDebug(KHTML_LOG) << d->m_lastModified;
6172     return d->m_lastModified;
6173 }
6174 
6175 void KHTMLPart::slotLoadImages()
6176 {
6177     if (d->m_doc) {
6178         d->m_doc->docLoader()->setAutoloadImages(!d->m_doc->docLoader()->autoloadImages());
6179     }
6180 
6181     ConstFrameIt it = d->m_frames.constBegin();
6182     const ConstFrameIt end = d->m_frames.constEnd();
6183     for (; it != end; ++it) {
6184         if (KHTMLPart *p = qobject_cast<KHTMLPart *>((*it)->m_part.data())) {
6185             p->slotLoadImages();
6186         }
6187     }
6188 }
6189 
6190 void KHTMLPart::reparseConfiguration()
6191 {
6192     KHTMLSettings *settings = KHTMLGlobal::defaultHTMLSettings();
6193     settings->init();
6194 
6195     setAutoloadImages(settings->autoLoadImages());
6196     if (d->m_doc) {
6197         d->m_doc->docLoader()->setShowAnimations(settings->showAnimations());
6198     }
6199 
6200     d->m_bOpenMiddleClick = settings->isOpenMiddleClickEnabled();
6201     d->m_bJScriptEnabled = settings->isJavaScriptEnabled(url().host());
6202     setDebugScript(settings->isJavaScriptDebugEnabled());
6203     d->m_bJavaEnabled = settings->isJavaEnabled(url().host());
6204     d->m_bPluginsEnabled = settings->isPluginsEnabled(url().host());
6205     d->m_metaRefreshEnabled = settings->isAutoDelayedActionsEnabled();
6206 
6207     delete d->m_settings;
6208     d->m_settings = new KHTMLSettings(*KHTMLGlobal::defaultHTMLSettings());
6209 
6210     QApplication::setOverrideCursor(Qt::WaitCursor);
6211     khtml::CSSStyleSelector::reparseConfiguration();
6212     if (d->m_doc) {
6213         d->m_doc->updateStyleSelector();
6214     }
6215     QApplication::restoreOverrideCursor();
6216 
6217     if (d->m_view) {
6218         KHTMLSettings::KSmoothScrollingMode ssm = d->m_settings->smoothScrolling();
6219         if (ssm == KHTMLSettings::KSmoothScrollingDisabled) {
6220             d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMDisabled);
6221         } else if (ssm == KHTMLSettings::KSmoothScrollingWhenEfficient) {
6222             d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMWhenEfficient);
6223         } else {
6224             d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMEnabled);
6225         }
6226     }
6227 
6228     if (KHTMLGlobal::defaultHTMLSettings()->isAdFilterEnabled()) {
6229         runAdFilter();
6230     }
6231 }
6232 
6233 QStringList KHTMLPart::frameNames() const
6234 {
6235     QStringList res;
6236 
6237     ConstFrameIt it = d->m_frames.constBegin();
6238     const ConstFrameIt end = d->m_frames.constEnd();
6239     for (; it != end; ++it)
6240         if (!(*it)->m_bPreloaded && (*it)->m_part) {
6241             res += (*it)->m_name;
6242         }
6243 
6244     return res;
6245 }
6246 
6247 QList<KParts::ReadOnlyPart *> KHTMLPart::frames() const
6248 {
6249     QList<KParts::ReadOnlyPart *> res;
6250 
6251     ConstFrameIt it = d->m_frames.constBegin();
6252     const ConstFrameIt end = d->m_frames.constEnd();
6253     for (; it != end; ++it)
6254         if (!(*it)->m_bPreloaded && (*it)->m_part) // ### TODO: make sure that we always create an empty
6255             // KHTMLPart for frames so this never happens.
6256         {
6257             res.append((*it)->m_part.data());
6258         }
6259 
6260     return res;
6261 }
6262 
6263 bool KHTMLPart::openUrlInFrame(const QUrl &url, const KParts::OpenUrlArguments &args, const KParts::BrowserArguments &browserArgs)
6264 {
6265     // qCDebug(KHTML_LOG) << this << url;
6266     FrameIt it = d->m_frames.find(browserArgs.frameName);
6267 
6268     if (it == d->m_frames.end()) {
6269         return false;
6270     }
6271 
6272     // Inform someone that we are about to show something else.
6273     if (!browserArgs.lockHistory()) {
6274         emit d->m_extension->openUrlNotify();
6275     }
6276 
6277     requestObject(*it, url, args, browserArgs);
6278 
6279     return true;
6280 }
6281 
6282 void KHTMLPart::setDNDEnabled(bool b)
6283 {
6284     d->m_bDnd = b;
6285 }
6286 
6287 bool KHTMLPart::dndEnabled() const
6288 {
6289     return d->m_bDnd;
6290 }
6291 
6292 void KHTMLPart::customEvent(QEvent *event)
6293 {
6294     if (khtml::MousePressEvent::test(event)) {
6295         khtmlMousePressEvent(static_cast<khtml::MousePressEvent *>(event));
6296         return;
6297     }
6298 
6299     if (khtml::MouseDoubleClickEvent::test(event)) {
6300         khtmlMouseDoubleClickEvent(static_cast<khtml::MouseDoubleClickEvent *>(event));
6301         return;
6302     }
6303 
6304     if (khtml::MouseMoveEvent::test(event)) {
6305         khtmlMouseMoveEvent(static_cast<khtml::MouseMoveEvent *>(event));
6306         return;
6307     }
6308 
6309     if (khtml::MouseReleaseEvent::test(event)) {
6310         khtmlMouseReleaseEvent(static_cast<khtml::MouseReleaseEvent *>(event));
6311         return;
6312     }
6313 
6314     if (khtml::DrawContentsEvent::test(event)) {
6315         khtmlDrawContentsEvent(static_cast<khtml::DrawContentsEvent *>(event));
6316         return;
6317     }
6318 
6319     KParts::ReadOnlyPart::customEvent(event);
6320 }
6321 
6322 bool KHTMLPart::isPointInsideSelection(int x, int y)
6323 {
6324     // Treat a collapsed selection like no selection.
6325     if (d->editor_context.m_selection.state() == Selection::CARET) {
6326         return false;
6327     }
6328     if (!xmlDocImpl()->renderer()) {
6329         return false;
6330     }
6331 
6332     khtml::RenderObject::NodeInfo nodeInfo(true, true);
6333     xmlDocImpl()->renderer()->layer()->nodeAtPoint(nodeInfo, x, y);
6334     NodeImpl *innerNode = nodeInfo.innerNode();
6335     if (!innerNode || !innerNode->renderer()) {
6336         return false;
6337     }
6338 
6339     return innerNode->isPointInsideSelection(x, y, d->editor_context.m_selection);
6340 }
6341 
6342 /** returns the position of the first inline text box of the line at
6343  * coordinate y in renderNode
6344  *
6345  * This is a helper function for line-by-line text selection.
6346  */
6347 static bool firstRunAt(khtml::RenderObject *renderNode, int y, NodeImpl *&startNode, long &startOffset)
6348 {
6349     for (khtml::RenderObject *n = renderNode; n; n = n->nextSibling()) {
6350         if (n->isText()) {
6351             khtml::RenderText *const textRenderer = static_cast<khtml::RenderText *>(n);
6352             for (khtml::InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
6353                 if (box->m_y == y && textRenderer->element()) {
6354                     startNode = textRenderer->element();
6355                     startOffset = box->m_start;
6356                     return true;
6357                 }
6358             }
6359         }
6360 
6361         if (firstRunAt(n->firstChild(), y, startNode, startOffset)) {
6362             return true;
6363         }
6364     }
6365 
6366     return false;
6367 }
6368 
6369 /** returns the position of the last inline text box of the line at
6370  * coordinate y in renderNode
6371  *
6372  * This is a helper function for line-by-line text selection.
6373  */
6374 static bool lastRunAt(khtml::RenderObject *renderNode, int y, NodeImpl *&endNode, long &endOffset)
6375 {
6376     khtml::RenderObject *n = renderNode;
6377     if (!n) {
6378         return false;
6379     }
6380     khtml::RenderObject *next;
6381     while ((next = n->nextSibling())) {
6382         n = next;
6383     }
6384 
6385     while (1) {
6386         if (lastRunAt(n->firstChild(), y, endNode, endOffset)) {
6387             return true;
6388         }
6389 
6390         if (n->isText()) {
6391             khtml::RenderText *const textRenderer =  static_cast<khtml::RenderText *>(n);
6392             for (khtml::InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
6393                 if (box->m_y == y && textRenderer->element()) {
6394                     endNode = textRenderer->element();
6395                     endOffset = box->m_start + box->m_len;
6396                     return true;
6397                 }
6398             }
6399         }
6400 
6401         if (n == renderNode) {
6402             return false;
6403         }
6404 
6405         n = n->previousSibling();
6406     }
6407 }
6408 
6409 void KHTMLPart::handleMousePressEventDoubleClick(khtml::MouseDoubleClickEvent *event)
6410 {
6411     QMouseEvent *mouse = event->qmouseEvent();
6412     DOM::Node innerNode = event->innerNode();
6413 
6414     Selection selection;
6415 
6416     if (mouse->button() == Qt::LeftButton && !innerNode.isNull() && innerNode.handle()->renderer() &&
6417             innerNode.handle()->renderer()->shouldSelect()) {
6418         Position pos(innerNode.handle()->positionForCoordinates(event->x(), event->y()).position());
6419         if (pos.node() && (pos.node()->nodeType() == Node::TEXT_NODE || pos.node()->nodeType() == Node::CDATA_SECTION_NODE)) {
6420             selection.moveTo(pos);
6421             selection.expandUsingGranularity(Selection::WORD);
6422         }
6423     }
6424 
6425     if (selection.state() != Selection::CARET) {
6426         d->editor_context.beginSelectingText(Selection::WORD);
6427     }
6428 
6429     setCaret(selection);
6430     startAutoScroll();
6431 }
6432 
6433 void KHTMLPart::handleMousePressEventTripleClick(khtml::MouseDoubleClickEvent *event)
6434 {
6435     QMouseEvent *mouse = event->qmouseEvent();
6436     DOM::Node innerNode = event->innerNode();
6437 
6438     Selection selection;
6439 
6440     if (mouse->button() == Qt::LeftButton && !innerNode.isNull() && innerNode.handle()->renderer() &&
6441             innerNode.handle()->renderer()->shouldSelect()) {
6442         Position pos(innerNode.handle()->positionForCoordinates(event->x(), event->y()).position());
6443         if (pos.node() && (pos.node()->nodeType() == Node::TEXT_NODE || pos.node()->nodeType() == Node::CDATA_SECTION_NODE)) {
6444             selection.moveTo(pos);
6445             selection.expandUsingGranularity(Selection::LINE);
6446         }
6447     }
6448 
6449     if (selection.state() != Selection::CARET) {
6450         d->editor_context.beginSelectingText(Selection::LINE);
6451     }
6452 
6453     setCaret(selection);
6454     startAutoScroll();
6455 }
6456 
6457 void KHTMLPart::handleMousePressEventSingleClick(khtml::MousePressEvent *event)
6458 {
6459     QMouseEvent *mouse = event->qmouseEvent();
6460     DOM::Node innerNode = event->innerNode();
6461 
6462     if (mouse->button() == Qt::LeftButton) {
6463         Selection sel;
6464 
6465         if (!innerNode.isNull() && innerNode.handle()->renderer() &&
6466                 innerNode.handle()->renderer()->shouldSelect()) {
6467             bool extendSelection = mouse->modifiers() & Qt::ShiftModifier;
6468 
6469             // Don't restart the selection when the mouse is pressed on an
6470             // existing selection so we can allow for text dragging.
6471             if (!extendSelection && isPointInsideSelection(event->x(), event->y())) {
6472                 return;
6473             }
6474             Position pos(innerNode.handle()->positionForCoordinates(event->x(), event->y()).position());
6475             if (pos.isEmpty()) {
6476                 pos = Position(innerNode.handle(), innerNode.handle()->caretMinOffset());
6477             }
6478             // qCDebug(KHTML_LOG) << event->x() << event->y() << pos;
6479 
6480             sel = caret();
6481             if (extendSelection && sel.notEmpty()) {
6482                 sel.clearModifyBias();
6483                 sel.setExtent(pos);
6484                 if (d->editor_context.m_selectionGranularity != Selection::CHARACTER) {
6485                     sel.expandUsingGranularity(d->editor_context.m_selectionGranularity);
6486                 }
6487                 d->editor_context.m_beganSelectingText = true;
6488             } else {
6489                 sel = pos;
6490                 d->editor_context.m_selectionGranularity = Selection::CHARACTER;
6491             }
6492         }
6493 
6494         setCaret(sel);
6495         startAutoScroll();
6496     }
6497 }
6498 
6499 void KHTMLPart::khtmlMousePressEvent(khtml::MousePressEvent *event)
6500 {
6501     DOM::DOMString url = event->url();
6502     QMouseEvent *_mouse = event->qmouseEvent();
6503     DOM::Node innerNode = event->innerNode();
6504     d->m_mousePressNode = innerNode;
6505 
6506     d->m_dragStartPos = QPoint(event->x(), event->y());
6507 
6508     if (!event->url().isNull()) {
6509         d->m_strSelectedURL = event->url().string();
6510         d->m_strSelectedURLTarget = event->target().string();
6511     } else {
6512         d->m_strSelectedURL.clear();
6513         d->m_strSelectedURLTarget.clear();
6514     }
6515 
6516     if (_mouse->button() == Qt::LeftButton ||
6517             _mouse->button() == Qt::MidButton) {
6518         d->m_bMousePressed = true;
6519 
6520 #ifdef KHTML_NO_SELECTION
6521         d->m_dragLastPos = _mouse->globalPos();
6522 #else
6523         if (_mouse->button() == Qt::LeftButton) {
6524             if ((!d->m_strSelectedURL.isNull() && !isEditable())
6525                     || (!d->m_mousePressNode.isNull() && d->m_mousePressNode.elementId() == ID_IMG)) {
6526                 return;
6527             }
6528 
6529             d->editor_context.m_beganSelectingText = false;
6530 
6531             handleMousePressEventSingleClick(event);
6532         }
6533 #endif
6534     }
6535 
6536     if (_mouse->button() == Qt::RightButton) {
6537         popupMenu(d->m_strSelectedURL);
6538         // might be deleted, don't touch "this"
6539     }
6540 }
6541 
6542 void KHTMLPart::khtmlMouseDoubleClickEvent(khtml::MouseDoubleClickEvent *event)
6543 {
6544     QMouseEvent *_mouse = event->qmouseEvent();
6545     if (_mouse->button() == Qt::LeftButton) {
6546         d->m_bMousePressed = true;
6547         d->editor_context.m_beganSelectingText = false;
6548 
6549         if (event->clickCount() == 2) {
6550             handleMousePressEventDoubleClick(event);
6551             return;
6552         }
6553 
6554         if (event->clickCount() >= 3) {
6555             handleMousePressEventTripleClick(event);
6556             return;
6557         }
6558     }
6559 }
6560 
6561 #ifndef KHTML_NO_SELECTION
6562 bool KHTMLPart::isExtendingSelection() const
6563 {
6564     // This is it, the whole detection. khtmlMousePressEvent only sets this
6565     // on LMB or MMB, but never on RMB. As text selection doesn't work for MMB,
6566     // it's sufficient to only rely on this flag to detect selection extension.
6567     return d->editor_context.m_beganSelectingText;
6568 }
6569 
6570 void KHTMLPart::extendSelectionTo(int x, int y, const DOM::Node &innerNode)
6571 {
6572     // handle making selection
6573     Position pos(innerNode.handle()->positionForCoordinates(x, y).position());
6574 
6575     // Don't modify the selection if we're not on a node.
6576     if (pos.isEmpty()) {
6577         return;
6578     }
6579 
6580     // Restart the selection if this is the first mouse move. This work is usually
6581     // done in khtmlMousePressEvent, but not if the mouse press was on an existing selection.
6582     Selection sel = caret();
6583     sel.clearModifyBias();
6584     if (!d->editor_context.m_beganSelectingText) {
6585         // We are beginning a selection during press-drag, when the original click
6586         // wasn't appropriate for one. Make sure to set the granularity.
6587         d->editor_context.beginSelectingText(Selection::CHARACTER);
6588         sel.moveTo(pos);
6589     }
6590 
6591     sel.setExtent(pos);
6592     if (d->editor_context.m_selectionGranularity != Selection::CHARACTER) {
6593         sel.expandUsingGranularity(d->editor_context.m_selectionGranularity);
6594     }
6595     setCaret(sel);
6596 
6597 }
6598 #endif // KHTML_NO_SELECTION
6599 
6600 bool KHTMLPart::handleMouseMoveEventDrag(khtml::MouseMoveEvent *event)
6601 {
6602 #ifdef QT_NO_DRAGANDDROP
6603     return false;
6604 #else
6605     if (!dndEnabled()) {
6606         return false;
6607     }
6608 
6609     if ((d->m_bMousePressed &&
6610             ((!d->m_strSelectedURL.isEmpty() && !isEditable())
6611              || (!d->m_mousePressNode.isNull() && d->m_mousePressNode.elementId() == ID_IMG)))
6612             && (d->m_dragStartPos - QPoint(event->x(), event->y())).manhattanLength() > QApplication::startDragDistance()) {
6613 
6614         const DOM::DOMString url = event->url();
6615         DOM::NodeImpl *innerNodeImpl = event->innerNode().handle();
6616 
6617         QPixmap pix;
6618         HTMLImageElementImpl *img = nullptr;
6619         QUrl u;
6620 
6621         // qDebug("****************** Event URL: %s", url.string().toLatin1().constData());
6622         // qDebug("****************** Event Target: %s", target.string().toLatin1().constData());
6623 
6624         // Normal image...
6625         if (url.isEmpty() && innerNodeImpl && innerNodeImpl->id() == ID_IMG) {
6626             img = static_cast<HTMLImageElementImpl *>(innerNodeImpl);
6627             u = completeURL(img->getAttribute(ATTR_SRC).trimSpaces().string());
6628             pix = KIconLoader::global()->loadIcon("image-x-generic", KIconLoader::Desktop);
6629         } else {
6630             // Text or image link...
6631             u = completeURL(d->m_strSelectedURL);
6632             pix = KIO::pixmapForUrl(u, 0, KIconLoader::Desktop, KIconLoader::SizeMedium);
6633         }
6634 
6635         u.setPassword(QString());
6636 
6637         QDrag *drag = new QDrag(d->m_view->viewport());
6638         QMap<QString, QString> metaDataMap;
6639         if (!d->m_referrer.isEmpty()) {
6640             metaDataMap.insert("referrer", d->m_referrer);
6641         }
6642         QMimeData *mimeData = new QMimeData();
6643         mimeData->setUrls(QList<QUrl>() << u);
6644         KUrlMimeData::setMetaData(metaDataMap, mimeData);
6645         drag->setMimeData(mimeData);
6646 
6647         if (img && img->complete()) {
6648             drag->mimeData()->setImageData(img->currentImage());
6649         }
6650 
6651         if (!pix.isNull()) {
6652             drag->setPixmap(pix);
6653         }
6654 
6655         stopAutoScroll();
6656         drag->start();
6657 
6658         // when we finish our drag, we need to undo our mouse press
6659         d->m_bMousePressed = false;
6660         d->m_strSelectedURL.clear();
6661         d->m_strSelectedURLTarget.clear();
6662         return true;
6663     }
6664     return false;
6665 #endif // QT_NO_DRAGANDDROP
6666 }
6667 
6668 bool KHTMLPart::handleMouseMoveEventOver(khtml::MouseMoveEvent *event)
6669 {
6670     // Mouse clicked -> do nothing
6671     if (d->m_bMousePressed) {
6672         return false;
6673     }
6674 
6675     DOM::DOMString url = event->url();
6676 
6677     // The mouse is over something
6678     if (url.length()) {
6679         DOM::DOMString target = event->target();
6680         QMouseEvent *_mouse = event->qmouseEvent();
6681         DOM::Node innerNode = event->innerNode();
6682 
6683         bool shiftPressed = (_mouse->modifiers() & Qt::ShiftModifier);
6684 
6685         // Image map
6686         if (!innerNode.isNull() && innerNode.elementId() == ID_IMG) {
6687             HTMLImageElementImpl *i = static_cast<HTMLImageElementImpl *>(innerNode.handle());
6688             if (i && i->isServerMap()) {
6689                 khtml::RenderObject *r = i->renderer();
6690                 if (r) {
6691                     int absx, absy;
6692                     r->absolutePosition(absx, absy);
6693                     int x(event->x() - absx), y(event->y() - absy);
6694 
6695                     d->m_overURL = url.string() + QString("?%1,%2").arg(x).arg(y);
6696                     d->m_overURLTarget = target.string();
6697                     overURL(d->m_overURL, target.string(), shiftPressed);
6698                     return true;
6699                 }
6700             }
6701         }
6702 
6703         // normal link
6704         if (d->m_overURL.isEmpty() || d->m_overURL != url || d->m_overURLTarget != target) {
6705             d->m_overURL = url.string();
6706             d->m_overURLTarget = target.string();
6707             overURL(d->m_overURL, target.string(), shiftPressed);
6708         }
6709     } else { // Not over a link...
6710         if (!d->m_overURL.isEmpty()) { // and we were over a link  -> reset to "default statusbar text"
6711             // reset to "default statusbar text"
6712             resetHoverText();
6713         }
6714     }
6715     return true;
6716 }
6717 
6718 void KHTMLPart::handleMouseMoveEventSelection(khtml::MouseMoveEvent *event)
6719 {
6720     // Mouse not pressed. Do nothing.
6721     if (!d->m_bMousePressed) {
6722         return;
6723     }
6724 
6725 #ifdef KHTML_NO_SELECTION
6726     if (d->m_doc && d->m_view) {
6727         QPoint diff(mouse->globalPos() - d->m_dragLastPos);
6728 
6729         if (abs(diff.x()) > 64 || abs(diff.y()) > 64) {
6730             d->m_view->scrollBy(-diff.x(), -diff.y());
6731             d->m_dragLastPos = mouse->globalPos();
6732         }
6733     }
6734 #else
6735 
6736     QMouseEvent *mouse = event->qmouseEvent();
6737     DOM::Node innerNode = event->innerNode();
6738 
6739     if ((mouse->buttons() & Qt::LeftButton) == 0 || !innerNode.handle() || !innerNode.handle()->renderer() ||
6740             !innerNode.handle()->renderer()->shouldSelect()) {
6741         return;
6742     }
6743 
6744     // handle making selection
6745     extendSelectionTo(event->x(), event->y(), innerNode);
6746 #endif // KHTML_NO_SELECTION
6747 }
6748 
6749 void KHTMLPart::khtmlMouseMoveEvent(khtml::MouseMoveEvent *event)
6750 {
6751     if (handleMouseMoveEventDrag(event)) {
6752         return;
6753     }
6754 
6755     if (handleMouseMoveEventOver(event)) {
6756         return;
6757     }
6758 
6759     handleMouseMoveEventSelection(event);
6760 }
6761 
6762 void KHTMLPart::khtmlMouseReleaseEvent(khtml::MouseReleaseEvent *event)
6763 {
6764     DOM::Node innerNode = event->innerNode();
6765     d->m_mousePressNode = DOM::Node();
6766 
6767     if (d->m_bMousePressed) {
6768         setStatusBarText(QString(), BarHoverText);
6769         stopAutoScroll();
6770     }
6771 
6772     // Used to prevent mouseMoveEvent from initiating a drag before
6773     // the mouse is pressed again.
6774     d->m_bMousePressed = false;
6775 
6776 #ifndef QT_NO_CLIPBOARD
6777     QMouseEvent *_mouse = event->qmouseEvent();
6778     if ((d->m_guiProfile == BrowserViewGUI) && (_mouse->button() == Qt::MidButton) && (event->url().isNull())) {
6779         // qCDebug(KHTML_LOG) << "MMB shouldOpen=" << d->m_bOpenMiddleClick;
6780 
6781         if (d->m_bOpenMiddleClick) {
6782             KHTMLPart *p = this;
6783             while (p->parentPart()) {
6784                 p = p->parentPart();
6785             }
6786             p->d->m_extension->pasteRequest();
6787         }
6788     }
6789 #endif
6790 
6791 #ifndef KHTML_NO_SELECTION
6792     {
6793 
6794         // Clear the selection if the mouse didn't move after the last mouse press.
6795         // We do this so when clicking on the selection, the selection goes away.
6796         // However, if we are editing, place the caret.
6797         if (!d->editor_context.m_beganSelectingText
6798                 && d->m_dragStartPos.x() == event->x()
6799                 && d->m_dragStartPos.y() == event->y()
6800                 && d->editor_context.m_selection.state() == Selection::RANGE) {
6801             Selection selection;
6802 #ifdef APPLE_CHANGES
6803             if (d->editor_context.m_selection.base().node()->isContentEditable())
6804 #endif
6805                 selection.moveTo(d->editor_context.m_selection.base().node()->positionForCoordinates(event->x(), event->y()).position());
6806             setCaret(selection);
6807         }
6808         // get selected text and paste to the clipboard
6809 #ifndef QT_NO_CLIPBOARD
6810         QString text = selectedText();
6811         text.replace(QChar(0xa0), ' ');
6812         if (!text.isEmpty()) {
6813             disconnect(qApp->clipboard(), SIGNAL(selectionChanged()), this, SLOT(slotClearSelection()));
6814             qApp->clipboard()->setText(text, QClipboard::Selection);
6815             connect(qApp->clipboard(), SIGNAL(selectionChanged()), SLOT(slotClearSelection()));
6816         }
6817 #endif
6818         //qCDebug(KHTML_LOG) << "selectedText = " << text;
6819         emitSelectionChanged();
6820 //qCDebug(KHTML_LOG) << "rel2: startBefEnd " << d->m_startBeforeEnd << " extAtEnd " << d->m_extendAtEnd << " (" << d->m_startOffset << ") - (" << d->m_endOffset << "), caretOfs " << d->caretOffset();
6821     }
6822 #endif
6823 }
6824 
6825 void KHTMLPart::khtmlDrawContentsEvent(khtml::DrawContentsEvent *)
6826 {
6827 }
6828 
6829 void KHTMLPart::guiActivateEvent(KParts::GUIActivateEvent *event)
6830 {
6831     if (event->activated()) {
6832         emitSelectionChanged();
6833         emit d->m_extension->enableAction("print", d->m_doc != nullptr);
6834 
6835         if (!d->m_settings->autoLoadImages() && d->m_paLoadImages) {
6836             QList<QAction *> lst;
6837             lst.append(d->m_paLoadImages);
6838             plugActionList("loadImages", lst);
6839         }
6840     }
6841 }
6842 
6843 void KHTMLPart::slotPrintFrame()
6844 {
6845     if (d->m_frames.count() == 0) {
6846         return;
6847     }
6848 
6849     KParts::ReadOnlyPart *frame = currentFrame();
6850     if (!frame) {
6851         return;
6852     }
6853 
6854     KParts::BrowserExtension *ext = KParts::BrowserExtension::childObject(frame);
6855 
6856     if (!ext) {
6857         return;
6858     }
6859 
6860     const QMetaObject *mo = ext->metaObject();
6861 
6862     if (mo->indexOfSlot("print()") != -1) {
6863         QMetaObject::invokeMethod(ext, "print()",  Qt::DirectConnection);
6864     }
6865 }
6866 
6867 void KHTMLPart::slotSelectAll()
6868 {
6869     KParts::ReadOnlyPart *part = currentFrame();
6870     if (part && part->inherits("KHTMLPart")) {
6871         static_cast<KHTMLPart *>(part)->selectAll();
6872     }
6873 }
6874 
6875 void KHTMLPart::startAutoScroll()
6876 {
6877     connect(&d->m_scrollTimer, SIGNAL(timeout()), this, SLOT(slotAutoScroll()));
6878     d->m_scrollTimer.setSingleShot(false);
6879     d->m_scrollTimer.start(100);
6880 }
6881 
6882 void KHTMLPart::stopAutoScroll()
6883 {
6884     disconnect(&d->m_scrollTimer, SIGNAL(timeout()), this, SLOT(slotAutoScroll()));
6885     if (d->m_scrollTimer.isActive()) {
6886         d->m_scrollTimer.stop();
6887     }
6888 }
6889 
6890 void KHTMLPart::slotAutoScroll()
6891 {
6892     if (d->m_view) {
6893         d->m_view->doAutoScroll();
6894     } else {
6895         stopAutoScroll();    // Safety
6896     }
6897 }
6898 
6899 void KHTMLPart::runAdFilter()
6900 {
6901     if (parentPart()) {
6902         parentPart()->runAdFilter();
6903     }
6904 
6905     if (!d->m_doc) {
6906         return;
6907     }
6908 
6909     QSetIterator<khtml::CachedObject *> it(d->m_doc->docLoader()->m_docObjects);
6910     while (it.hasNext()) {
6911         khtml::CachedObject *obj = it.next();
6912         if (obj->type() == khtml::CachedObject::Image) {
6913             khtml::CachedImage *image = static_cast<khtml::CachedImage *>(obj);
6914             bool wasBlocked = image->m_wasBlocked;
6915             image->m_wasBlocked = KHTMLGlobal::defaultHTMLSettings()->isAdFiltered(d->m_doc->completeURL(image->url().string()));
6916             if (image->m_wasBlocked != wasBlocked) {
6917                 image->do_notify(QRect(QPoint(0, 0), image->pixmap_size()));
6918             }
6919         }
6920     }
6921 
6922     if (KHTMLGlobal::defaultHTMLSettings()->isHideAdsEnabled()) {
6923         for (NodeImpl * nextNode, *node = d->m_doc; node; node = nextNode) {
6924 
6925             // We might be deleting 'node' shortly.
6926             nextNode = node->traverseNextNode();
6927 
6928             if (node->id() == ID_IMG ||
6929                     node->id() == ID_IFRAME ||
6930                     (node->id() == ID_INPUT && static_cast<HTMLInputElementImpl *>(node)->inputType() == HTMLInputElementImpl::IMAGE)) {
6931                 if (KHTMLGlobal::defaultHTMLSettings()->isAdFiltered(d->m_doc->completeURL(static_cast<ElementImpl *>(node)->getAttribute(ATTR_SRC).trimSpaces().string()))) {
6932                     // Since any kids of node will be deleted, too, fastforward nextNode
6933                     // until we get outside of node.
6934                     while (nextNode && nextNode->isAncestor(node)) {
6935                         nextNode = nextNode->traverseNextNode();
6936                     }
6937 
6938                     node->ref();
6939                     NodeImpl *parent = node->parent();
6940                     if (parent) {
6941                         int exception = 0;
6942                         parent->removeChild(node, exception);
6943                     }
6944                     node->deref();
6945                 }
6946             }
6947         }
6948     }
6949 }
6950 
6951 void KHTMLPart::selectAll()
6952 {
6953     if (!d->m_doc) {
6954         return;
6955     }
6956 
6957     NodeImpl *first;
6958     if (d->m_doc->isHTMLDocument()) {
6959         first = static_cast<HTMLDocumentImpl *>(d->m_doc)->body();
6960     } else {
6961         first = d->m_doc;
6962     }
6963     NodeImpl *next;
6964 
6965     // Look for first text/cdata node that has a renderer,
6966     // or first childless replaced element
6967     while (first && !(first->renderer()
6968                       && ((first->nodeType() == Node::TEXT_NODE || first->nodeType() == Node::CDATA_SECTION_NODE)
6969                           || (first->renderer()->isReplaced() && !first->renderer()->firstChild())))) {
6970         next = first->firstChild();
6971         if (!next) {
6972             next = first->nextSibling();
6973         }
6974         while (first && !next) {
6975             first = first->parentNode();
6976             if (first) {
6977                 next = first->nextSibling();
6978             }
6979         }
6980         first = next;
6981     }
6982 
6983     NodeImpl *last;
6984     if (d->m_doc->isHTMLDocument()) {
6985         last = static_cast<HTMLDocumentImpl *>(d->m_doc)->body();
6986     } else {
6987         last = d->m_doc;
6988     }
6989     // Look for last text/cdata node that has a renderer,
6990     // or last childless replaced element
6991     // ### Instead of changing this loop, use findLastSelectableNode
6992     // in render_table.cpp (LS)
6993     while (last && !(last->renderer()
6994                      && ((last->nodeType() == Node::TEXT_NODE || last->nodeType() == Node::CDATA_SECTION_NODE)
6995                          || (last->renderer()->isReplaced() && !last->renderer()->lastChild())))) {
6996         next = last->lastChild();
6997         if (!next) {
6998             next = last->previousSibling();
6999         }
7000         while (last && !next) {
7001             last = last->parentNode();
7002             if (last) {
7003                 next = last->previousSibling();
7004             }
7005         }
7006         last = next;
7007     }
7008 
7009     if (!first || !last) {
7010         return;
7011     }
7012     Q_ASSERT(first->renderer());
7013     Q_ASSERT(last->renderer());
7014     d->editor_context.m_selection.moveTo(Position(first, 0), Position(last, last->nodeValue().length()));
7015     d->m_doc->updateSelection();
7016 
7017     emitSelectionChanged();
7018 }
7019 
7020 bool KHTMLPart::checkLinkSecurity(const QUrl &linkURL, const KLocalizedString &message, const QString &button)
7021 {
7022     bool linkAllowed = true;
7023 
7024     if (d->m_doc) {
7025         linkAllowed = KUrlAuthorized::authorizeUrlAction("redirect", url(), linkURL);
7026     }
7027 
7028     if (!linkAllowed) {
7029         khtml::Tokenizer *tokenizer = d->m_doc->tokenizer();
7030         if (tokenizer) {
7031             tokenizer->setOnHold(true);
7032         }
7033 
7034         int response = KMessageBox::Cancel;
7035         if (!message.isEmpty()) {
7036             // Dangerous flag makes the Cancel button the default
7037             response = KMessageBox::warningContinueCancel(nullptr,
7038                        message.subs(linkURL.toDisplayString().toHtmlEscaped()).toString(),
7039                        i18n("Security Warning"),
7040                        KGuiItem(button),
7041                        KStandardGuiItem::cancel(),
7042                        QString(), // no don't ask again info
7043                        KMessageBox::Notify | KMessageBox::Dangerous);
7044         } else {
7045             KMessageBox::error(nullptr,
7046                                i18n("<qt>Access by untrusted page to<br /><b>%1</b><br /> denied.</qt>", linkURL.toDisplayString().toHtmlEscaped()),
7047                                i18n("Security Alert"));
7048         }
7049 
7050         if (tokenizer) {
7051             tokenizer->setOnHold(false);
7052         }
7053         return (response == KMessageBox::Continue);
7054     }
7055     return true;
7056 }
7057 
7058 void KHTMLPart::slotPartRemoved(KParts::Part *part)
7059 {
7060 //    qCDebug(KHTML_LOG) << part;
7061     if (part == d->m_activeFrame) {
7062         d->m_activeFrame = nullptr;
7063         if (!part->inherits("KHTMLPart")) {
7064             if (factory()) {
7065                 factory()->removeClient(part);
7066             }
7067             if (childClients().contains(part)) {
7068                 removeChildClient(part);
7069             }
7070         }
7071     }
7072 }
7073 
7074 void KHTMLPart::slotActiveFrameChanged(KParts::Part *part)
7075 {
7076 //    qCDebug(KHTML_LOG) << this << "part=" << part;
7077     if (part == this) {
7078         qCCritical(KHTML_LOG) << "strange error! we activated ourselves";
7079         assert(false);
7080         return;
7081     }
7082 //    qCDebug(KHTML_LOG) << "d->m_activeFrame=" << d->m_activeFrame;
7083     if (d->m_activeFrame && d->m_activeFrame->widget() && d->m_activeFrame->widget()->inherits("QFrame")) {
7084         QFrame *frame = static_cast<QFrame *>(d->m_activeFrame->widget());
7085         if (frame->frameStyle() != QFrame::NoFrame) {
7086             frame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
7087             frame->repaint();
7088         }
7089     }
7090 
7091     if (d->m_activeFrame && !d->m_activeFrame->inherits("KHTMLPart")) {
7092         if (factory()) {
7093             factory()->removeClient(d->m_activeFrame);
7094         }
7095         removeChildClient(d->m_activeFrame);
7096     }
7097     if (part && !part->inherits("KHTMLPart")) {
7098         if (factory()) {
7099             factory()->addClient(part);
7100         }
7101         insertChildClient(part);
7102     }
7103 
7104     d->m_activeFrame = part;
7105 
7106     if (d->m_activeFrame && d->m_activeFrame->widget()->inherits("QFrame")) {
7107         QFrame *frame = static_cast<QFrame *>(d->m_activeFrame->widget());
7108         if (frame->frameStyle() != QFrame::NoFrame) {
7109             frame->setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
7110             frame->repaint();
7111         }
7112         // qCDebug(KHTML_LOG) << "new active frame " << d->m_activeFrame;
7113     }
7114 
7115     updateActions();
7116 
7117     // (note: childObject returns 0 if the argument is 0)
7118     d->m_extension->setExtensionProxy(KParts::BrowserExtension::childObject(d->m_activeFrame));
7119 }
7120 
7121 void KHTMLPart::setActiveNode(const DOM::Node &node)
7122 {
7123     if (!d->m_doc || !d->m_view) {
7124         return;
7125     }
7126 
7127     // Set the document's active node
7128     d->m_doc->setFocusNode(node.handle());
7129 
7130     // Scroll the view if necessary to ensure that the new focus node is visible
7131     QRect rect  = node.handle()->getRect();
7132     d->m_view->ensureVisible(rect.right(), rect.bottom());
7133     d->m_view->ensureVisible(rect.left(), rect.top());
7134 }
7135 
7136 DOM::Node KHTMLPart::activeNode() const
7137 {
7138     return DOM::Node(d->m_doc ? d->m_doc->focusNode() : nullptr);
7139 }
7140 
7141 DOM::EventListener *KHTMLPart::createHTMLEventListener(QString code, QString name, NodeImpl *node, bool svg)
7142 {
7143     KJSProxy *proxy = jScript();
7144 
7145     if (!proxy) {
7146         return nullptr;
7147     }
7148 
7149     return proxy->createHTMLEventHandler(url().toString(), name, code, node, svg);
7150 }
7151 
7152 KHTMLPart *KHTMLPart::opener()
7153 {
7154     return d->m_opener;
7155 }
7156 
7157 void KHTMLPart::setOpener(KHTMLPart *_opener)
7158 {
7159     d->m_opener = _opener;
7160 }
7161 
7162 bool KHTMLPart::openedByJS()
7163 {
7164     return d->m_openedByJS;
7165 }
7166 
7167 void KHTMLPart::setOpenedByJS(bool _openedByJS)
7168 {
7169     d->m_openedByJS = _openedByJS;
7170 }
7171 
7172 void KHTMLPart::preloadStyleSheet(const QString &url, const QString &stylesheet)
7173 {
7174     khtml::Cache::preloadStyleSheet(url, stylesheet);
7175 }
7176 
7177 void KHTMLPart::preloadScript(const QString &url, const QString &script)
7178 {
7179     khtml::Cache::preloadScript(url, script);
7180 }
7181 
7182 long KHTMLPart::cacheId() const
7183 {
7184     return d->m_cacheId;
7185 }
7186 
7187 bool KHTMLPart::restored() const
7188 {
7189     return d->m_restored;
7190 }
7191 
7192 bool KHTMLPart::pluginPageQuestionAsked(const QString &mimetype) const
7193 {
7194     // parentPart() should be const!
7195     KHTMLPart *parent = const_cast<KHTMLPart *>(this)->parentPart();
7196     if (parent) {
7197         return parent->pluginPageQuestionAsked(mimetype);
7198     }
7199 
7200     return d->m_pluginPageQuestionAsked.contains(mimetype);
7201 }
7202 
7203 void KHTMLPart::setPluginPageQuestionAsked(const QString &mimetype)
7204 {
7205     if (parentPart()) {
7206         parentPart()->setPluginPageQuestionAsked(mimetype);
7207     }
7208 
7209     d->m_pluginPageQuestionAsked.append(mimetype);
7210 }
7211 
7212 KEncodingDetector *KHTMLPart::createDecoder()
7213 {
7214     KEncodingDetector *dec = new KEncodingDetector();
7215     if (!d->m_encoding.isNull())
7216         dec->setEncoding(d->m_encoding.toLatin1().constData(),
7217                          d->m_haveEncoding ? KEncodingDetector::UserChosenEncoding : KEncodingDetector::EncodingFromHTTPHeader);
7218     else {
7219         // Inherit the default encoding from the parent frame if there is one.
7220         QByteArray defaultEncoding = (parentPart() && parentPart()->d->m_decoder)
7221                                      ? QByteArray(parentPart()->d->m_decoder->encoding()) : settings()->encoding().toLatin1();
7222         dec->setEncoding(defaultEncoding.constData(), KEncodingDetector::DefaultEncoding);
7223     }
7224 
7225     if (d->m_doc) {
7226         d->m_doc->setDecoder(dec);
7227     }
7228 
7229     // convert from KEncodingProber::ProberType to KEncodingDetector::AutoDetectScript
7230     KEncodingDetector::AutoDetectScript scri;
7231     switch (d->m_autoDetectLanguage) {
7232     case KEncodingProber::None: scri = KEncodingDetector::None; break;
7233     case KEncodingProber::Universal: scri = KEncodingDetector::SemiautomaticDetection; break;
7234     case KEncodingProber::Arabic: scri = KEncodingDetector::Arabic; break;
7235     case KEncodingProber::Baltic: scri = KEncodingDetector::Baltic; break;
7236     case KEncodingProber::CentralEuropean: scri = KEncodingDetector::CentralEuropean; break;
7237     case KEncodingProber::ChineseSimplified: scri = KEncodingDetector::ChineseSimplified; break;
7238     case KEncodingProber::ChineseTraditional: scri = KEncodingDetector::ChineseTraditional; break;
7239     case KEncodingProber::Cyrillic: scri = KEncodingDetector::Cyrillic; break;
7240     case KEncodingProber::Greek: scri = KEncodingDetector::Greek; break;
7241     case KEncodingProber::Hebrew: scri = KEncodingDetector::Hebrew; break;
7242     case KEncodingProber::Japanese: scri = KEncodingDetector::Japanese; break;
7243     case KEncodingProber::Korean: scri = KEncodingDetector::Korean; break;
7244     case KEncodingProber::NorthernSaami: scri = KEncodingDetector::NorthernSaami; break;
7245     case KEncodingProber::Other: scri = KEncodingDetector::SemiautomaticDetection; break;
7246     case KEncodingProber::SouthEasternEurope: scri = KEncodingDetector::SouthEasternEurope; break;
7247     case KEncodingProber::Thai: scri = KEncodingDetector::Thai; break;
7248     case KEncodingProber::Turkish: scri = KEncodingDetector::Turkish; break;
7249     case KEncodingProber::Unicode: scri = KEncodingDetector::Unicode; break;
7250     case KEncodingProber::WesternEuropean: scri = KEncodingDetector::WesternEuropean; break;
7251     default: scri = KEncodingDetector::SemiautomaticDetection; break;
7252     }
7253     dec->setAutoDetectLanguage(scri);
7254     return dec;
7255 }
7256 
7257 void KHTMLPart::emitCaretPositionChanged(const DOM::Position &pos)
7258 {
7259     // pos must not be already converted to range-compliant coordinates
7260     Position rng_pos = pos.equivalentRangeCompliantPosition();
7261     Node node = rng_pos.node();
7262     emit caretPositionChanged(node, rng_pos.offset());
7263 }
7264 
7265 void KHTMLPart::restoreScrollPosition()
7266 {
7267     const KParts::OpenUrlArguments args(arguments());
7268 
7269     if (url().hasFragment() && !d->m_restoreScrollPosition && !args.reload()) {
7270         if (!d->m_doc || !d->m_doc->parsing()) {
7271             disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
7272         }
7273         if (!gotoAnchor(url().fragment(QUrl::FullyEncoded))) {
7274             gotoAnchor(url().fragment(QUrl::FullyDecoded));
7275         }
7276         return;
7277     }
7278 
7279     // Check whether the viewport has become large enough to encompass the stored
7280     // offsets. If the document has been fully loaded, force the new coordinates,
7281     // even if the canvas is too short (can happen when user resizes the window
7282     // during loading).
7283     if ((d->m_view->contentsHeight() - d->m_view->visibleHeight()) >= args.yOffset()
7284             || d->m_bComplete) {
7285         d->m_view->setContentsPos(args.xOffset(), args.yOffset());
7286         disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
7287     }
7288 }
7289 
7290 void KHTMLPart::openWallet(DOM::HTMLFormElementImpl *form)
7291 {
7292 #ifndef KHTML_NO_WALLET
7293     KHTMLPart *p;
7294 
7295     for (p = parentPart(); p && p->parentPart(); p = p->parentPart()) {
7296     }
7297 
7298     if (p) {
7299         p->openWallet(form);
7300         return;
7301     }
7302 
7303     if (onlyLocalReferences()) { // avoid triggering on local apps, thumbnails
7304         return;
7305     }
7306 
7307     if (d->m_wallet) {
7308         if (d->m_bWalletOpened) {
7309             if (d->m_wallet->isOpen()) {
7310                 form->walletOpened(d->m_wallet);
7311                 return;
7312             }
7313             d->m_wallet->deleteLater();
7314             d->m_wallet = nullptr;
7315             d->m_bWalletOpened = false;
7316         }
7317     }
7318 
7319     if (!d->m_wq) {
7320         KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), widget() ? widget()->topLevelWidget()->winId() : 0, KWallet::Wallet::Asynchronous);
7321         d->m_wq = new KHTMLWalletQueue(this);
7322         d->m_wq->wallet = wallet;
7323         connect(wallet, SIGNAL(walletOpened(bool)), d->m_wq, SLOT(walletOpened(bool)));
7324         connect(d->m_wq, SIGNAL(walletOpened(KWallet::Wallet*)), this, SLOT(walletOpened(KWallet::Wallet*)));
7325     }
7326     assert(form);
7327     d->m_wq->callers.append(KHTMLWalletQueue::Caller(form, form->document()));
7328 #endif // KHTML_NO_WALLET
7329 }
7330 
7331 void KHTMLPart::saveToWallet(const QString &key, const QMap<QString, QString> &data)
7332 {
7333 #ifndef KHTML_NO_WALLET
7334     KHTMLPart *p;
7335 
7336     for (p = parentPart(); p && p->parentPart(); p = p->parentPart()) {
7337     }
7338 
7339     if (p) {
7340         p->saveToWallet(key, data);
7341         return;
7342     }
7343 
7344     if (d->m_wallet) {
7345         if (d->m_bWalletOpened) {
7346             if (d->m_wallet->isOpen()) {
7347                 if (!d->m_wallet->hasFolder(KWallet::Wallet::FormDataFolder())) {
7348                     d->m_wallet->createFolder(KWallet::Wallet::FormDataFolder());
7349                 }
7350                 d->m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
7351                 d->m_wallet->writeMap(key, data);
7352                 return;
7353             }
7354             d->m_wallet->deleteLater();
7355             d->m_wallet = nullptr;
7356             d->m_bWalletOpened = false;
7357         }
7358     }
7359 
7360     if (!d->m_wq) {
7361         KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), widget() ? widget()->topLevelWidget()->winId() : 0, KWallet::Wallet::Asynchronous);
7362         d->m_wq = new KHTMLWalletQueue(this);
7363         d->m_wq->wallet = wallet;
7364         connect(wallet, SIGNAL(walletOpened(bool)), d->m_wq, SLOT(walletOpened(bool)));
7365         connect(d->m_wq, SIGNAL(walletOpened(KWallet::Wallet*)), this, SLOT(walletOpened(KWallet::Wallet*)));
7366     }
7367     d->m_wq->savers.append(qMakePair(key, data));
7368 #endif // KHTML_NO_WALLET
7369 }
7370 
7371 void KHTMLPart::dequeueWallet(DOM::HTMLFormElementImpl *form)
7372 {
7373 #ifndef KHTML_NO_WALLET
7374     KHTMLPart *p;
7375 
7376     for (p = parentPart(); p && p->parentPart(); p = p->parentPart()) {
7377     }
7378 
7379     if (p) {
7380         p->dequeueWallet(form);
7381         return;
7382     }
7383 
7384     if (d->m_wq) {
7385         d->m_wq->callers.removeAll(KHTMLWalletQueue::Caller(form, form->document()));
7386     }
7387 #endif // KHTML_NO_WALLET
7388 }
7389 
7390 void KHTMLPart::walletOpened(KWallet::Wallet *wallet)
7391 {
7392 #ifndef KHTML_NO_WALLET
7393     assert(!d->m_wallet);
7394     assert(d->m_wq);
7395 
7396     d->m_wq->deleteLater(); // safe?
7397     d->m_wq = nullptr;
7398 
7399     if (!wallet) {
7400         d->m_bWalletOpened = false;
7401         return;
7402     }
7403 
7404     d->m_wallet = wallet;
7405     d->m_bWalletOpened = true;
7406     connect(d->m_wallet, SIGNAL(walletClosed()), SLOT(slotWalletClosed()));
7407     d->m_walletForms.clear();
7408     if (!d->m_statusBarWalletLabel) {
7409         d->m_statusBarWalletLabel = new KUrlLabel(d->m_statusBarExtension->statusBar());
7410         d->m_statusBarWalletLabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum));
7411         d->m_statusBarWalletLabel->setUseCursor(false);
7412         d->m_statusBarExtension->addStatusBarItem(d->m_statusBarWalletLabel, 0, false);
7413         d->m_statusBarWalletLabel->setPixmap(SmallIcon("wallet-open"));
7414         connect(d->m_statusBarWalletLabel, SIGNAL(leftClickedUrl()), SLOT(launchWalletManager()));
7415         connect(d->m_statusBarWalletLabel, SIGNAL(rightClickedUrl()), SLOT(walletMenu()));
7416     }
7417     d->m_statusBarWalletLabel->setToolTip(i18n("The wallet '%1' is open and being used for form data and passwords.", KWallet::Wallet::NetworkWallet()));
7418 #endif // KHTML_NO_WALLET
7419 }
7420 
7421 KWallet::Wallet *KHTMLPart::wallet()
7422 {
7423 #ifndef KHTML_NO_WALLET
7424     KHTMLPart *p;
7425 
7426     for (p = parentPart(); p && p->parentPart(); p = p->parentPart())
7427         ;
7428 
7429     if (p) {
7430         return p->wallet();
7431     }
7432 
7433     return d->m_wallet;
7434 #else
7435     return 0;
7436 #endif // !KHTML_NO_WALLET
7437 }
7438 
7439 void KHTMLPart::slotWalletClosed()
7440 {
7441 #ifndef KHTML_NO_WALLET
7442     if (d->m_wallet) {
7443         d->m_wallet->deleteLater();
7444         d->m_wallet = nullptr;
7445     }
7446     d->m_bWalletOpened = false;
7447     if (d->m_statusBarWalletLabel) {
7448         d->m_statusBarExtension->removeStatusBarItem(d->m_statusBarWalletLabel);
7449         delete d->m_statusBarWalletLabel;
7450         d->m_statusBarWalletLabel = nullptr;
7451     }
7452 #endif // KHTML_NO_WALLET
7453 }
7454 
7455 void KHTMLPart::launchWalletManager()
7456 {
7457 #ifndef KHTML_NO_WALLET
7458     QDBusInterface r("org.kde.kwalletmanager", "/kwalletmanager/MainWindow_1",
7459                      "org.kde.KMainWindow");
7460     if (!r.isValid()) {
7461         KToolInvocation::startServiceByDesktopName("kwalletmanager_show");
7462     } else {
7463         r.call(QDBus::NoBlock, "show");
7464         r.call(QDBus::NoBlock, "raise");
7465     }
7466 #endif // KHTML_NO_WALLET
7467 }
7468 
7469 void KHTMLPart::walletMenu()
7470 {
7471 #ifndef KHTML_NO_WALLET
7472     QMenu *menu = new QMenu(nullptr);
7473     QActionGroup *menuActionGroup = new QActionGroup(menu);
7474     connect(menuActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(removeStoredPasswordForm(QAction*)));
7475 
7476     menu->addAction(i18n("&Close Wallet"), this, SLOT(slotWalletClosed()));
7477 
7478     if (d->m_view && d->m_view->nonPasswordStorableSite(toplevelURL().host())) {
7479         menu->addAction(i18n("&Allow storing passwords for this site"), this, SLOT(delNonPasswordStorableSite()));
7480     }
7481 
7482     // List currently removable form passwords
7483     for (QStringList::ConstIterator it = d->m_walletForms.constBegin(); it != d->m_walletForms.constEnd(); ++it) {
7484         QAction *action = menu->addAction(i18n("Remove password for form %1", *it));
7485         action->setActionGroup(menuActionGroup);
7486         QVariant var(*it);
7487         action->setData(var);
7488     }
7489 
7490     KAcceleratorManager::manage(menu);
7491     menu->popup(QCursor::pos());
7492 #endif // KHTML_NO_WALLET
7493 }
7494 
7495 void KHTMLPart::removeStoredPasswordForm(QAction *action)
7496 {
7497 #ifndef KHTML_NO_WALLET
7498     assert(action);
7499     assert(d->m_wallet);
7500     QVariant var(action->data());
7501 
7502     if (var.isNull() || !var.isValid() || var.type() != QVariant::String) {
7503         return;
7504     }
7505 
7506     QString key = var.toString();
7507     if (KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
7508                                          KWallet::Wallet::FormDataFolder(),
7509                                          key)) {
7510         return;    // failed
7511     }
7512 
7513     if (!d->m_wallet->hasFolder(KWallet::Wallet::FormDataFolder())) {
7514         return;    // failed
7515     }
7516 
7517     d->m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
7518     if (d->m_wallet->removeEntry(key)) {
7519         return;    // failed
7520     }
7521 
7522     d->m_walletForms.removeAll(key);
7523 #endif // KHTML_NO_WALLET
7524 }
7525 
7526 void KHTMLPart::addWalletFormKey(const QString &walletFormKey)
7527 {
7528 #ifndef KHTML_NO_WALLET
7529 
7530     if (parentPart()) {
7531         parentPart()->addWalletFormKey(walletFormKey);
7532         return;
7533     }
7534 
7535     if (!d->m_walletForms.contains(walletFormKey)) {
7536         d->m_walletForms.append(walletFormKey);
7537     }
7538 #endif // KHTML_NO_WALLET
7539 }
7540 
7541 void KHTMLPart::delNonPasswordStorableSite()
7542 {
7543 #ifndef KHTML_NO_WALLET
7544     if (d->m_view) {
7545         d->m_view->delNonPasswordStorableSite(toplevelURL().host());
7546     }
7547 #endif // KHTML_NO_WALLET
7548 }
7549 void KHTMLPart::saveLoginInformation(const QString &host, const QString &key, const QMap<QString, QString> &walletMap)
7550 {
7551 #ifndef KHTML_NO_WALLET
7552     d->m_storePass.saveLoginInformation(host, key, walletMap);
7553 #endif // KHTML_NO_WALLET
7554 }
7555 
7556 void KHTMLPart::slotToggleCaretMode()
7557 {
7558     setCaretMode(d->m_paToggleCaretMode->isChecked());
7559 }
7560 
7561 void KHTMLPart::setFormNotification(KHTMLPart::FormNotification fn)
7562 {
7563     d->m_formNotification = fn;
7564 }
7565 
7566 KHTMLPart::FormNotification KHTMLPart::formNotification() const
7567 {
7568     return d->m_formNotification;
7569 }
7570 
7571 QUrl KHTMLPart::toplevelURL()
7572 {
7573     KHTMLPart *part = this;
7574     while (part->parentPart()) {
7575         part = part->parentPart();
7576     }
7577 
7578     if (!part) {
7579         return QUrl();
7580     }
7581 
7582     return part->url();
7583 }
7584 
7585 bool KHTMLPart::isModified() const
7586 {
7587     if (!d->m_doc) {
7588         return false;
7589     }
7590 
7591     return d->m_doc->unsubmittedFormChanges();
7592 }
7593 
7594 void KHTMLPart::setDebugScript(bool enable)
7595 {
7596     unplugActionList("debugScriptList");
7597     if (enable) {
7598         if (!d->m_paDebugScript) {
7599             d->m_paDebugScript = new QAction(i18n("JavaScript &Debugger"), this);
7600             actionCollection()->addAction("debugScript", d->m_paDebugScript);
7601             connect(d->m_paDebugScript, SIGNAL(triggered(bool)), this, SLOT(slotDebugScript()));
7602         }
7603         d->m_paDebugScript->setEnabled(d->m_frame ? d->m_frame->m_jscript : nullptr);
7604         QList<QAction *> lst;
7605         lst.append(d->m_paDebugScript);
7606         plugActionList("debugScriptList", lst);
7607     }
7608     d->m_bJScriptDebugEnabled = enable;
7609 }
7610 
7611 void KHTMLPart::setSuppressedPopupIndicator(bool enable, KHTMLPart *originPart)
7612 {
7613     if (parentPart()) {
7614         parentPart()->setSuppressedPopupIndicator(enable, originPart);
7615         return;
7616     }
7617 
7618     if (enable && originPart) {
7619         d->m_openableSuppressedPopups++;
7620         if (d->m_suppressedPopupOriginParts.indexOf(originPart) == -1) {
7621             d->m_suppressedPopupOriginParts.append(originPart);
7622         }
7623     }
7624 
7625     if (enable && !d->m_statusBarPopupLabel) {
7626         d->m_statusBarPopupLabel = new KUrlLabel(d->m_statusBarExtension->statusBar());
7627         d->m_statusBarPopupLabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum));
7628         d->m_statusBarPopupLabel->setUseCursor(false);
7629         d->m_statusBarExtension->addStatusBarItem(d->m_statusBarPopupLabel, 0, false);
7630         d->m_statusBarPopupLabel->setPixmap(SmallIcon("window-suppressed"));
7631 
7632         d->m_statusBarPopupLabel->setToolTip(i18n("This page was prevented from opening a new window via JavaScript."));
7633 
7634         connect(d->m_statusBarPopupLabel, SIGNAL(leftClickedUrl()), SLOT(suppressedPopupMenu()));
7635         if (d->m_settings->jsPopupBlockerPassivePopup()) {
7636             QPixmap px;
7637             px = MainBarIcon("window-suppressed");
7638             KPassivePopup::message(i18n("Popup Window Blocked"), i18n("This page has attempted to open a popup window but was blocked.\nYou can click on this icon in the status bar to control this behavior\nor to open the popup."), px, d->m_statusBarPopupLabel);
7639         }
7640     } else if (!enable && d->m_statusBarPopupLabel) {
7641         d->m_statusBarPopupLabel->setToolTip("");
7642         d->m_statusBarExtension->removeStatusBarItem(d->m_statusBarPopupLabel);
7643         delete d->m_statusBarPopupLabel;
7644         d->m_statusBarPopupLabel = nullptr;
7645     }
7646 }
7647 
7648 void KHTMLPart::suppressedPopupMenu()
7649 {
7650     QMenu *m = new QMenu(nullptr);
7651     if (d->m_openableSuppressedPopups) {
7652         m->addAction(i18np("&Show Blocked Popup Window", "&Show %1 Blocked Popup Windows", d->m_openableSuppressedPopups), this, SLOT(showSuppressedPopups()));
7653     }
7654     QAction *a = m->addAction(i18n("Show Blocked Window Passive Popup &Notification"), this, SLOT(togglePopupPassivePopup()));
7655     a->setChecked(d->m_settings->jsPopupBlockerPassivePopup());
7656     m->addAction(i18n("&Configure JavaScript New Window Policies..."), this, SLOT(launchJSConfigDialog()));
7657     m->popup(QCursor::pos());
7658 }
7659 
7660 void KHTMLPart::togglePopupPassivePopup()
7661 {
7662     // Same hack as in disableJSErrorExtension()
7663     d->m_settings->setJSPopupBlockerPassivePopup(!d->m_settings->jsPopupBlockerPassivePopup());
7664     emit configurationChanged();
7665 }
7666 
7667 void KHTMLPart::showSuppressedPopups()
7668 {
7669     foreach (KHTMLPart *part, d->m_suppressedPopupOriginParts) {
7670         if (part) {
7671             KJS::Window *w = KJS::Window::retrieveWindow(part);
7672             if (w) {
7673                 w->showSuppressedWindows();
7674                 w->forgetSuppressedWindows();
7675             }
7676         }
7677     }
7678     setSuppressedPopupIndicator(false);
7679     d->m_openableSuppressedPopups = 0;
7680     d->m_suppressedPopupOriginParts.clear();
7681 }
7682 
7683 // Extension to use for "view document source", "save as" etc.
7684 // Using the right extension can help the viewer get into the right mode (#40496)
7685 QString KHTMLPart::defaultExtension() const
7686 {
7687     if (!d->m_doc) {
7688         return ".html";
7689     }
7690     if (!d->m_doc->isHTMLDocument()) {
7691         return ".xml";
7692     }
7693     return d->m_doc->htmlMode() == DOM::DocumentImpl::XHtml ? ".xhtml" : ".html";
7694 }
7695 
7696 bool KHTMLPart::inProgress() const
7697 {
7698     if (!d->m_bComplete || d->m_runningScripts || (d->m_doc && d->m_doc->parsing())) {
7699         return true;
7700     }
7701 
7702     // Any frame that hasn't completed yet ?
7703     ConstFrameIt it = d->m_frames.constBegin();
7704     const ConstFrameIt end = d->m_frames.constEnd();
7705     for (; it != end; ++it) {
7706         if ((*it)->m_run || !(*it)->m_bCompleted) {
7707             return true;
7708         }
7709     }
7710 
7711     return d->m_submitForm || !d->m_redirectURL.isEmpty() || d->m_redirectionTimer.isActive() || d->m_job;
7712 }
7713 
7714 using namespace KParts;
7715 #ifndef KHTML_NO_WALLET
7716 #include "moc_khtml_wallet_p.cpp"
7717 #endif
7718 
7719 #include "moc_khtml_part.cpp"