KHtml

khtml_part.cpp
1 /* This file is part of the KDE project
2  *
3  * Copyright (C) 1998, 1999 Torben Weis <[email protected]>
4  * 1999 Lars Knoll <[email protected]>
5  * 1999 Antti Koivisto <[email protected]>
6  * 2000 Simon Hausmann <[email protected]>
7  * 2000 Stefan Schimanski <[email protected]>
8  * 2001-2005 George Staikos <[email protected]>
9  * 2001-2003 Dirk Mueller <[email protected]>
10  * 2000-2005 David Faure <[email protected]>
11  * 2002 Apple Computer, Inc.
12  * 2010 Maksim Orlovich ([email protected])
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Library General Public
16  * License as published by the Free Software Foundation; either
17  * version 2 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22  * Library General Public License for more details.
23  *
24  * You should have received a copy of the GNU Library General Public License
25  * along with this library; see the file COPYING.LIB. If not, write to
26  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27  * Boston, MA 02110-1301, USA.
28  */
29 
30 //#define SPEED_DEBUG
31 #include "khtml_part.h"
32 
33 #include "ui_htmlpageinfo.h"
34 
35 #include "khtmlviewbar.h"
36 #include "khtml_pagecache.h"
37 
38 #include "dom/dom_string.h"
39 #include "dom/dom_element.h"
40 #include "dom/dom_exception.h"
41 #include "dom/html_document.h"
42 #include "editing/editor.h"
43 #include "html/html_documentimpl.h"
44 #include "html/html_baseimpl.h"
45 #include "html/html_objectimpl.h"
46 #include "html/html_miscimpl.h"
47 #include "html/html_imageimpl.h"
48 #include "imload/imagemanager.h"
49 #include "rendering/render_text.h"
50 #include "rendering/render_frames.h"
51 #include "rendering/render_layer.h"
52 #include "rendering/render_position.h"
53 #include "misc/loader.h"
54 #include "misc/khtml_partaccessor.h"
55 #include "xml/dom2_eventsimpl.h"
56 #include "xml/dom2_rangeimpl.h"
57 #include "xml/xml_tokenizer.h"
58 #include "css/cssstyleselector.h"
59 using namespace DOM;
60 
61 #include "khtmlview.h"
62 #include <kparts/partmanager.h>
63 #include <kparts/browseropenorsavequestion.h>
64 #include <kparts/guiactivateevent.h>
65 
66 #include <kacceleratormanager.h>
67 #include "ecma/kjs_proxy.h"
68 #include "ecma/kjs_window.h"
69 #include "ecma/kjs_events.h"
70 #include "khtml_settings.h"
71 #include "kjserrordlg.h"
72 
73 #include <kjs/function.h>
74 #include <kjs/interpreter.h>
75 
76 #include <sys/types.h>
77 #include <assert.h>
78 
79 #include <kstringhandler.h>
80 #include <kio/job.h>
81 #include <kio/jobuidelegate.h>
82 #include <kio/global.h>
83 #include <kio/pixmaploader.h>
84 #include <kio/hostinfo.h>
85 #include <kprotocolmanager.h>
86 #include "khtml_debug.h"
87 #include <kjobwidgets.h>
88 #include <kmessagebox.h>
89 #include <kstandardaction.h>
90 #include <kstandardguiitem.h>
91 #include <kactioncollection.h>
92 #include <kmimetypetrader.h>
93 #include <qtemporaryfile.h>
94 #include <ktoolinvocation.h>
95 #include <kurlauthorized.h>
96 #include <kparts/browserinterface.h>
97 #include <kparts/scriptableextension.h>
98 #include <kparts/liveconnectextension.h>
99 #include <kactionmenu.h>
100 #include <ktoggleaction.h>
101 #include <kcodecaction.h>
102 #include <kselectaction.h>
103 
104 #include <QDBusConnection>
105 #include <ksslinfodialog.h>
106 #include <ksslsettings.h>
107 
108 #include <QDBusInterface>
109 #include <QMimeData>
110 #include <kfileitem.h>
111 #include <kurifilter.h>
112 #include <kurllabel.h>
113 #include <kurlmimedata.h>
114 
115 #include <QClipboard>
116 #include <QLocale>
117 #include <QMenu>
118 #include <QToolTip>
119 #include <QDrag>
120 #include <QMouseEvent>
121 #include <QFile>
122 #include <QMetaEnum>
123 #include <QTextDocument>
124 #include <QDate>
125 #include <QtNetwork/QSslCertificate>
126 #include <QStatusBar>
127 #include <QStyle>
128 #include <qmimedatabase.h>
129 #include <qplatformdefs.h>
130 #include <QFileInfo>
131 
132 #include "khtmlpart_p.h"
133 #include "khtml_iface.h"
134 
135 #include "kpassivepopup.h"
136 #include "rendering/render_form.h"
137 #include <kwindowsystem.h>
138 #include <kconfiggroup.h>
139 #include <ksharedconfig.h>
140 
141 #ifdef KJS_DEBUGGER
142 #include "ecma/debugger/debugwindow.h"
143 #endif
144 
145 // SVG
146 #include <svg/SVGDocument.h>
147 #include <qstandardpaths.h>
148 
149 bool KHTMLPartPrivate::s_dnsInitialised = false;
150 
151 // DNS prefetch settings
152 static const int sMaxDNSPrefetchPerPage = 42;
153 static const int sDNSPrefetchTimerDelay = 200;
154 static const int sDNSTTLSeconds = 400;
155 static const int sDNSCacheSize = 500;
156 
157 namespace khtml
158 {
159 
160 class PartStyleSheetLoader : public CachedObjectClient
161 {
162 public:
163  PartStyleSheetLoader(KHTMLPart *part, DOM::DOMString url, DocLoader *dl)
164  {
165  m_part = part;
166  m_cachedSheet = dl->requestStyleSheet(url, QString(), "text/css",
167  true /* "user sheet" */);
168  if (m_cachedSheet) {
169  m_cachedSheet->ref(this);
170  }
171  }
172  virtual ~PartStyleSheetLoader()
173  {
174  if (m_cachedSheet) {
175  m_cachedSheet->deref(this);
176  }
177  }
178  void setStyleSheet(const DOM::DOMString &, const DOM::DOMString &sheet, const DOM::DOMString &, const DOM::DOMString &/*mimetype*/) override
179  {
180  if (m_part) {
181  m_part->setUserStyleSheet(sheet.string());
182  }
183 
184  delete this;
185  }
186  void error(int, const QString &) override
187  {
188  delete this;
189  }
190  QPointer<KHTMLPart> m_part;
191  khtml::CachedCSSStyleSheet *m_cachedSheet;
192 };
193 }
194 
195 KHTMLPart::KHTMLPart(QWidget *parentWidget, QObject *parent, GUIProfile prof)
196  : KParts::ReadOnlyPart(parent)
197 {
198  d = nullptr;
199  KHTMLGlobal::registerPart(this);
200  setComponentData(KHTMLGlobal::aboutData(), false);
201  init(new KHTMLView(this, parentWidget), prof);
202 }
203 
205  : KParts::ReadOnlyPart(parent)
206 {
207  d = nullptr;
208  KHTMLGlobal::registerPart(this);
209  setComponentData(KHTMLGlobal::aboutData(), false);
210  assert(view);
211  if (!view->part()) {
212  view->setPart(this);
213  }
214  init(view, prof);
215 }
216 
217 void KHTMLPart::init(KHTMLView *view, GUIProfile prof)
218 {
219  if (prof == DefaultGUI) {
220  setXMLFile("khtml.rc");
221  } else if (prof == BrowserViewGUI) {
222  setXMLFile("khtml_browser.rc");
223  }
224 
225  d = new KHTMLPartPrivate(this, parent());
226 
227  d->m_view = view;
228 
229  if (!parentPart()) {
230  QWidget *widget = new QWidget(view->parentWidget());
231  widget->setObjectName("khtml_part_widget");
232  QVBoxLayout *layout = new QVBoxLayout(widget);
233  layout->setContentsMargins(0, 0, 0, 0);
234  layout->setSpacing(0);
235  widget->setLayout(layout);
236 
237  d->m_topViewBar = new KHTMLViewBar(KHTMLViewBar::Top, d->m_view, widget);
238  d->m_bottomViewBar = new KHTMLViewBar(KHTMLViewBar::Bottom, d->m_view, widget);
239 
240  layout->addWidget(d->m_topViewBar);
241  layout->addWidget(d->m_view);
242  layout->addWidget(d->m_bottomViewBar);
243  setWidget(widget);
244  widget->setFocusProxy(d->m_view);
245  } else {
246  setWidget(view);
247  }
248 
249  d->m_guiProfile = prof;
250  d->m_extension = new KHTMLPartBrowserExtension(this);
251  d->m_extension->setObjectName("KHTMLBrowserExtension");
252  d->m_hostExtension = new KHTMLPartBrowserHostExtension(this);
253  d->m_statusBarExtension = new KParts::StatusBarExtension(this);
254  d->m_scriptableExtension = new KJS::KHTMLPartScriptable(this);
255  new KHTMLTextExtension(this);
256  new KHTMLHtmlExtension(this);
257  d->m_statusBarPopupLabel = nullptr;
258  d->m_openableSuppressedPopups = 0;
259 
260  d->m_paLoadImages = nullptr;
261  d->m_paDebugScript = nullptr;
262  d->m_bMousePressed = false;
263  d->m_bRightMousePressed = false;
264  d->m_bCleared = false;
265 
266  if (prof == BrowserViewGUI) {
267  d->m_paViewDocument = new QAction(i18n("View Do&cument Source"), this);
268  actionCollection()->addAction("viewDocumentSource", d->m_paViewDocument);
269  connect(d->m_paViewDocument, SIGNAL(triggered(bool)), this, SLOT(slotViewDocumentSource()));
270  if (!parentPart()) {
272  }
273 
274  d->m_paViewFrame = new QAction(i18n("View Frame Source"), this);
275  actionCollection()->addAction("viewFrameSource", d->m_paViewFrame);
276  connect(d->m_paViewFrame, SIGNAL(triggered(bool)), this, SLOT(slotViewFrameSource()));
277  if (!parentPart()) {
279  }
280 
281  d->m_paViewInfo = new QAction(i18n("View Document Information"), this);
282  actionCollection()->addAction("viewPageInfo", d->m_paViewInfo);
283  if (!parentPart()) {
285  }
286  connect(d->m_paViewInfo, SIGNAL(triggered(bool)), this, SLOT(slotViewPageInfo()));
287 
288  d->m_paSaveBackground = new QAction(i18n("Save &Background Image As..."), this);
289  actionCollection()->addAction("saveBackground", d->m_paSaveBackground);
290  connect(d->m_paSaveBackground, SIGNAL(triggered(bool)), this, SLOT(slotSaveBackground()));
291 
292  d->m_paSaveDocument = actionCollection()->addAction(KStandardAction::SaveAs, "saveDocument",
293  this, SLOT(slotSaveDocument()));
294  if (parentPart()) {
295  d->m_paSaveDocument->setShortcuts(QList<QKeySequence>()); // avoid clashes
296  }
297 
298  d->m_paSaveFrame = new QAction(i18n("Save &Frame As..."), this);
299  actionCollection()->addAction("saveFrame", d->m_paSaveFrame);
300  connect(d->m_paSaveFrame, SIGNAL(triggered(bool)), this, SLOT(slotSaveFrame()));
301  } else {
302  d->m_paViewDocument = nullptr;
303  d->m_paViewFrame = nullptr;
304  d->m_paViewInfo = nullptr;
305  d->m_paSaveBackground = nullptr;
306  d->m_paSaveDocument = nullptr;
307  d->m_paSaveFrame = nullptr;
308  }
309 
310  d->m_paSecurity = new QAction(i18n("SSL"), this);
311  actionCollection()->addAction("security", d->m_paSecurity);
312  connect(d->m_paSecurity, SIGNAL(triggered(bool)), this, SLOT(slotSecurity()));
313 
314  d->m_paDebugRenderTree = new QAction(i18n("Print Rendering Tree to STDOUT"), this);
315  actionCollection()->addAction("debugRenderTree", d->m_paDebugRenderTree);
316  connect(d->m_paDebugRenderTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugRenderTree()));
317 
318  d->m_paDebugDOMTree = new QAction(i18n("Print DOM Tree to STDOUT"), this);
319  actionCollection()->addAction("debugDOMTree", d->m_paDebugDOMTree);
320  connect(d->m_paDebugDOMTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugDOMTree()));
321 
322  QAction *paDebugFrameTree = new QAction(i18n("Print frame tree to STDOUT"), this);
323  actionCollection()->addAction("debugFrameTree", paDebugFrameTree);
324  connect(paDebugFrameTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugFrameTree()));
325 
326  d->m_paStopAnimations = new QAction(i18n("Stop Animated Images"), this);
327  actionCollection()->addAction("stopAnimations", d->m_paStopAnimations);
328  connect(d->m_paStopAnimations, SIGNAL(triggered(bool)), this, SLOT(slotStopAnimations()));
329 
330  d->m_paSetEncoding = new KCodecAction(QIcon::fromTheme("character-set"), i18n("Set &Encoding"), this, true);
331  actionCollection()->addAction("setEncoding", d->m_paSetEncoding);
332 // d->m_paSetEncoding->setDelayed( false );
333 
334  connect(d->m_paSetEncoding, &KSelectAction::textTriggered, this, &KHTMLPart::slotSetEncoding);
335  connect(d->m_paSetEncoding, &KCodecAction::encodingProberTriggered, this, &KHTMLPart::slotAutomaticDetectionLanguage);
336 
337  if (KSharedConfig::openConfig()->hasGroup("HTML Settings")) {
338  KConfigGroup config(KSharedConfig::openConfig(), "HTML Settings");
339 
340  d->m_autoDetectLanguage = static_cast<KEncodingProber::ProberType>(config.readEntry("AutomaticDetectionLanguage", /*static_cast<int>(language) */0));
341  if (d->m_autoDetectLanguage == KEncodingProber::None) {
343 // qCWarning(KHTML_LOG) << "00000000 ";
344  if (name.endsWith("1251") || name.startsWith("koi") || name == "iso-8859-5") {
345  d->m_autoDetectLanguage = KEncodingProber::Cyrillic;
346  } else if (name.endsWith("1256") || name == "iso-8859-6") {
347  d->m_autoDetectLanguage = KEncodingProber::Arabic;
348  } else if (name.endsWith("1257") || name == "iso-8859-13" || name == "iso-8859-4") {
349  d->m_autoDetectLanguage = KEncodingProber::Baltic;
350  } else if (name.endsWith("1250") || name == "ibm852" || name == "iso-8859-2" || name == "iso-8859-3") {
351  d->m_autoDetectLanguage = KEncodingProber::CentralEuropean;
352  } else if (name.endsWith("1253") || name == "iso-8859-7") {
353  d->m_autoDetectLanguage = KEncodingProber::Greek;
354  } else if (name.endsWith("1255") || name == "iso-8859-8" || name == "iso-8859-8-i") {
355  d->m_autoDetectLanguage = KEncodingProber::Hebrew;
356  } else if (name == "jis7" || name == "eucjp" || name == "sjis") {
357  d->m_autoDetectLanguage = KEncodingProber::Japanese;
358  } else if (name == "gb2312" || name == "gbk" || name == "gb18030") {
359  d->m_autoDetectLanguage = KEncodingProber::ChineseSimplified;
360  } else if (name == "big5") {
361  d->m_autoDetectLanguage = KEncodingProber::ChineseTraditional;
362  } else if (name == "euc-kr") {
363  d->m_autoDetectLanguage = KEncodingProber::Korean;
364  } else if (name.endsWith("1254") || name == "iso-8859-9") {
365  d->m_autoDetectLanguage = KEncodingProber::Turkish;
366  } else if (name.endsWith("1252") || name == "iso-8859-1" || name == "iso-8859-15") {
367  d->m_autoDetectLanguage = KEncodingProber::WesternEuropean;
368  } else {
369  d->m_autoDetectLanguage = KEncodingProber::Universal;
370  }
371 // qCWarning(KHTML_LOG) << "0000000end " << d->m_autoDetectLanguage << " " << QTextCodec::codecForLocale()->mibEnum();
372  }
373  d->m_paSetEncoding->setCurrentProberType(d->m_autoDetectLanguage);
374  }
375 
376  d->m_paUseStylesheet = new KSelectAction(i18n("Use S&tylesheet"), this);
377  actionCollection()->addAction("useStylesheet", d->m_paUseStylesheet);
378  connect(d->m_paUseStylesheet, SIGNAL(triggered(int)), this, SLOT(slotUseStylesheet()));
379 
380  if (prof == BrowserViewGUI) {
381  d->m_paIncZoomFactor = new KHTMLZoomFactorAction(this, true, "format-font-size-more", i18n("Enlarge Font"), this);
382  actionCollection()->addAction("incFontSizes", d->m_paIncZoomFactor);
383  connect(d->m_paIncZoomFactor, SIGNAL(triggered(bool)), SLOT(slotIncFontSizeFast()));
384  d->m_paIncZoomFactor->setWhatsThis(i18n("<qt>Enlarge Font<br /><br />"
385  "Make the font in this window bigger. "
386  "Click and hold down the mouse button for a menu with all available font sizes.</qt>"));
387 
388  d->m_paDecZoomFactor = new KHTMLZoomFactorAction(this, false, "format-font-size-less", i18n("Shrink Font"), this);
389  actionCollection()->addAction("decFontSizes", d->m_paDecZoomFactor);
390  connect(d->m_paDecZoomFactor, SIGNAL(triggered(bool)), SLOT(slotDecFontSizeFast()));
391  d->m_paDecZoomFactor->setWhatsThis(i18n("<qt>Shrink Font<br /><br />"
392  "Make the font in this window smaller. "
393  "Click and hold down the mouse button for a menu with all available font sizes.</qt>"));
394  if (!parentPart()) {
395  // For framesets, this action also affects frames, so only
396  // the frameset needs to define a shortcut for the action.
397 
398  // TODO: Why also CTRL+=? Because of http://trolltech.com/developer/knowledgebase/524/?
399  // Nobody else does it...
400  actionCollection()->setDefaultShortcut(d->m_paIncZoomFactor, QKeySequence("CTRL++; CTRL+="));
402  }
403  }
404 
405  d->m_paFind = actionCollection()->addAction(KStandardAction::Find, "find", this, SLOT(slotFind()));
406  d->m_paFind->setWhatsThis(i18n("<qt>Find text<br /><br />"
407  "Shows a dialog that allows you to find text on the displayed page.</qt>"));
408 
409  d->m_paFindNext = actionCollection()->addAction(KStandardAction::FindNext, "findNext", this, SLOT(slotFindNext()));
410  d->m_paFindNext->setWhatsThis(i18n("<qt>Find next<br /><br />"
411  "Find the next occurrence of the text that you "
412  "have found using the <b>Find Text</b> function.</qt>"));
413 
414  d->m_paFindPrev = actionCollection()->addAction(KStandardAction::FindPrev, "findPrevious",
415  this, SLOT(slotFindPrev()));
416  d->m_paFindPrev->setWhatsThis(i18n("<qt>Find previous<br /><br />"
417  "Find the previous occurrence of the text that you "
418  "have found using the <b>Find Text</b> function.</qt>"));
419 
420  // These two actions aren't visible in the menus, but exist for the (configurable) shortcut
421  d->m_paFindAheadText = new QAction(i18n("Find Text as You Type"), this);
422  actionCollection()->addAction("findAheadText", d->m_paFindAheadText);
423  actionCollection()->setDefaultShortcut(d->m_paFindAheadText, QKeySequence("/"));
424  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."));
425  d->m_paFindAheadText->setStatusTip(d->m_paFindAheadText->toolTip());
426  connect(d->m_paFindAheadText, SIGNAL(triggered(bool)), this, SLOT(slotFindAheadText()));
427 
428  d->m_paFindAheadLinks = new QAction(i18n("Find Links as You Type"), this);
429  actionCollection()->addAction("findAheadLink", d->m_paFindAheadLinks);
430  // The issue is that it sets the (sticky) option FindLinksOnly, so
431  // if you trigger this shortcut once by mistake, Esc and Ctrl+F will still have the option set.
432  // Better let advanced users configure a shortcut for this advanced option
433  //d->m_paFindAheadLinks->setShortcut( QKeySequence("\'") );
434  d->m_paFindAheadLinks->setToolTip(i18n("This shortcut shows the find bar, and sets the option \"Find links only\"."));
435  d->m_paFindAheadLinks->setStatusTip(d->m_paFindAheadLinks->toolTip());
436  connect(d->m_paFindAheadLinks, SIGNAL(triggered(bool)), this, SLOT(slotFindAheadLink()));
437 
438  if (parentPart()) {
439  d->m_paFind->setShortcuts(QList<QKeySequence>()); // avoid clashes
440  d->m_paFindNext->setShortcuts(QList<QKeySequence>()); // avoid clashes
441  d->m_paFindPrev->setShortcuts(QList<QKeySequence>()); // avoid clashes
442  d->m_paFindAheadText->setShortcuts(QList<QKeySequence>());
443  d->m_paFindAheadLinks->setShortcuts(QList<QKeySequence>());
444  }
445 
446  d->m_paPrintFrame = new QAction(i18n("Print Frame..."), this);
447  actionCollection()->addAction("printFrame", d->m_paPrintFrame);
448  d->m_paPrintFrame->setIcon(QIcon::fromTheme("document-print-frame"));
449  connect(d->m_paPrintFrame, SIGNAL(triggered(bool)), this, SLOT(slotPrintFrame()));
450  d->m_paPrintFrame->setWhatsThis(i18n("<qt>Print Frame<br /><br />"
451  "Some pages have several frames. To print only a single frame, click "
452  "on it and then use this function.</qt>"));
453 
454  // Warning: The name selectAll is used hardcoded by some 3rd parties to remove the
455  // shortcut for selectAll so they do not get ambigous shortcuts. Renaming it
456  // will either crash or render useless that workaround. It would be better
457  // to use the name KStandardAction::name(KStandardAction::SelectAll) but we
458  // can't for the same reason.
459  d->m_paSelectAll = actionCollection()->addAction(KStandardAction::SelectAll, "selectAll",
460  this, SLOT(slotSelectAll()));
461  if (parentPart()) { // Only the frameset has the shortcut, but the slot uses the current frame.
462  d->m_paSelectAll->setShortcuts(QList<QKeySequence>()); // avoid clashes
463  }
464 
465  d->m_paToggleCaretMode = new KToggleAction(i18n("Toggle Caret Mode"), this);
466  actionCollection()->addAction("caretMode", d->m_paToggleCaretMode);
467  actionCollection()->setDefaultShortcut(d->m_paToggleCaretMode, QKeySequence(Qt::Key_F7));
468  connect(d->m_paToggleCaretMode, SIGNAL(triggered(bool)), this, SLOT(slotToggleCaretMode()));
469  d->m_paToggleCaretMode->setChecked(isCaretMode());
470  if (parentPart()) {
471  d->m_paToggleCaretMode->setShortcuts(QList<QKeySequence>()); // avoid clashes
472  }
473 
474  // set the default java(script) flags according to the current host.
475  d->m_bOpenMiddleClick = d->m_settings->isOpenMiddleClickEnabled();
476  d->m_bJScriptEnabled = d->m_settings->isJavaScriptEnabled();
477  setDebugScript(d->m_settings->isJavaScriptDebugEnabled());
478  d->m_bJavaEnabled = d->m_settings->isJavaEnabled();
479  d->m_bPluginsEnabled = d->m_settings->isPluginsEnabled();
480 
481  // Set the meta-refresh flag...
482  d->m_metaRefreshEnabled = d->m_settings->isAutoDelayedActionsEnabled();
483 
484  KHTMLSettings::KSmoothScrollingMode ssm = d->m_settings->smoothScrolling();
485  if (ssm == KHTMLSettings::KSmoothScrollingDisabled) {
486  d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMDisabled);
487  } else if (ssm == KHTMLSettings::KSmoothScrollingWhenEfficient) {
488  d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMWhenEfficient);
489  } else {
490  d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMEnabled);
491  }
492 
493  if (d->m_bDNSPrefetchIsDefault && !onlyLocalReferences()) {
494  KHTMLSettings::KDNSPrefetch dpm = d->m_settings->dnsPrefetch();
495  if (dpm == KHTMLSettings::KDNSPrefetchDisabled) {
496  d->m_bDNSPrefetch = DNSPrefetchDisabled;
497  } else if (dpm == KHTMLSettings::KDNSPrefetchOnlyWWWAndSLD) {
498  d->m_bDNSPrefetch = DNSPrefetchOnlyWWWAndSLD;
499  } else {
500  d->m_bDNSPrefetch = DNSPrefetchEnabled;
501  }
502  }
503 
504  if (!KHTMLPartPrivate::s_dnsInitialised && d->m_bDNSPrefetch != DNSPrefetchDisabled) {
505  KIO::HostInfo::setCacheSize(sDNSCacheSize);
506  KIO::HostInfo::setTTL(sDNSTTLSeconds);
507  KHTMLPartPrivate::s_dnsInitialised = true;
508  }
509 
510  // all shortcuts should only be active, when this part has focus
511  foreach (QAction *action, actionCollection()->actions()) {
513  }
515 
516  connect(view, SIGNAL(zoomView(int)), SLOT(slotZoomView(int)));
517 
518  connect(this, SIGNAL(completed()),
519  this, SLOT(updateActions()));
520  connect(this, SIGNAL(completed(bool)),
521  this, SLOT(updateActions()));
522  connect(this, SIGNAL(started(KIO::Job*)),
523  this, SLOT(updateActions()));
524 
525  // #### FIXME: the process wide loader is going to signal every part about every loaded object.
526  // That's quite inefficient. Should be per-document-tree somehow. Even signaling to
527  // child parts that a request from an ancestor has loaded is inefficent..
528  connect(khtml::Cache::loader(), SIGNAL(requestStarted(khtml::DocLoader*,khtml::CachedObject*)),
529  this, SLOT(slotLoaderRequestStarted(khtml::DocLoader*,khtml::CachedObject*)));
530  connect(khtml::Cache::loader(), SIGNAL(requestDone(khtml::DocLoader*,khtml::CachedObject*)),
531  this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
532  connect(khtml::Cache::loader(), SIGNAL(requestFailed(khtml::DocLoader*,khtml::CachedObject*)),
533  this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
534 
535  connect(&d->m_progressUpdateTimer, SIGNAL(timeout()), this, SLOT(slotProgressUpdate()));
536 
537  findTextBegin(); //reset find variables
538 
539  connect(&d->m_redirectionTimer, SIGNAL(timeout()),
540  this, SLOT(slotRedirect()));
541 
542  if (QDBusConnection::sessionBus().isConnected()) {
543  new KHTMLPartIface(this); // our "adaptor"
544  for (int i = 1;; ++i)
545  if (QDBusConnection::sessionBus().registerObject(QString("/KHTML/%1/widget").arg(i), this)) {
546  break;
547  } else if (i == 0xffff) {
548  qFatal("Something is very wrong in KHTMLPart!");
549  }
550  }
551 
552  if (prof == BrowserViewGUI && !parentPart()) {
553  loadPlugins();
554  }
555 }
556 
558 {
559  // qCDebug(KHTML_LOG) << this;
560  KConfigGroup config(KSharedConfig::openConfig(), "HTML Settings");
561  config.writeEntry("AutomaticDetectionLanguage", int(d->m_autoDetectLanguage));
562 
563  if (d->m_manager) { // the PartManager for this part's children
564  d->m_manager->removePart(this);
565  }
566 
567  slotWalletClosed();
568  if (!parentPart()) { // only delete it if the top khtml_part closes
569  removeJSErrorExtension();
570  }
571 
572  stopAutoScroll();
573  d->m_redirectionTimer.stop();
574 
575  if (!d->m_bComplete) {
576  closeUrl();
577  }
578 
579  disconnect(khtml::Cache::loader(), SIGNAL(requestStarted(khtml::DocLoader*,khtml::CachedObject*)),
580  this, SLOT(slotLoaderRequestStarted(khtml::DocLoader*,khtml::CachedObject*)));
581  disconnect(khtml::Cache::loader(), SIGNAL(requestDone(khtml::DocLoader*,khtml::CachedObject*)),
582  this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
583  disconnect(khtml::Cache::loader(), SIGNAL(requestFailed(khtml::DocLoader*,khtml::CachedObject*)),
584  this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
585 
586  clear();
587  hide();
588 
589  if (d->m_view) {
590  d->m_view->m_part = nullptr;
591  }
592 
593  // Have to delete this here since we forward declare it in khtmlpart_p and
594  // at least some compilers won't call the destructor in this case.
595  delete d->m_jsedlg;
596  d->m_jsedlg = nullptr;
597 
598  if (!parentPart()) { // only delete d->m_frame if the top khtml_part closes
599  delete d->m_frame;
600  } else if (d->m_frame && d->m_frame->m_run) { // for kids, they may get detached while
601  d->m_frame->m_run.data()->abort(); // resolving mimetype; cancel that if needed
602  }
603  delete d; d = nullptr;
604  KHTMLGlobal::deregisterPart(this);
605 }
606 
607 bool KHTMLPart::restoreURL(const QUrl &url)
608 {
609  // qCDebug(KHTML_LOG) << url;
610 
611  d->m_redirectionTimer.stop();
612 
613  /*
614  * That's not a good idea as it will call closeUrl() on all
615  * child frames, preventing them from further loading. This
616  * method gets called from restoreState() in case of a full frameset
617  * restoral, and restoreState() calls closeUrl() before restoring
618  * anyway.
619  // qCDebug(KHTML_LOG) << "closing old URL";
620  closeUrl();
621  */
622 
623  d->m_bComplete = false;
624  d->m_bLoadEventEmitted = false;
625  d->m_workingURL = url;
626 
627  // set the java(script) flags according to the current host.
628  d->m_bJScriptEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaScriptEnabled(url.host());
629  setDebugScript(KHTMLGlobal::defaultHTMLSettings()->isJavaScriptDebugEnabled());
630  d->m_bJavaEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaEnabled(url.host());
631  d->m_bPluginsEnabled = KHTMLGlobal::defaultHTMLSettings()->isPluginsEnabled(url.host());
632 
633  setUrl(url);
634 
635  d->m_restoreScrollPosition = true;
636  disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
637  connect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
638 
639  KHTMLPageCache::self()->fetchData(d->m_cacheId, this, SLOT(slotRestoreData(QByteArray)));
640 
641  emit started(nullptr);
642 
643  return true;
644 }
645 
646 static bool areUrlsForSamePage(const QUrl &url1, const QUrl &url2)
647 {
649  u1.setFragment(QString());
650  if (u1.path() == QLatin1String("/")) {
651  u1.setPath(QString());
652  }
654  u2.setFragment(QString());
655  if (u2.path() == QLatin1String("/")) {
656  u2.setPath(QString());
657  }
658  return u1 == u2;
659 }
660 
661 bool KHTMLPartPrivate::isLocalAnchorJump(const QUrl &url)
662 {
663  // kio_help actually uses fragments to identify different pages, so
664  // always reload with it.
665  if (url.scheme() == QLatin1String("help")) {
666  return false;
667  }
668 
669  return url.hasFragment() && areUrlsForSamePage(url, q->url());
670 }
671 
672 void KHTMLPartPrivate::executeAnchorJump(const QUrl &url, bool lockHistory)
673 {
674  DOM::HashChangeEventImpl *hashChangeEvImpl = nullptr;
675  const QString &oldRef = q->url().fragment(QUrl::FullyEncoded);
676  const QString &newRef = url.fragment(QUrl::FullyEncoded);
677  const bool hashChanged = (oldRef != newRef) || (oldRef.isNull() && newRef.isEmpty());
678 
679  if (hashChanged) {
680  // Note: we want to emit openUrlNotify first thing to make the history capture the old state,
681  // however do not update history if a lock was explicitly requested, e.g. Location.replace()
682  if (!lockHistory) {
683  emit m_extension->openUrlNotify();
684  }
685  // Create hashchange event
686  hashChangeEvImpl = new DOM::HashChangeEventImpl();
687  hashChangeEvImpl->initHashChangeEvent("hashchange",
688  true, //bubble
689  false, //cancelable
690  q->url().toString(), //oldURL
691  url.toString() //newURL
692  );
693  }
694 
695  if (!q->gotoAnchor(newRef)) { // encoded fragment
696  q->gotoAnchor(url.fragment(QUrl::FullyDecoded)); // not encoded fragment
697  }
698 
699  q->setUrl(url);
700  emit m_extension->setLocationBarUrl(url.toDisplayString());
701 
702  if (hashChangeEvImpl) {
703  m_doc->dispatchWindowEvent(hashChangeEvImpl);
704  }
705 }
706 
707 bool KHTMLPart::openUrl(const QUrl &url)
708 {
709  // qCDebug(KHTML_LOG) << this << "opening" << url;
710 
711 #ifndef KHTML_NO_WALLET
712  // Wallet forms are per page, so clear it when loading a different page if we
713  // are not an iframe (because we store walletforms only on the topmost part).
714  if (!parentPart()) {
715  d->m_walletForms.clear();
716  }
717 #endif
718  d->m_redirectionTimer.stop();
719 
720  // check to see if this is an "error://" URL. This is caused when an error
721  // occurs before this part was loaded (e.g. KonqRun), and is passed to
722  // khtmlpart so that it can display the error.
723  if (url.scheme() == "error") {
724  closeUrl();
725 
726  if (d->m_bJScriptEnabled) {
727  d->m_statusBarText[BarOverrideText].clear();
728  d->m_statusBarText[BarDefaultText].clear();
729  }
730 
731  /**
732  * The format of the error url is that two variables are passed in the query:
733  * error = int kio error code, errText = QString error text from kio
734  * and the URL where the error happened is passed as a sub URL.
735  */
736  const QUrl mainURL(url.fragment());
737  //qCDebug(KHTML_LOG) << "Handling error URL. URL count:" << urls.count();
738 
739  if (mainURL.isValid()) {
740  QString query = url.query(QUrl::FullyDecoded);
741  QRegularExpression pattern("error=(\\d+)&errText=(.*)");
742  QRegularExpressionMatch match = pattern.match(query);
743  int error = match.captured(1).toInt();
744  // error=0 isn't a valid error code, so 0 means it's missing from the URL
745  if (error == 0) {
746  error = KIO::ERR_UNKNOWN;
747  }
748  const QString errorText = match.captured(2);
749  d->m_workingURL = mainURL;
750  //qCDebug(KHTML_LOG) << "Emitting fixed URL " << d->m_workingURL;
751  emit d->m_extension->setLocationBarUrl(d->m_workingURL.toDisplayString());
752  htmlError(error, errorText, d->m_workingURL);
753  return true;
754  }
755  }
756 
757  if (!parentPart()) { // only do it for toplevel part
758  QString host = url.isLocalFile() ? "localhost" : url.host();
760  if (userAgent != KProtocolManager::userAgentForHost(QString())) {
761  if (!d->m_statusBarUALabel) {
762  d->m_statusBarUALabel = new KUrlLabel(d->m_statusBarExtension->statusBar());
763  d->m_statusBarUALabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum));
764  d->m_statusBarUALabel->setUseCursor(false);
765  d->m_statusBarExtension->addStatusBarItem(d->m_statusBarUALabel, 0, false);
766  d->m_statusBarUALabel->setPixmap(SmallIcon("preferences-web-browser-identification"));
767  }
768  d->m_statusBarUALabel->setToolTip(i18n("The fake user-agent '%1' is in use.", userAgent));
769  } else if (d->m_statusBarUALabel) {
770  d->m_statusBarExtension->removeStatusBarItem(d->m_statusBarUALabel);
771  delete d->m_statusBarUALabel;
772  d->m_statusBarUALabel = nullptr;
773  }
774  }
775 
776  KParts::BrowserArguments browserArgs(d->m_extension->browserArguments());
778 
779  // in case
780  // a) we have no frameset (don't test m_frames.count(), iframes get in there)
781  // b) the url is identical with the currently displayed one (except for the htmlref!)
782  // c) the url request is not a POST operation and
783  // d) the caller did not request to reload the page
784  // e) there was no HTTP redirection meanwhile (testcase: webmin's software/tree.cgi)
785  // => we don't reload the whole document and
786  // we just jump to the requested html anchor
787  bool isFrameSet = false;
788  if (d->m_doc && d->m_doc->isHTMLDocument()) {
789  HTMLDocumentImpl *htmlDoc = static_cast<HTMLDocumentImpl *>(d->m_doc);
790  isFrameSet = htmlDoc->body() && (htmlDoc->body()->id() == ID_FRAMESET);
791  }
792 
793  if (isFrameSet && d->isLocalAnchorJump(url) && browserArgs.softReload) {
794  QList<khtml::ChildFrame *>::Iterator it = d->m_frames.begin();
795  const QList<khtml::ChildFrame *>::Iterator end = d->m_frames.end();
796  for (; it != end; ++it) {
797  KHTMLPart *const part = qobject_cast<KHTMLPart *>((*it)->m_part.data());
798  if (part) {
799  // We are reloading frames to make them jump into offsets.
800  KParts::OpenUrlArguments partargs(part->arguments());
801  partargs.setReload(true);
802  part->setArguments(partargs);
803 
804  part->openUrl(part->url());
805  }
806  }/*next it*/
807  return true;
808  }
809 
810  if (url.hasFragment() && !isFrameSet) {
811  bool noReloadForced = !args.reload() && !browserArgs.redirectedRequest() && !browserArgs.doPost();
812  if (noReloadForced && d->isLocalAnchorJump(url)) {
813  // qCDebug(KHTML_LOG) << "jumping to anchor. m_url = " << url;
814  setUrl(url);
815  emit started(nullptr);
816 
819  }
820 
821  d->m_bComplete = true;
822  if (d->m_doc) {
823  d->m_doc->setParsing(false);
824  }
825 
826  // qCDebug(KHTML_LOG) << "completed...";
827  emit completed();
828  return true;
829  }
830  }
831 
832  // Save offset of viewport when page is reloaded to be compliant
833  // to every other capable browser out there.
834  if (args.reload()) {
835  args.setXOffset(d->m_view->contentsX());
836  args.setYOffset(d->m_view->contentsY());
837  setArguments(args);
838  }
839 
840  if (!d->m_restored) {
841  closeUrl();
842  }
843 
844  d->m_restoreScrollPosition = d->m_restored;
845  disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
846  connect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
847 
848  // Classify the mimetype. Some, like images and plugins are handled
849  // by wrapping things up in tags, so we want to plain output the HTML,
850  // and not start the job and all that (since we would want the
851  // KPart or whatever to load it).
852  // This is also the only place we need to do this, as it's for
853  // internal iframe use, not any other clients.
854  MimeType type = d->classifyMimeType(args.mimeType());
855 
856  if (type == MimeImage || type == MimeOther) {
857  begin(url, args.xOffset(), args.yOffset());
858  write(QString::fromLatin1("<html><head></head><body>"));
859  if (type == MimeImage) {
860  write(QString::fromLatin1("<img "));
861  } else {
862  write(QString::fromLatin1("<embed "));
863  }
864  write(QString::fromLatin1("src=\""));
865 
866  assert(url.toString().indexOf('"') == -1);
867  write(url.toString());
868 
869  write(QString::fromLatin1("\">"));
870  end();
871  return true;
872  }
873 
874  // 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
875  // data arrives) (Simon)
876  d->m_workingURL = url;
877  if (url.scheme().startsWith("http") && !url.host().isEmpty() &&
878  url.path().isEmpty()) {
879  d->m_workingURL.setPath("/");
880  emit d->m_extension->setLocationBarUrl(d->m_workingURL.toDisplayString());
881  }
882  setUrl(d->m_workingURL);
883 
884  QMap<QString, QString> &metaData = args.metaData();
885  metaData.insert("main_frame_request", parentPart() == nullptr ? "TRUE" : "FALSE");
886  metaData.insert("ssl_parent_ip", d->m_ssl_parent_ip);
887  metaData.insert("ssl_parent_cert", d->m_ssl_parent_cert);
888  metaData.insert("PropagateHttpHeader", "true");
889  metaData.insert("ssl_was_in_use", d->m_ssl_in_use ? "TRUE" : "FALSE");
890  metaData.insert("ssl_activate_warnings", "TRUE");
891  metaData.insert("cross-domain", toplevelURL().toString());
892 
893  if (d->m_restored) {
894  metaData.insert("referrer", d->m_pageReferrer);
895  d->m_cachePolicy = KIO::CC_Cache;
896  } else if (args.reload() && !browserArgs.softReload) {
897  d->m_cachePolicy = KIO::CC_Reload;
898  } else {
899  d->m_cachePolicy = KProtocolManager::cacheControl();
900  }
901 
902  if (browserArgs.doPost() && (url.scheme().startsWith("http"))) {
903  d->m_job = KIO::http_post(url, browserArgs.postData, KIO::HideProgressInfo);
904  d->m_job->addMetaData("content-type", browserArgs.contentType());
905  } else {
906  d->m_job = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo);
907  d->m_job->addMetaData("cache", KIO::getCacheControlString(d->m_cachePolicy));
908  }
909 
910  if (widget()) {
911  KJobWidgets::setWindow(d->m_job, widget()->topLevelWidget());
912  }
913  d->m_job->addMetaData(metaData);
914 
915  connect(d->m_job, SIGNAL(result(KJob*)),
916  SLOT(slotFinished(KJob*)));
917  connect(d->m_job, SIGNAL(data(KIO::Job*,QByteArray)),
918  SLOT(slotData(KIO::Job*,QByteArray)));
919  connect(d->m_job, SIGNAL(infoMessage(KJob*,QString,QString)),
920  SLOT(slotInfoMessage(KJob*,QString)));
921  connect(d->m_job, SIGNAL(redirection(KIO::Job*,QUrl)),
922  SLOT(slotRedirection(KIO::Job*,QUrl)));
923 
924  d->m_bComplete = false;
925  d->m_bLoadEventEmitted = false;
926 
927  // delete old status bar msg's from kjs (if it _was_ activated on last URL)
928  if (d->m_bJScriptEnabled) {
929  d->m_statusBarText[BarOverrideText].clear();
930  d->m_statusBarText[BarDefaultText].clear();
931  }
932 
933  // set the javascript flags according to the current url
934  d->m_bJScriptEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaScriptEnabled(url.host());
935  setDebugScript(KHTMLGlobal::defaultHTMLSettings()->isJavaScriptDebugEnabled());
936  d->m_bJavaEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaEnabled(url.host());
937  d->m_bPluginsEnabled = KHTMLGlobal::defaultHTMLSettings()->isPluginsEnabled(url.host());
938 
939  connect(d->m_job, SIGNAL(speed(KJob*,ulong)),
940  this, SLOT(slotJobSpeed(KJob*,ulong)));
941 
942  connect(d->m_job, SIGNAL(percent(KJob*,ulong)),
943  this, SLOT(slotJobPercent(KJob*,ulong)));
944 
945  connect(d->m_job, SIGNAL(result(KJob*)),
946  this, SLOT(slotJobDone(KJob*)));
947 
948  d->m_jobspeed = 0;
949 
950  // If this was an explicit reload and the user style sheet should be used,
951  // do a stat to see whether the stylesheet was changed in the meanwhile.
952  if (args.reload() && !settings()->userStyleSheet().isEmpty()) {
953  QUrl userStyleSheetUrl(settings()->userStyleSheet());
954  KIO::StatJob *job = KIO::stat(userStyleSheetUrl, KIO::HideProgressInfo);
955  connect(job, SIGNAL(result(KJob*)),
956  this, SLOT(slotUserSheetStatDone(KJob*)));
957  }
958  startingJob(d->m_job);
959  emit started(nullptr);
960 
961  return true;
962 }
963 
965 {
966  if (d->m_job) {
967  KHTMLPageCache::self()->cancelEntry(d->m_cacheId);
968  d->m_job->kill();
969  d->m_job = nullptr;
970  }
971 
972  if (d->m_doc && d->m_doc->isHTMLDocument()) {
973  HTMLDocumentImpl *hdoc = static_cast<HTMLDocumentImpl *>(d->m_doc);
974 
975  if (hdoc->body() && d->m_bLoadEventEmitted) {
976  hdoc->body()->dispatchWindowEvent(EventImpl::UNLOAD_EVENT, false, false);
977  if (d->m_doc) {
978  d->m_doc->updateRendering();
979  }
980  d->m_bLoadEventEmitted = false;
981  }
982  }
983 
984  d->m_bComplete = true; // to avoid emitting completed() in slotFinishedParsing() (David)
985  d->m_bLoadEventEmitted = true; // don't want that one either
986  d->m_cachePolicy = KProtocolManager::cacheControl(); // reset cache policy
987 
988  disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
989 
991  if (d->m_doc && d->m_doc->parsing()) {
992  // qCDebug(KHTML_LOG) << " was still parsing... calling end ";
993  slotFinishedParsing();
994  d->m_doc->setParsing(false);
995  }
996 
997  if (!d->m_workingURL.isEmpty()) {
998  // Aborted before starting to render
999  // 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 
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 
1058 {
1059  return d->m_doc;
1060 }
1061 
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 
1090 {
1091  return d->m_extension;
1092 }
1093 
1094 KParts::BrowserHostExtension *KHTMLPart::browserHostExtension() const
1095 {
1096  return d->m_hostExtension;
1097 }
1098 
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 
1121 {
1122  d->m_statusMessagesEnabled = enable;
1123 }
1124 
1126 {
1127  KJSProxy *proxy = jScript();
1128  if (!proxy || proxy->paused()) {
1129  return nullptr;
1130  }
1131 
1132  return proxy->interpreter();
1133 }
1134 
1136 {
1137  return d->m_statusMessagesEnabled;
1138 }
1139 
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 
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 
1162 {
1163  d->m_bDNSPrefetch = pmode;
1164  d->m_bDNSPrefetchIsDefault = false;
1165 }
1166 
1168 {
1169  if (onlyLocalReferences()) {
1170  return DNSPrefetchDisabled;
1171  }
1172  return d->m_bDNSPrefetch;
1173 }
1174 
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 
1378 {
1379  return executeScript(DOM::Node(), script);
1380 }
1381 
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 
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 
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 
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 
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 
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();
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 
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  }
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();
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 
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
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 
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 
2276 {
2277  write(data.data(), data.size());
2278  return true;
2279 }
2280 
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 
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();
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 
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 
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 
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 
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 slave 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 
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 
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 
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 
2874 {
2875  if (!d->m_doc) {
2876  return false;
2877  }
2878  d->m_view->focusNextPrevNode(true);
2879 
2880  return true;
2881 }
2882 
2884 {
2885  if (!d->m_doc) {
2886  return false;
2887  }
2888  d->m_view->focusNextPrevNode(false);
2889 
2890  return true;
2891 }
2892 
2894 {
2895  d->m_settings->setStdFontName(name);
2896 }
2897 
2899 {
2900  d->m_settings->setFixedFontName(name);
2901 }
2902 
2904 {
2905  d->m_linkCursor = c;
2906 }
2907 
2909 {
2910  return d->m_linkCursor;
2911 }
2912 
2914 {
2915  return d->m_onlyLocalReferences;
2916 }
2917 
2919 {
2920  d->m_onlyLocalReferences = enable;
2921 }
2922 
2924 {
2925  return d->m_forcePermitLocalImages;
2926 }
2927 
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  {
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  {
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 
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 
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 
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 
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 
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 
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 {
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 {
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 {
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 {
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 {
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 
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 
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 
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 
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 
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 
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 
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()) {
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)) {
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);
3974  dlg->setObjectName("KHTML Page Info Dialog");
3975  ui.setupUi(dlg);
3976 
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 {
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 
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 {
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 
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 {
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 
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) {
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 
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..
4717  if (!scriptExt) {
4718  // Try to fall back to LiveConnectExtension compat
4719  KParts::LiveConnectExtension *lc = KParts::LiveConnectExtension::childObject(part);
4720  if (lc) {
4722  }
4723  }
4724 
4725  if (scriptExt) {
4726  scriptExt->setHost(d->m_scriptableExtension);
4727  }
4728  child->m_scriptable = scriptExt;
4729  }
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 
4765 
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 
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 
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 
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")),
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")),
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 
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
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(