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 "dom/dom2_range.h"
43 #include "editing/editor.h"
44 #include "html/html_documentimpl.h"
45 #include "html/html_baseimpl.h"
46 #include "html/html_objectimpl.h"
47 #include "html/html_miscimpl.h"
48 #include "html/html_imageimpl.h"
49 #include "imload/imagemanager.h"
50 #include "rendering/render_text.h"
51 #include "rendering/render_frames.h"
52 #include "rendering/render_layer.h"
53 #include "rendering/render_position.h"
54 #include "misc/loader.h"
55 #include "misc/khtml_partaccessor.h"
56 #include "xml/dom2_eventsimpl.h"
57 #include "xml/dom2_rangeimpl.h"
58 #include "xml/xml_tokenizer.h"
59 #include "css/cssstyleselector.h"
60 using namespace DOM;
61 
62 #include "khtmlview.h"
63 #include <kparts/partmanager.h>
64 #include <kparts/browseropenorsavequestion.h>
65 #include <kparts/guiactivateevent.h>
66 
67 #include <kacceleratormanager.h>
68 #include "ecma/kjs_proxy.h"
69 #include "ecma/kjs_window.h"
70 #include "ecma/kjs_events.h"
71 #include "khtml_settings.h"
72 #include "kjserrordlg.h"
73 
74 #include <kjs/function.h>
75 #include <kjs/interpreter.h>
76 
77 #include <sys/types.h>
78 #include <assert.h>
79 
80 #include <kstringhandler.h>
81 #include <kio/job.h>
82 #include <kio/jobuidelegate.h>
83 #include <kio/global.h>
84 #include <kio/pixmaploader.h>
85 #include <kio/hostinfo.h>
86 #include <kprotocolmanager.h>
87 #include "khtml_debug.h"
88 #include <kjobwidgets.h>
89 #include <kmessagebox.h>
90 #include <kstandardaction.h>
91 #include <kstandardguiitem.h>
92 #include <kactioncollection.h>
93 #include <kmimetypetrader.h>
94 #include <qtemporaryfile.h>
95 #include <ktoolinvocation.h>
96 #include <kurlauthorized.h>
97 #include <kparts/browserinterface.h>
98 #include <kparts/scriptableextension.h>
99 #include <kparts/liveconnectextension.h>
100 #include <kactionmenu.h>
101 #include <ktoggleaction.h>
102 #include <kcodecaction.h>
103 #include <kselectaction.h>
104 
105 #include <QDBusConnection>
106 #include <ksslinfodialog.h>
107 #include <ksslsettings.h>
108 
109 #include <QDBusInterface>
110 #include <QMimeData>
111 #include <kfileitem.h>
112 #include <kurifilter.h>
113 #include <kurllabel.h>
114 #include <kurlmimedata.h>
115 
116 #include <QClipboard>
117 #include <QLocale>
118 #include <QMenu>
119 #include <QToolTip>
120 #include <QDrag>
121 #include <QMouseEvent>
122 #include <QFile>
123 #include <QMetaEnum>
124 #include <QTextDocument>
125 #include <QDate>
126 #include <QtNetwork/QSslCertificate>
127 #include <QStatusBar>
128 #include <QStyle>
129 #include <qmimedatabase.h>
130 #include <qplatformdefs.h>
131 #include <QFileInfo>
132 
133 #include "khtmlpart_p.h"
134 #include "khtml_iface.h"
135 
136 #include "kpassivepopup.h"
137 #include "rendering/render_form.h"
138 #include <kwindowsystem.h>
139 #include <kconfiggroup.h>
140 #include <ksharedconfig.h>
141 
142 #ifdef KJS_DEBUGGER
143 #include "ecma/debugger/debugwindow.h"
144 #endif
145 
146 // SVG
147 #include <svg/SVGDocument.h>
148 #include <qstandardpaths.h>
149 
150 bool KHTMLPartPrivate::s_dnsInitialised = false;
151 
152 // DNS prefetch settings
153 static const int sMaxDNSPrefetchPerPage = 42;
154 static const int sDNSPrefetchTimerDelay = 200;
155 static const int sDNSTTLSeconds = 400;
156 static const int sDNSCacheSize = 500;
157 
158 namespace khtml
159 {
160 
161 class PartStyleSheetLoader : public CachedObjectClient
162 {
163 public:
164  PartStyleSheetLoader(KHTMLPart *part, DOM::DOMString url, DocLoader *dl)
165  {
166  m_part = part;
167  m_cachedSheet = dl->requestStyleSheet(url, QString(), "text/css",
168  true /* "user sheet" */);
169  if (m_cachedSheet) {
170  m_cachedSheet->ref(this);
171  }
172  }
173  virtual ~PartStyleSheetLoader()
174  {
175  if (m_cachedSheet) {
176  m_cachedSheet->deref(this);
177  }
178  }
179  void setStyleSheet(const DOM::DOMString &, const DOM::DOMString &sheet, const DOM::DOMString &, const DOM::DOMString &/*mimetype*/) override
180  {
181  if (m_part) {
182  m_part->setUserStyleSheet(sheet.string());
183  }
184 
185  delete this;
186  }
187  void error(int, const QString &) override
188  {
189  delete this;
190  }
191  QPointer<KHTMLPart> m_part;
192  khtml::CachedCSSStyleSheet *m_cachedSheet;
193 };
194 }
195 
196 KHTMLPart::KHTMLPart(QWidget *parentWidget, QObject *parent, GUIProfile prof)
197  : KParts::ReadOnlyPart(parent)
198 {
199  d = nullptr;
200  KHTMLGlobal::registerPart(this);
201  setComponentData(KHTMLGlobal::aboutData(), false);
202  init(new KHTMLView(this, parentWidget), prof);
203 }
204 
206  : KParts::ReadOnlyPart(parent)
207 {
208  d = nullptr;
209  KHTMLGlobal::registerPart(this);
210  setComponentData(KHTMLGlobal::aboutData(), false);
211  assert(view);
212  if (!view->part()) {
213  view->setPart(this);
214  }
215  init(view, prof);
216 }
217 
218 void KHTMLPart::init(KHTMLView *view, GUIProfile prof)
219 {
220  if (prof == DefaultGUI) {
221  setXMLFile("khtml.rc");
222  } else if (prof == BrowserViewGUI) {
223  setXMLFile("khtml_browser.rc");
224  }
225 
226  d = new KHTMLPartPrivate(this, parent());
227 
228  d->m_view = view;
229 
230  if (!parentPart()) {
231  QWidget *widget = new QWidget(view->parentWidget());
232  widget->setObjectName("khtml_part_widget");
233  QVBoxLayout *layout = new QVBoxLayout(widget);
234  layout->setContentsMargins(0, 0, 0, 0);
235  layout->setSpacing(0);
236  widget->setLayout(layout);
237 
238  d->m_topViewBar = new KHTMLViewBar(KHTMLViewBar::Top, d->m_view, widget);
239  d->m_bottomViewBar = new KHTMLViewBar(KHTMLViewBar::Bottom, d->m_view, widget);
240 
241  layout->addWidget(d->m_topViewBar);
242  layout->addWidget(d->m_view);
243  layout->addWidget(d->m_bottomViewBar);
244  setWidget(widget);
245  widget->setFocusProxy(d->m_view);
246  } else {
247  setWidget(view);
248  }
249 
250  d->m_guiProfile = prof;
251  d->m_extension = new KHTMLPartBrowserExtension(this);
252  d->m_extension->setObjectName("KHTMLBrowserExtension");
253  d->m_hostExtension = new KHTMLPartBrowserHostExtension(this);
254  d->m_statusBarExtension = new KParts::StatusBarExtension(this);
255  d->m_scriptableExtension = new KJS::KHTMLPartScriptable(this);
256  new KHTMLTextExtension(this);
257  new KHTMLHtmlExtension(this);
258  d->m_statusBarPopupLabel = nullptr;
259  d->m_openableSuppressedPopups = 0;
260 
261  d->m_paLoadImages = nullptr;
262  d->m_paDebugScript = nullptr;
263  d->m_bMousePressed = false;
264  d->m_bRightMousePressed = false;
265  d->m_bCleared = false;
266 
267  if (prof == BrowserViewGUI) {
268  d->m_paViewDocument = new QAction(i18n("View Do&cument Source"), this);
269  actionCollection()->addAction("viewDocumentSource", d->m_paViewDocument);
270  connect(d->m_paViewDocument, SIGNAL(triggered(bool)), this, SLOT(slotViewDocumentSource()));
271  if (!parentPart()) {
273  }
274 
275  d->m_paViewFrame = new QAction(i18n("View Frame Source"), this);
276  actionCollection()->addAction("viewFrameSource", d->m_paViewFrame);
277  connect(d->m_paViewFrame, SIGNAL(triggered(bool)), this, SLOT(slotViewFrameSource()));
278  if (!parentPart()) {
280  }
281 
282  d->m_paViewInfo = new QAction(i18n("View Document Information"), this);
283  actionCollection()->addAction("viewPageInfo", d->m_paViewInfo);
284  if (!parentPart()) {
286  }
287  connect(d->m_paViewInfo, SIGNAL(triggered(bool)), this, SLOT(slotViewPageInfo()));
288 
289  d->m_paSaveBackground = new QAction(i18n("Save &Background Image As..."), this);
290  actionCollection()->addAction("saveBackground", d->m_paSaveBackground);
291  connect(d->m_paSaveBackground, SIGNAL(triggered(bool)), this, SLOT(slotSaveBackground()));
292 
293  d->m_paSaveDocument = actionCollection()->addAction(KStandardAction::SaveAs, "saveDocument",
294  this, SLOT(slotSaveDocument()));
295  if (parentPart()) {
296  d->m_paSaveDocument->setShortcuts(QList<QKeySequence>()); // avoid clashes
297  }
298 
299  d->m_paSaveFrame = new QAction(i18n("Save &Frame As..."), this);
300  actionCollection()->addAction("saveFrame", d->m_paSaveFrame);
301  connect(d->m_paSaveFrame, SIGNAL(triggered(bool)), this, SLOT(slotSaveFrame()));
302  } else {
303  d->m_paViewDocument = nullptr;
304  d->m_paViewFrame = nullptr;
305  d->m_paViewInfo = nullptr;
306  d->m_paSaveBackground = nullptr;
307  d->m_paSaveDocument = nullptr;
308  d->m_paSaveFrame = nullptr;
309  }
310 
311  d->m_paSecurity = new QAction(i18n("SSL"), this);
312  actionCollection()->addAction("security", d->m_paSecurity);
313  connect(d->m_paSecurity, SIGNAL(triggered(bool)), this, SLOT(slotSecurity()));
314 
315  d->m_paDebugRenderTree = new QAction(i18n("Print Rendering Tree to STDOUT"), this);
316  actionCollection()->addAction("debugRenderTree", d->m_paDebugRenderTree);
317  connect(d->m_paDebugRenderTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugRenderTree()));
318 
319  d->m_paDebugDOMTree = new QAction(i18n("Print DOM Tree to STDOUT"), this);
320  actionCollection()->addAction("debugDOMTree", d->m_paDebugDOMTree);
321  connect(d->m_paDebugDOMTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugDOMTree()));
322 
323  QAction *paDebugFrameTree = new QAction(i18n("Print frame tree to STDOUT"), this);
324  actionCollection()->addAction("debugFrameTree", paDebugFrameTree);
325  connect(paDebugFrameTree, SIGNAL(triggered(bool)), this, SLOT(slotDebugFrameTree()));
326 
327  d->m_paStopAnimations = new QAction(i18n("Stop Animated Images"), this);
328  actionCollection()->addAction("stopAnimations", d->m_paStopAnimations);
329  connect(d->m_paStopAnimations, SIGNAL(triggered(bool)), this, SLOT(slotStopAnimations()));
330 
331  d->m_paSetEncoding = new KCodecAction(QIcon::fromTheme("character-set"), i18n("Set &Encoding"), this, true);
332  actionCollection()->addAction("setEncoding", d->m_paSetEncoding);
333 // d->m_paSetEncoding->setDelayed( false );
334 
335  connect(d->m_paSetEncoding, SIGNAL(triggered(QString)), this, SLOT(slotSetEncoding(QString)));
336  connect(d->m_paSetEncoding, SIGNAL(triggered(KEncodingProber::ProberType)), this, SLOT(slotAutomaticDetectionLanguage(KEncodingProber::ProberType)));
337 
338  if (KSharedConfig::openConfig()->hasGroup("HTML Settings")) {
339  KConfigGroup config(KSharedConfig::openConfig(), "HTML Settings");
340 
341  d->m_autoDetectLanguage = static_cast<KEncodingProber::ProberType>(config.readEntry("AutomaticDetectionLanguage", /*static_cast<int>(language) */0));
342  if (d->m_autoDetectLanguage == KEncodingProber::None) {
344 // qCWarning(KHTML_LOG) << "00000000 ";
345  if (name.endsWith("1251") || name.startsWith("koi") || name == "iso-8859-5") {
346  d->m_autoDetectLanguage = KEncodingProber::Cyrillic;
347  } else if (name.endsWith("1256") || name == "iso-8859-6") {
348  d->m_autoDetectLanguage = KEncodingProber::Arabic;
349  } else if (name.endsWith("1257") || name == "iso-8859-13" || name == "iso-8859-4") {
350  d->m_autoDetectLanguage = KEncodingProber::Baltic;
351  } else if (name.endsWith("1250") || name == "ibm852" || name == "iso-8859-2" || name == "iso-8859-3") {
352  d->m_autoDetectLanguage = KEncodingProber::CentralEuropean;
353  } else if (name.endsWith("1253") || name == "iso-8859-7") {
354  d->m_autoDetectLanguage = KEncodingProber::Greek;
355  } else if (name.endsWith("1255") || name == "iso-8859-8" || name == "iso-8859-8-i") {
356  d->m_autoDetectLanguage = KEncodingProber::Hebrew;
357  } else if (name == "jis7" || name == "eucjp" || name == "sjis") {
358  d->m_autoDetectLanguage = KEncodingProber::Japanese;
359  } else if (name == "gb2312" || name == "gbk" || name == "gb18030") {
360  d->m_autoDetectLanguage = KEncodingProber::ChineseSimplified;
361  } else if (name == "big5") {
362  d->m_autoDetectLanguage = KEncodingProber::ChineseTraditional;
363  } else if (name == "euc-kr") {
364  d->m_autoDetectLanguage = KEncodingProber::Korean;
365  } else if (name.endsWith("1254") || name == "iso-8859-9") {
366  d->m_autoDetectLanguage = KEncodingProber::Turkish;
367  } else if (name.endsWith("1252") || name == "iso-8859-1" || name == "iso-8859-15") {
368  d->m_autoDetectLanguage = KEncodingProber::WesternEuropean;
369  } else {
370  d->m_autoDetectLanguage = KEncodingProber::Universal;
371  }
372 // qCWarning(KHTML_LOG) << "0000000end " << d->m_autoDetectLanguage << " " << QTextCodec::codecForLocale()->mibEnum();
373  }
374  d->m_paSetEncoding->setCurrentProberType(d->m_autoDetectLanguage);
375  }
376 
377  d->m_paUseStylesheet = new KSelectAction(i18n("Use S&tylesheet"), this);
378  actionCollection()->addAction("useStylesheet", d->m_paUseStylesheet);
379  connect(d->m_paUseStylesheet, SIGNAL(triggered(int)), this, SLOT(slotUseStylesheet()));
380 
381  if (prof == BrowserViewGUI) {
382  d->m_paIncZoomFactor = new KHTMLZoomFactorAction(this, true, "format-font-size-more", i18n("Enlarge Font"), this);
383  actionCollection()->addAction("incFontSizes", d->m_paIncZoomFactor);
384  connect(d->m_paIncZoomFactor, SIGNAL(triggered(bool)), SLOT(slotIncFontSizeFast()));
385  d->m_paIncZoomFactor->setWhatsThis(i18n("<qt>Enlarge Font<br /><br />"
386  "Make the font in this window bigger. "
387  "Click and hold down the mouse button for a menu with all available font sizes.</qt>"));
388 
389  d->m_paDecZoomFactor = new KHTMLZoomFactorAction(this, false, "format-font-size-less", i18n("Shrink Font"), this);
390  actionCollection()->addAction("decFontSizes", d->m_paDecZoomFactor);
391  connect(d->m_paDecZoomFactor, SIGNAL(triggered(bool)), SLOT(slotDecFontSizeFast()));
392  d->m_paDecZoomFactor->setWhatsThis(i18n("<qt>Shrink Font<br /><br />"
393  "Make the font in this window smaller. "
394  "Click and hold down the mouse button for a menu with all available font sizes.</qt>"));
395  if (!parentPart()) {
396  // For framesets, this action also affects frames, so only
397  // the frameset needs to define a shortcut for the action.
398 
399  // TODO: Why also CTRL+=? Because of http://trolltech.com/developer/knowledgebase/524/?
400  // Nobody else does it...
401  actionCollection()->setDefaultShortcut(d->m_paIncZoomFactor, QKeySequence("CTRL++; CTRL+="));
403  }
404  }
405 
406  d->m_paFind = actionCollection()->addAction(KStandardAction::Find, "find", this, SLOT(slotFind()));
407  d->m_paFind->setWhatsThis(i18n("<qt>Find text<br /><br />"
408  "Shows a dialog that allows you to find text on the displayed page.</qt>"));
409 
410  d->m_paFindNext = actionCollection()->addAction(KStandardAction::FindNext, "findNext", this, SLOT(slotFindNext()));
411  d->m_paFindNext->setWhatsThis(i18n("<qt>Find next<br /><br />"
412  "Find the next occurrence of the text that you "
413  "have found using the <b>Find Text</b> function.</qt>"));
414 
415  d->m_paFindPrev = actionCollection()->addAction(KStandardAction::FindPrev, "findPrevious",
416  this, SLOT(slotFindPrev()));
417  d->m_paFindPrev->setWhatsThis(i18n("<qt>Find previous<br /><br />"
418  "Find the previous occurrence of the text that you "
419  "have found using the <b>Find Text</b> function.</qt>"));
420 
421  // These two actions aren't visible in the menus, but exist for the (configurable) shortcut
422  d->m_paFindAheadText = new QAction(i18n("Find Text as You Type"), this);
423  actionCollection()->addAction("findAheadText", d->m_paFindAheadText);
424  actionCollection()->setDefaultShortcut(d->m_paFindAheadText, QKeySequence("/"));
425  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."));
426  d->m_paFindAheadText->setStatusTip(d->m_paFindAheadText->toolTip());
427  connect(d->m_paFindAheadText, SIGNAL(triggered(bool)), this, SLOT(slotFindAheadText()));
428 
429  d->m_paFindAheadLinks = new QAction(i18n("Find Links as You Type"), this);
430  actionCollection()->addAction("findAheadLink", d->m_paFindAheadLinks);
431  // The issue is that it sets the (sticky) option FindLinksOnly, so
432  // if you trigger this shortcut once by mistake, Esc and Ctrl+F will still have the option set.
433  // Better let advanced users configure a shortcut for this advanced option
434  //d->m_paFindAheadLinks->setShortcut( QKeySequence("\'") );
435  d->m_paFindAheadLinks->setToolTip(i18n("This shortcut shows the find bar, and sets the option \"Find links only\"."));
436  d->m_paFindAheadLinks->setStatusTip(d->m_paFindAheadLinks->toolTip());
437  connect(d->m_paFindAheadLinks, SIGNAL(triggered(bool)), this, SLOT(slotFindAheadLink()));
438 
439  if (parentPart()) {
440  d->m_paFind->setShortcuts(QList<QKeySequence>()); // avoid clashes
441  d->m_paFindNext->setShortcuts(QList<QKeySequence>()); // avoid clashes
442  d->m_paFindPrev->setShortcuts(QList<QKeySequence>()); // avoid clashes
443  d->m_paFindAheadText->setShortcuts(QList<QKeySequence>());
444  d->m_paFindAheadLinks->setShortcuts(QList<QKeySequence>());
445  }
446 
447  d->m_paPrintFrame = new QAction(i18n("Print Frame..."), this);
448  actionCollection()->addAction("printFrame", d->m_paPrintFrame);
449  d->m_paPrintFrame->setIcon(QIcon::fromTheme("document-print-frame"));
450  connect(d->m_paPrintFrame, SIGNAL(triggered(bool)), this, SLOT(slotPrintFrame()));
451  d->m_paPrintFrame->setWhatsThis(i18n("<qt>Print Frame<br /><br />"
452  "Some pages have several frames. To print only a single frame, click "
453  "on it and then use this function.</qt>"));
454 
455  // Warning: The name selectAll is used hardcoded by some 3rd parties to remove the
456  // shortcut for selectAll so they do not get ambigous shortcuts. Renaming it
457  // will either crash or render useless that workaround. It would be better
458  // to use the name KStandardAction::name(KStandardAction::SelectAll) but we
459  // can't for the same reason.
460  d->m_paSelectAll = actionCollection()->addAction(KStandardAction::SelectAll, "selectAll",
461  this, SLOT(slotSelectAll()));
462  if (parentPart()) { // Only the frameset has the shortcut, but the slot uses the current frame.
463  d->m_paSelectAll->setShortcuts(QList<QKeySequence>()); // avoid clashes
464  }
465 
466  d->m_paToggleCaretMode = new KToggleAction(i18n("Toggle Caret Mode"), this);
467  actionCollection()->addAction("caretMode", d->m_paToggleCaretMode);
468  actionCollection()->setDefaultShortcut(d->m_paToggleCaretMode, QKeySequence(Qt::Key_F7));
469  connect(d->m_paToggleCaretMode, SIGNAL(triggered(bool)), this, SLOT(slotToggleCaretMode()));
470  d->m_paToggleCaretMode->setChecked(isCaretMode());
471  if (parentPart()) {
472  d->m_paToggleCaretMode->setShortcuts(QList<QKeySequence>()); // avoid clashes
473  }
474 
475  // set the default java(script) flags according to the current host.
476  d->m_bOpenMiddleClick = d->m_settings->isOpenMiddleClickEnabled();
477  d->m_bJScriptEnabled = d->m_settings->isJavaScriptEnabled();
478  setDebugScript(d->m_settings->isJavaScriptDebugEnabled());
479  d->m_bJavaEnabled = d->m_settings->isJavaEnabled();
480  d->m_bPluginsEnabled = d->m_settings->isPluginsEnabled();
481 
482  // Set the meta-refresh flag...
483  d->m_metaRefreshEnabled = d->m_settings->isAutoDelayedActionsEnabled();
484 
485  KHTMLSettings::KSmoothScrollingMode ssm = d->m_settings->smoothScrolling();
486  if (ssm == KHTMLSettings::KSmoothScrollingDisabled) {
487  d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMDisabled);
488  } else if (ssm == KHTMLSettings::KSmoothScrollingWhenEfficient) {
489  d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMWhenEfficient);
490  } else {
491  d->m_view->setSmoothScrollingModeDefault(KHTMLView::SSMEnabled);
492  }
493 
494  if (d->m_bDNSPrefetchIsDefault && !onlyLocalReferences()) {
495  KHTMLSettings::KDNSPrefetch dpm = d->m_settings->dnsPrefetch();
496  if (dpm == KHTMLSettings::KDNSPrefetchDisabled) {
497  d->m_bDNSPrefetch = DNSPrefetchDisabled;
498  } else if (dpm == KHTMLSettings::KDNSPrefetchOnlyWWWAndSLD) {
499  d->m_bDNSPrefetch = DNSPrefetchOnlyWWWAndSLD;
500  } else {
501  d->m_bDNSPrefetch = DNSPrefetchEnabled;
502  }
503  }
504 
505  if (!KHTMLPartPrivate::s_dnsInitialised && d->m_bDNSPrefetch != DNSPrefetchDisabled) {
506  KIO::HostInfo::setCacheSize(sDNSCacheSize);
507  KIO::HostInfo::setTTL(sDNSTTLSeconds);
508  KHTMLPartPrivate::s_dnsInitialised = true;
509  }
510 
511  // all shortcuts should only be active, when this part has focus
512  foreach (QAction *action, actionCollection()->actions()) {
514  }
516 
517  connect(view, SIGNAL(zoomView(int)), SLOT(slotZoomView(int)));
518 
519  connect(this, SIGNAL(completed()),
520  this, SLOT(updateActions()));
521  connect(this, SIGNAL(completed(bool)),
522  this, SLOT(updateActions()));
523  connect(this, SIGNAL(started(KIO::Job*)),
524  this, SLOT(updateActions()));
525 
526  // #### FIXME: the process wide loader is going to signal every part about every loaded object.
527  // That's quite inefficient. Should be per-document-tree somehow. Even signaling to
528  // child parts that a request from an ancestor has loaded is inefficent..
529  connect(khtml::Cache::loader(), SIGNAL(requestStarted(khtml::DocLoader*,khtml::CachedObject*)),
530  this, SLOT(slotLoaderRequestStarted(khtml::DocLoader*,khtml::CachedObject*)));
531  connect(khtml::Cache::loader(), SIGNAL(requestDone(khtml::DocLoader*,khtml::CachedObject*)),
532  this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
533  connect(khtml::Cache::loader(), SIGNAL(requestFailed(khtml::DocLoader*,khtml::CachedObject*)),
534  this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
535 
536  connect(&d->m_progressUpdateTimer, SIGNAL(timeout()), this, SLOT(slotProgressUpdate()));
537 
538  findTextBegin(); //reset find variables
539 
540  connect(&d->m_redirectionTimer, SIGNAL(timeout()),
541  this, SLOT(slotRedirect()));
542 
543  if (QDBusConnection::sessionBus().isConnected()) {
544  new KHTMLPartIface(this); // our "adaptor"
545  for (int i = 1;; ++i)
546  if (QDBusConnection::sessionBus().registerObject(QString("/KHTML/%1/widget").arg(i), this)) {
547  break;
548  } else if (i == 0xffff) {
549  qFatal("Something is very wrong in KHTMLPart!");
550  }
551  }
552 
553  if (prof == BrowserViewGUI && !parentPart()) {
554  loadPlugins();
555  }
556 }
557 
559 {
560  // qCDebug(KHTML_LOG) << this;
561  KConfigGroup config(KSharedConfig::openConfig(), "HTML Settings");
562  config.writeEntry("AutomaticDetectionLanguage", int(d->m_autoDetectLanguage));
563 
564  if (d->m_manager) { // the PartManager for this part's children
565  d->m_manager->removePart(this);
566  }
567 
568  slotWalletClosed();
569  if (!parentPart()) { // only delete it if the top khtml_part closes
570  removeJSErrorExtension();
571  }
572 
573  stopAutoScroll();
574  d->m_redirectionTimer.stop();
575 
576  if (!d->m_bComplete) {
577  closeUrl();
578  }
579 
580  disconnect(khtml::Cache::loader(), SIGNAL(requestStarted(khtml::DocLoader*,khtml::CachedObject*)),
581  this, SLOT(slotLoaderRequestStarted(khtml::DocLoader*,khtml::CachedObject*)));
582  disconnect(khtml::Cache::loader(), SIGNAL(requestDone(khtml::DocLoader*,khtml::CachedObject*)),
583  this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
584  disconnect(khtml::Cache::loader(), SIGNAL(requestFailed(khtml::DocLoader*,khtml::CachedObject*)),
585  this, SLOT(slotLoaderRequestDone(khtml::DocLoader*,khtml::CachedObject*)));
586 
587  clear();
588  hide();
589 
590  if (d->m_view) {
591  d->m_view->m_part = nullptr;
592  }
593 
594  // Have to delete this here since we forward declare it in khtmlpart_p and
595  // at least some compilers won't call the destructor in this case.
596  delete d->m_jsedlg;
597  d->m_jsedlg = nullptr;
598 
599  if (!parentPart()) { // only delete d->m_frame if the top khtml_part closes
600  delete d->m_frame;
601  } else if (d->m_frame && d->m_frame->m_run) { // for kids, they may get detached while
602  d->m_frame->m_run.data()->abort(); // resolving mimetype; cancel that if needed
603  }
604  delete d; d = nullptr;
605  KHTMLGlobal::deregisterPart(this);
606 }
607 
608 bool KHTMLPart::restoreURL(const QUrl &url)
609 {
610  // qCDebug(KHTML_LOG) << url;
611 
612  d->m_redirectionTimer.stop();
613 
614  /*
615  * That's not a good idea as it will call closeUrl() on all
616  * child frames, preventing them from further loading. This
617  * method gets called from restoreState() in case of a full frameset
618  * restoral, and restoreState() calls closeUrl() before restoring
619  * anyway.
620  // qCDebug(KHTML_LOG) << "closing old URL";
621  closeUrl();
622  */
623 
624  d->m_bComplete = false;
625  d->m_bLoadEventEmitted = false;
626  d->m_workingURL = url;
627 
628  // set the java(script) flags according to the current host.
629  d->m_bJScriptEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaScriptEnabled(url.host());
630  setDebugScript(KHTMLGlobal::defaultHTMLSettings()->isJavaScriptDebugEnabled());
631  d->m_bJavaEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaEnabled(url.host());
632  d->m_bPluginsEnabled = KHTMLGlobal::defaultHTMLSettings()->isPluginsEnabled(url.host());
633 
634  setUrl(url);
635 
636  d->m_restoreScrollPosition = true;
637  disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
638  connect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
639 
640  KHTMLPageCache::self()->fetchData(d->m_cacheId, this, SLOT(slotRestoreData(QByteArray)));
641 
642  emit started(nullptr);
643 
644  return true;
645 }
646 
647 static bool areUrlsForSamePage(const QUrl &url1, const QUrl &url2)
648 {
650  u1.setFragment(QString());
651  if (u1.path() == QLatin1String("/")) {
652  u1.setPath(QString());
653  }
655  u2.setFragment(QString());
656  if (u2.path() == QLatin1String("/")) {
657  u2.setPath(QString());
658  }
659  return u1 == u2;
660 }
661 
662 bool KHTMLPartPrivate::isLocalAnchorJump(const QUrl &url)
663 {
664  // kio_help actually uses fragments to identify different pages, so
665  // always reload with it.
666  if (url.scheme() == QLatin1String("help")) {
667  return false;
668  }
669 
670  return url.hasFragment() && areUrlsForSamePage(url, q->url());
671 }
672 
673 void KHTMLPartPrivate::executeAnchorJump(const QUrl &url, bool lockHistory)
674 {
675  DOM::HashChangeEventImpl *hashChangeEvImpl = nullptr;
676  const QString &oldRef = q->url().fragment(QUrl::FullyEncoded);
677  const QString &newRef = url.fragment(QUrl::FullyEncoded);
678  const bool hashChanged = (oldRef != newRef) || (oldRef.isNull() && newRef.isEmpty());
679 
680  if (hashChanged) {
681  // Note: we want to emit openUrlNotify first thing to make the history capture the old state,
682  // however do not update history if a lock was explicitly requested, e.g. Location.replace()
683  if (!lockHistory) {
684  emit m_extension->openUrlNotify();
685  }
686  // Create hashchange event
687  hashChangeEvImpl = new DOM::HashChangeEventImpl();
688  hashChangeEvImpl->initHashChangeEvent("hashchange",
689  true, //bubble
690  false, //cancelable
691  q->url().toString(), //oldURL
692  url.toString() //newURL
693  );
694  }
695 
696  if (!q->gotoAnchor(newRef)) { // encoded fragment
697  q->gotoAnchor(url.fragment(QUrl::FullyDecoded)); // not encoded fragment
698  }
699 
700  q->setUrl(url);
701  emit m_extension->setLocationBarUrl(url.toDisplayString());
702 
703  if (hashChangeEvImpl) {
704  m_doc->dispatchWindowEvent(hashChangeEvImpl);
705  }
706 }
707 
708 bool KHTMLPart::openUrl(const QUrl &url)
709 {
710  // qCDebug(KHTML_LOG) << this << "opening" << url;
711 
712 #ifndef KHTML_NO_WALLET
713  // Wallet forms are per page, so clear it when loading a different page if we
714  // are not an iframe (because we store walletforms only on the topmost part).
715  if (!parentPart()) {
716  d->m_walletForms.clear();
717  }
718 #endif
719  d->m_redirectionTimer.stop();
720 
721  // check to see if this is an "error://" URL. This is caused when an error
722  // occurs before this part was loaded (e.g. KonqRun), and is passed to
723  // khtmlpart so that it can display the error.
724  if (url.scheme() == "error") {
725  closeUrl();
726 
727  if (d->m_bJScriptEnabled) {
728  d->m_statusBarText[BarOverrideText].clear();
729  d->m_statusBarText[BarDefaultText].clear();
730  }
731 
737  const QUrl mainURL(url.fragment());
738  //qCDebug(KHTML_LOG) << "Handling error URL. URL count:" << urls.count();
739 
740  if (mainURL.isValid()) {
741  QString query = url.query(QUrl::FullyDecoded);
742  QRegularExpression pattern("error=(\\d+)&errText=(.*)");
743  QRegularExpressionMatch match = pattern.match(query);
744  int error = match.captured(1).toInt();
745  // error=0 isn't a valid error code, so 0 means it's missing from the URL
746  if (error == 0) {
747  error = KIO::ERR_UNKNOWN;
748  }
749  const QString errorText = match.captured(2);
750  d->m_workingURL = mainURL;
751  //qCDebug(KHTML_LOG) << "Emitting fixed URL " << d->m_workingURL;
752  emit d->m_extension->setLocationBarUrl(d->m_workingURL.toDisplayString());
753  htmlError(error, errorText, d->m_workingURL);
754  return true;
755  }
756  }
757 
758  if (!parentPart()) { // only do it for toplevel part
759  QString host = url.isLocalFile() ? "localhost" : url.host();
761  if (userAgent != KProtocolManager::userAgentForHost(QString())) {
762  if (!d->m_statusBarUALabel) {
763  d->m_statusBarUALabel = new KUrlLabel(d->m_statusBarExtension->statusBar());
764  d->m_statusBarUALabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum));
765  d->m_statusBarUALabel->setUseCursor(false);
766  d->m_statusBarExtension->addStatusBarItem(d->m_statusBarUALabel, 0, false);
767  d->m_statusBarUALabel->setPixmap(SmallIcon("preferences-web-browser-identification"));
768  }
769  d->m_statusBarUALabel->setToolTip(i18n("The fake user-agent '%1' is in use.", userAgent));
770  } else if (d->m_statusBarUALabel) {
771  d->m_statusBarExtension->removeStatusBarItem(d->m_statusBarUALabel);
772  delete d->m_statusBarUALabel;
773  d->m_statusBarUALabel = nullptr;
774  }
775  }
776 
777  KParts::BrowserArguments browserArgs(d->m_extension->browserArguments());
779 
780  // in case
781  // a) we have no frameset (don't test m_frames.count(), iframes get in there)
782  // b) the url is identical with the currently displayed one (except for the htmlref!)
783  // c) the url request is not a POST operation and
784  // d) the caller did not request to reload the page
785  // e) there was no HTTP redirection meanwhile (testcase: webmin's software/tree.cgi)
786  // => we don't reload the whole document and
787  // we just jump to the requested html anchor
788  bool isFrameSet = false;
789  if (d->m_doc && d->m_doc->isHTMLDocument()) {
790  HTMLDocumentImpl *htmlDoc = static_cast<HTMLDocumentImpl *>(d->m_doc);
791  isFrameSet = htmlDoc->body() && (htmlDoc->body()->id() == ID_FRAMESET);
792  }
793 
794  if (isFrameSet && d->isLocalAnchorJump(url) && browserArgs.softReload) {
795  QList<khtml::ChildFrame *>::Iterator it = d->m_frames.begin();
796  const QList<khtml::ChildFrame *>::Iterator end = d->m_frames.end();
797  for (; it != end; ++it) {
798  KHTMLPart *const part = qobject_cast<KHTMLPart *>((*it)->m_part.data());
799  if (part) {
800  // We are reloading frames to make them jump into offsets.
801  KParts::OpenUrlArguments partargs(part->arguments());
802  partargs.setReload(true);
803  part->setArguments(partargs);
804 
805  part->openUrl(part->url());
806  }
807  }/*next it*/
808  return true;
809  }
810 
811  if (url.hasFragment() && !isFrameSet) {
812  bool noReloadForced = !args.reload() && !browserArgs.redirectedRequest() && !browserArgs.doPost();
813  if (noReloadForced && d->isLocalAnchorJump(url)) {
814  // qCDebug(KHTML_LOG) << "jumping to anchor. m_url = " << url;
815  setUrl(url);
816  emit started(nullptr);
817 
820  }
821 
822  d->m_bComplete = true;
823  if (d->m_doc) {
824  d->m_doc->setParsing(false);
825  }
826 
827  // qCDebug(KHTML_LOG) << "completed...";
828  emit completed();
829  return true;
830  }
831  }
832 
833  // Save offset of viewport when page is reloaded to be compliant
834  // to every other capable browser out there.
835  if (args.reload()) {
836  args.setXOffset(d->m_view->contentsX());
837  args.setYOffset(d->m_view->contentsY());
838  setArguments(args);
839  }
840 
841  if (!d->m_restored) {
842  closeUrl();
843  }
844 
845  d->m_restoreScrollPosition = d->m_restored;
846  disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
847  connect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
848 
849  // Classify the mimetype. Some, like images and plugins are handled
850  // by wrapping things up in tags, so we want to plain output the HTML,
851  // and not start the job and all that (since we would want the
852  // KPart or whatever to load it).
853  // This is also the only place we need to do this, as it's for
854  // internal iframe use, not any other clients.
855  MimeType type = d->classifyMimeType(args.mimeType());
856 
857  if (type == MimeImage || type == MimeOther) {
858  begin(url, args.xOffset(), args.yOffset());
859  write(QString::fromLatin1("<html><head></head><body>"));
860  if (type == MimeImage) {
861  write(QString::fromLatin1("<img "));
862  } else {
863  write(QString::fromLatin1("<embed "));
864  }
865  write(QString::fromLatin1("src=\""));
866 
867  assert(url.toString().indexOf('"') == -1);
868  write(url.toString());
869 
870  write(QString::fromLatin1("\">"));
871  end();
872  return true;
873  }
874 
875  // 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
876  // data arrives) (Simon)
877  d->m_workingURL = url;
878  if (url.scheme().startsWith("http") && !url.host().isEmpty() &&
879  url.path().isEmpty()) {
880  d->m_workingURL.setPath("/");
881  emit d->m_extension->setLocationBarUrl(d->m_workingURL.toDisplayString());
882  }
883  setUrl(d->m_workingURL);
884 
885  QMap<QString, QString> &metaData = args.metaData();
886  metaData.insert("main_frame_request", parentPart() == nullptr ? "TRUE" : "FALSE");
887  metaData.insert("ssl_parent_ip", d->m_ssl_parent_ip);
888  metaData.insert("ssl_parent_cert", d->m_ssl_parent_cert);
889  metaData.insert("PropagateHttpHeader", "true");
890  metaData.insert("ssl_was_in_use", d->m_ssl_in_use ? "TRUE" : "FALSE");
891  metaData.insert("ssl_activate_warnings", "TRUE");
892  metaData.insert("cross-domain", toplevelURL().toString());
893 
894  if (d->m_restored) {
895  metaData.insert("referrer", d->m_pageReferrer);
896  d->m_cachePolicy = KIO::CC_Cache;
897  } else if (args.reload() && !browserArgs.softReload) {
898  d->m_cachePolicy = KIO::CC_Reload;
899  } else {
900  d->m_cachePolicy = KProtocolManager::cacheControl();
901  }
902 
903  if (browserArgs.doPost() && (url.scheme().startsWith("http"))) {
904  d->m_job = KIO::http_post(url, browserArgs.postData, KIO::HideProgressInfo);
905  d->m_job->addMetaData("content-type", browserArgs.contentType());
906  } else {
907  d->m_job = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo);
908  d->m_job->addMetaData("cache", KIO::getCacheControlString(d->m_cachePolicy));
909  }
910 
911  if (widget()) {
912  KJobWidgets::setWindow(d->m_job, widget()->topLevelWidget());
913  }
914  d->m_job->addMetaData(metaData);
915 
916  connect(d->m_job, SIGNAL(result(KJob*)),
917  SLOT(slotFinished(KJob*)));
918  connect(d->m_job, SIGNAL(data(KIO::Job*,QByteArray)),
919  SLOT(slotData(KIO::Job*,QByteArray)));
920  connect(d->m_job, SIGNAL(infoMessage(KJob*,QString,QString)),
921  SLOT(slotInfoMessage(KJob*,QString)));
922  connect(d->m_job, SIGNAL(redirection(KIO::Job*,QUrl)),
923  SLOT(slotRedirection(KIO::Job*,QUrl)));
924 
925  d->m_bComplete = false;
926  d->m_bLoadEventEmitted = false;
927 
928  // delete old status bar msg's from kjs (if it _was_ activated on last URL)
929  if (d->m_bJScriptEnabled) {
930  d->m_statusBarText[BarOverrideText].clear();
931  d->m_statusBarText[BarDefaultText].clear();
932  }
933 
934  // set the javascript flags according to the current url
935  d->m_bJScriptEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaScriptEnabled(url.host());
936  setDebugScript(KHTMLGlobal::defaultHTMLSettings()->isJavaScriptDebugEnabled());
937  d->m_bJavaEnabled = KHTMLGlobal::defaultHTMLSettings()->isJavaEnabled(url.host());
938  d->m_bPluginsEnabled = KHTMLGlobal::defaultHTMLSettings()->isPluginsEnabled(url.host());
939 
940  connect(d->m_job, SIGNAL(speed(KJob*,ulong)),
941  this, SLOT(slotJobSpeed(KJob*,ulong)));
942 
943  connect(d->m_job, SIGNAL(percent(KJob*,ulong)),
944  this, SLOT(slotJobPercent(KJob*,ulong)));
945 
946  connect(d->m_job, SIGNAL(result(KJob*)),
947  this, SLOT(slotJobDone(KJob*)));
948 
949  d->m_jobspeed = 0;
950 
951  // If this was an explicit reload and the user style sheet should be used,
952  // do a stat to see whether the stylesheet was changed in the meanwhile.
953  if (args.reload() && !settings()->userStyleSheet().isEmpty()) {
954  QUrl userStyleSheetUrl(settings()->userStyleSheet());
955  KIO::StatJob *job = KIO::stat(userStyleSheetUrl, KIO::HideProgressInfo);
956  connect(job, SIGNAL(result(KJob*)),
957  this, SLOT(slotUserSheetStatDone(KJob*)));
958  }
959  startingJob(d->m_job);
960  emit started(nullptr);
961 
962  return true;
963 }
964 
966 {
967  if (d->m_job) {
968  KHTMLPageCache::self()->cancelEntry(d->m_cacheId);
969  d->m_job->kill();
970  d->m_job = nullptr;
971  }
972 
973  if (d->m_doc && d->m_doc->isHTMLDocument()) {
974  HTMLDocumentImpl *hdoc = static_cast<HTMLDocumentImpl *>(d->m_doc);
975 
976  if (hdoc->body() && d->m_bLoadEventEmitted) {
977  hdoc->body()->dispatchWindowEvent(EventImpl::UNLOAD_EVENT, false, false);
978  if (d->m_doc) {
979  d->m_doc->updateRendering();
980  }
981  d->m_bLoadEventEmitted = false;
982  }
983  }
984 
985  d->m_bComplete = true; // to avoid emitting completed() in slotFinishedParsing() (David)
986  d->m_bLoadEventEmitted = true; // don't want that one either
987  d->m_cachePolicy = KProtocolManager::cacheControl(); // reset cache policy
988 
989  disconnect(d->m_view, SIGNAL(finishedLayout()), this, SLOT(restoreScrollPosition()));
990 
992  if (d->m_doc && d->m_doc->parsing()) {
993  // qCDebug(KHTML_LOG) << " was still parsing... calling end ";
994  slotFinishedParsing();
995  d->m_doc->setParsing(false);
996  }
997 
998  if (!d->m_workingURL.isEmpty()) {
999  // Aborted before starting to render
1000  // qCDebug(KHTML_LOG) << "Aborted before starting to render, reverting location bar to " << url();
1001  emit d->m_extension->setLocationBarUrl(url().toDisplayString());
1002  }
1003 
1004  d->m_workingURL = QUrl();
1005 
1006  if (d->m_doc && d->m_doc->docLoader()) {
1007  khtml::Cache::loader()->cancelRequests(d->m_doc->docLoader());
1008  }
1009 
1010  // tell all subframes to stop as well
1011  {
1012  ConstFrameIt it = d->m_frames.constBegin();
1013  const ConstFrameIt end = d->m_frames.constEnd();
1014  for (; it != end; ++it) {
1015  if ((*it)->m_run) {
1016  (*it)->m_run.data()->abort();
1017  }
1018  if (!(*it)->m_part.isNull()) {
1019  (*it)->m_part.data()->closeUrl();
1020  }
1021  }
1022  }
1023  // tell all objects to stop as well
1024  {
1025  ConstFrameIt it = d->m_objects.constBegin();
1026  const ConstFrameIt end = d->m_objects.constEnd();
1027  for (; it != end; ++it) {
1028  if (!(*it)->m_part.isNull()) {
1029  (*it)->m_part.data()->closeUrl();
1030  }
1031  }
1032  }
1033  // Stop any started redirections as well!! (DA)
1034  if (d && d->m_redirectionTimer.isActive()) {
1035  d->m_redirectionTimer.stop();
1036  }
1037 
1038  // null node activated.
1039  emit nodeActivated(Node());
1040 
1041  // make sure before clear() runs, we pop out of a dialog's message loop
1042  if (d->m_view) {
1043  d->m_view->closeChildDialogs();
1044  }
1045 
1046  return true;
1047 }
1048 
1050 {
1051  if (d->m_doc && d->m_doc->isHTMLDocument()) {
1052  return static_cast<HTMLDocumentImpl *>(d->m_doc);
1053  } else {
1054  return static_cast<HTMLDocumentImpl *>(nullptr);
1055  }
1056 }
1057 
1059 {
1060  return d->m_doc;
1061 }
1062 
1064 {
1065  QString sourceStr;
1066  if (!(url().isLocalFile()) && KHTMLPageCache::self()->isComplete(d->m_cacheId)) {
1067  QByteArray sourceArray;
1068  QDataStream dataStream(&sourceArray, QIODevice::WriteOnly);
1069  KHTMLPageCache::self()->saveData(d->m_cacheId, &dataStream);
1070  QTextStream stream(sourceArray, QIODevice::ReadOnly);
1071  stream.setCodec(QTextCodec::codecForName(encoding().toLatin1().constData()));
1072  sourceStr = stream.readAll();
1073  } else {
1074  QTemporaryFile tmpFile;
1075  if (!tmpFile.open()) {
1076  return sourceStr;
1077  }
1078 
1079  KIO::FileCopyJob *job = KIO::file_copy(url(), QUrl::fromLocalFile(tmpFile.fileName()), KIO::Overwrite);
1080  if (job->exec()) {
1081  QTextStream stream(&tmpFile);
1082  stream.setCodec(QTextCodec::codecForName(encoding().toLatin1().constData()));
1083  sourceStr = stream.readAll();
1084  }
1085  }
1086 
1087  return sourceStr;
1088 }
1089 
1091 {
1092  return d->m_extension;
1093 }
1094 
1095 KParts::BrowserHostExtension *KHTMLPart::browserHostExtension() const
1096 {
1097  return d->m_hostExtension;
1098 }
1099 
1101 {
1102  return d->m_view;
1103 }
1104 
1105 KHTMLViewBar *KHTMLPart::pTopViewBar() const
1106 {
1107  if (const_cast<KHTMLPart *>(this)->parentPart()) {
1108  return const_cast<KHTMLPart *>(this)->parentPart()->pTopViewBar();
1109  }
1110  return d->m_topViewBar;
1111 }
1112 
1113 KHTMLViewBar *KHTMLPart::pBottomViewBar() const
1114 {
1115  if (const_cast<KHTMLPart *>(this)->parentPart()) {
1116  return const_cast<KHTMLPart *>(this)->parentPart()->pBottomViewBar();
1117  }
1118  return d->m_bottomViewBar;
1119 }
1120 
1122 {
1123  d->m_statusMessagesEnabled = enable;
1124 }
1125 
1127 {
1128  KJSProxy *proxy = jScript();
1129  if (!proxy || proxy->paused()) {
1130  return nullptr;
1131  }
1132 
1133  return proxy->interpreter();
1134 }
1135 
1137 {
1138  return d->m_statusMessagesEnabled;
1139 }
1140 
1142 {
1143  if (!enable && jScriptEnabled() && d->m_frame && d->m_frame->m_jscript) {
1144  d->m_frame->m_jscript->clear();
1145  }
1146  d->m_bJScriptForce = enable;
1147  d->m_bJScriptOverride = true;
1148 }
1149 
1151 {
1152  if (onlyLocalReferences()) {
1153  return false;
1154  }
1155 
1156  if (d->m_bJScriptOverride) {
1157  return d->m_bJScriptForce;
1158  }
1159  return d->m_bJScriptEnabled;
1160 }
1161 
1163 {
1164  d->m_bDNSPrefetch = pmode;
1165  d->m_bDNSPrefetchIsDefault = false;
1166 }
1167 
1169 {
1170  if (onlyLocalReferences()) {
1171  return DNSPrefetchDisabled;
1172  }
1173  return d->m_bDNSPrefetch;
1174 }
1175 
1177 {
1178  d->m_metaRefreshEnabled = enable;
1179 }
1180 
1181 bool KHTMLPart::metaRefreshEnabled() const
1182 {
1183  return d->m_metaRefreshEnabled;
1184 }
1185 
1186 KJSProxy *KHTMLPart::jScript()
1187 {
1188  if (!jScriptEnabled()) {
1189  return nullptr;
1190  }
1191 
1192  if (!d->m_frame) {
1193  KHTMLPart *p = parentPart();
1194  if (!p) {
1195  d->m_frame = new khtml::ChildFrame;
1196  d->m_frame->m_part = this;
1197  } else {
1198  ConstFrameIt it = p->d->m_frames.constBegin();
1199  const ConstFrameIt end = p->d->m_frames.constEnd();
1200  for (; it != end; ++it)
1201  if ((*it)->m_part.data() == this) {
1202  d->m_frame = *it;
1203  break;
1204  }
1205  }
1206  if (!d->m_frame) {
1207  return nullptr;
1208  }
1209  }
1210  if (!d->m_frame->m_jscript) {
1211  d->m_frame->m_jscript = new KJSProxy(d->m_frame);
1212  }
1213  d->m_frame->m_jscript->setDebugEnabled(d->m_bJScriptDebugEnabled);
1214 
1215  return d->m_frame->m_jscript;
1216 }
1217 
1218 QVariant KHTMLPart::crossFrameExecuteScript(const QString &target, const QString &script)
1219 {
1220  KHTMLPart *destpart = this;
1221 
1222  QString trg = target.toLower();
1223 
1224  if (target == "_top") {
1225  while (destpart->parentPart()) {
1226  destpart = destpart->parentPart();
1227  }
1228  } else if (target == "_parent") {
1229  if (parentPart()) {
1230  destpart = parentPart();
1231  }
1232  } else if (target == "_self" || target == "_blank") {
1233  // we always allow these
1234  } else {
1235  destpart = findFrame(target);
1236  if (!destpart) {
1237  destpart = this;
1238  }
1239  }
1240 
1241  // easy way out?
1242  if (destpart == this) {
1243  return executeScript(DOM::Node(), script);
1244  }
1245 
1246  // now compare the domains
1247  if (destpart->checkFrameAccess(this)) {
1248  return destpart->executeScript(DOM::Node(), script);
1249  }
1250 
1251  // eww, something went wrong. better execute it in our frame
1252  return executeScript(DOM::Node(), script);
1253 }
1254 
1255 //Enable this to see all JS scripts being executed
1256 //#define KJS_VERBOSE
1257 
1258 KJSErrorDlg *KHTMLPart::jsErrorExtension()
1259 {
1260  if (!d->m_settings->jsErrorsEnabled()) {
1261  return nullptr;
1262  }
1263 
1264  if (parentPart()) {
1265  return parentPart()->jsErrorExtension();
1266  }
1267 
1268  if (!d->m_statusBarJSErrorLabel) {
1269  d->m_statusBarJSErrorLabel = new KUrlLabel(d->m_statusBarExtension->statusBar());
1270  d->m_statusBarJSErrorLabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum));
1271  d->m_statusBarJSErrorLabel->setUseCursor(false);
1272  d->m_statusBarExtension->addStatusBarItem(d->m_statusBarJSErrorLabel, 0, false);
1273  d->m_statusBarJSErrorLabel->setToolTip(i18n("This web page contains coding errors."));
1274  d->m_statusBarJSErrorLabel->setPixmap(SmallIcon("script-error"));
1275  connect(d->m_statusBarJSErrorLabel, SIGNAL(leftClickedUrl()), SLOT(launchJSErrorDialog()));
1276  connect(d->m_statusBarJSErrorLabel, SIGNAL(rightClickedUrl()), SLOT(jsErrorDialogContextMenu()));
1277  }
1278  if (!d->m_jsedlg) {
1279  d->m_jsedlg = new KJSErrorDlg;
1280  d->m_jsedlg->setURL(url().toDisplayString());
1281  }
1282  return d->m_jsedlg;
1283 }
1284 
1285 void KHTMLPart::removeJSErrorExtension()
1286 {
1287  if (parentPart()) {
1288  parentPart()->removeJSErrorExtension();
1289  return;
1290  }
1291  if (d->m_statusBarJSErrorLabel != nullptr) {
1292  d->m_statusBarExtension->removeStatusBarItem(d->m_statusBarJSErrorLabel);
1293  delete d->m_statusBarJSErrorLabel;
1294  d->m_statusBarJSErrorLabel = nullptr;
1295  }
1296  delete d->m_jsedlg;
1297  d->m_jsedlg = nullptr;
1298 }
1299 
1300 void KHTMLPart::disableJSErrorExtension()
1301 {
1302  removeJSErrorExtension();
1303  // These two lines are really kind of hacky, and it sucks to do this inside
1304  // KHTML but I don't know of anything that's reasonably easy as an alternative
1305  // right now. It makes me wonder if there should be a more clean way to
1306  // contact all running "KHTML" instance as opposed to Konqueror instances too.
1307  d->m_settings->setJSErrorsEnabled(false);
1308  emit configurationChanged();
1309 }
1310 
1311 void KHTMLPart::jsErrorDialogContextMenu()
1312 {
1313  QMenu *m = new QMenu(nullptr);
1314  m->addAction(i18n("&Hide Errors"), this, SLOT(removeJSErrorExtension()));
1315  m->addAction(i18n("&Disable Error Reporting"), this, SLOT(disableJSErrorExtension()));
1316  m->popup(QCursor::pos());
1317 }
1318 
1319 void KHTMLPart::launchJSErrorDialog()
1320 {
1321  KJSErrorDlg *dlg = jsErrorExtension();
1322  if (dlg) {
1323  dlg->show();
1324  dlg->raise();
1325  }
1326 }
1327 
1328 void KHTMLPart::launchJSConfigDialog()
1329 {
1330  QStringList args;
1331  args << "khtml_java_js";
1332  KToolInvocation::kdeinitExec("kcmshell5", args);
1333 }
1334 
1335 QVariant KHTMLPart::executeScript(const QString &filename, int baseLine, const DOM::Node &n, const QString &script)
1336 {
1337 #ifdef KJS_VERBOSE
1338  // The script is now printed by KJS's Parser::parse
1339  qCDebug(KHTML_LOG) << "executeScript: caller='" << objectName() << "' filename=" << filename << " baseLine=" << baseLine /*<< " script=" << script*/;
1340 #endif
1341  KJSProxy *proxy = jScript();
1342 
1343  if (!proxy || proxy->paused()) {
1344  return QVariant();
1345  }
1346 
1347  KJS::Completion comp;
1348  QVariant ret = proxy->evaluate(filename, baseLine, script, n, &comp);
1349 
1350  /*
1351  * Error handling
1352  */
1353  if (comp.complType() == KJS::Throw && comp.value()) {
1354  KJSErrorDlg *dlg = jsErrorExtension();
1355  if (dlg) {
1356  QString msg = KJS::exceptionToString(
1357  proxy->interpreter()->globalExec(), comp.value());
1358  dlg->addError(i18n("<qt><b>Error</b>: %1: %2</qt>",
1359  filename.toHtmlEscaped(), msg.toHtmlEscaped()));
1360  }
1361  }
1362 
1363  // Handle immediate redirects now (e.g. location='foo')
1364  if (!d->m_redirectURL.isEmpty() && d->m_delayRedirect == -1) {
1365  // qCDebug(KHTML_LOG) << "executeScript done, handling immediate redirection NOW";
1366  // Must abort tokenizer, no further script must execute.
1367  khtml::Tokenizer *t = d->m_doc->tokenizer();
1368  if (t) {
1369  t->abort();
1370  }
1371  d->m_redirectionTimer.setSingleShot(true);
1372  d->m_redirectionTimer.start(0);
1373  }
1374 
1375  return ret;
1376 }
1377 
1379 {
1380  return executeScript(DOM::Node(), script);
1381 }
1382 
1384 {
1385 #ifdef KJS_VERBOSE
1386  qCDebug(KHTML_LOG) << "caller=" << objectName() << "node=" << n.nodeName().string().toLatin1().constData() << "(" << (n.isNull() ? 0 : n.nodeType()) << ") " /* << script */;
1387 #endif
1388  KJSProxy *proxy = jScript();
1389 
1390  if (!proxy || proxy->paused()) {
1391  return QVariant();
1392  }
1393 
1394  ++(d->m_runningScripts);
1395  KJS::Completion comp;
1396  const QVariant ret = proxy->evaluate(QString(), 1, script, n, &comp);
1397  --(d->m_runningScripts);
1398 
1399  /*
1400  * Error handling
1401  */
1402  if (comp.complType() == KJS::Throw && comp.value()) {
1403  KJSErrorDlg *dlg = jsErrorExtension();
1404  if (dlg) {
1405  QString msg = KJS::exceptionToString(
1406  proxy->interpreter()->globalExec(), comp.value());
1407  dlg->addError(i18n("<qt><b>Error</b>: node %1: %2</qt>",
1408  n.nodeName().string(), msg.toHtmlEscaped()));
1409  }
1410  }
1411 
1412  if (!d->m_runningScripts && d->m_doc && !d->m_doc->parsing() && d->m_submitForm) {
1413  submitFormAgain();
1414  }
1415 
1416 #ifdef KJS_VERBOSE
1417  qCDebug(KHTML_LOG) << "done";
1418 #endif
1419  return ret;
1420 }
1421 
1422 void KHTMLPart::setJavaEnabled(bool enable)
1423 {
1424  d->m_bJavaForce = enable;
1425  d->m_bJavaOverride = true;
1426 }
1427 
1428 bool KHTMLPart::javaEnabled() const
1429 {
1430  if (onlyLocalReferences()) {
1431  return false;
1432  }
1433 
1434  if (d->m_bJavaOverride) {
1435  return d->m_bJavaForce;
1436  }
1437  return d->m_bJavaEnabled;
1438 }
1439 
1441 {
1442  d->m_bPluginsForce = enable;
1443  d->m_bPluginsOverride = true;
1444 }
1445 
1446 bool KHTMLPart::pluginsEnabled() const
1447 {
1448  if (onlyLocalReferences()) {
1449  return false;
1450  }
1451 
1452  if (d->m_bPluginsOverride) {
1453  return d->m_bPluginsForce;
1454  }
1455  return d->m_bPluginsEnabled;
1456 }
1457 
1458 static int s_DOMTreeIndentLevel = 0;
1459 
1460 void KHTMLPart::slotDebugDOMTree()
1461 {
1462  if (d->m_doc) {
1463  qDebug("%s", d->m_doc->toString().string().toLatin1().constData());
1464  }
1465 
1466  // Now print the contents of the frames that contain HTML
1467 
1468  const int indentLevel = s_DOMTreeIndentLevel++;
1469 
1470  ConstFrameIt it = d->m_frames.constBegin();
1471  const ConstFrameIt end = d->m_frames.constEnd();
1472  for (; it != end; ++it)
1473  if (!(*it)->m_part.isNull() && (*it)->m_part.data()->inherits("KHTMLPart")) {
1474  KParts::ReadOnlyPart *const p = (*it)->m_part.data();
1475  // qCDebug(KHTML_LOG) << QString().leftJustified(s_DOMTreeIndentLevel*4,' ') << "FRAME " << p->objectName() << " ";
1476  static_cast<KHTMLPart *>(p)->slotDebugDOMTree();
1477  }
1478  s_DOMTreeIndentLevel = indentLevel;
1479 }
1480 
1481 void KHTMLPart::slotDebugScript()
1482 {
1483  if (jScript()) {
1484  jScript()->showDebugWindow();
1485  }
1486 }
1487 
1488 void KHTMLPart::slotDebugRenderTree()
1489 {
1490 #ifndef NDEBUG
1491  if (d->m_doc) {
1492  d->m_doc->renderer()->printTree();
1493  // dump out the contents of the rendering & DOM trees
1494 // QString dumps;
1495 // QTextStream outputStream(&dumps,QIODevice::WriteOnly);
1496 // d->m_doc->renderer()->layer()->dump( outputStream );
1497 // qCDebug(KHTML_LOG) << "dump output:" << "\n" + dumps;
1498 // d->m_doc->renderer()->printLineBoxTree();
1499  }
1500 #endif
1501 }
1502 
1503 void KHTMLPart::slotDebugFrameTree()
1504 {
1505  khtml::ChildFrame::dumpFrameTree(this);
1506 }
1507 
1508 void KHTMLPart::slotStopAnimations()
1509 {
1510  stopAnimations();
1511 }
1512 
1514 {
1515  if (d->m_doc && d->m_doc->docLoader()->autoloadImages() == enable) {
1516  return;
1517  }
1518 
1519  if (d->m_doc) {
1520  d->m_doc->docLoader()->setAutoloadImages(enable);
1521  }
1522 
1523  unplugActionList("loadImages");
1524 
1525  if (enable) {
1526  delete d->m_paLoadImages;
1527  d->m_paLoadImages = nullptr;
1528  } else if (!d->m_paLoadImages) {
1529  d->m_paLoadImages = new QAction(i18n("Display Images on Page"), this);
1530  actionCollection()->addAction("loadImages", d->m_paLoadImages);
1531  d->m_paLoadImages->setIcon(QIcon::fromTheme("image-loading"));
1532  connect(d->m_paLoadImages, SIGNAL(triggered(bool)), this, SLOT(slotLoadImages()));
1533  }
1534 
1535  if (d->m_paLoadImages) {
1536  QList<QAction *> lst;
1537  lst.append(d->m_paLoadImages);
1538  plugActionList("loadImages", lst);
1539  }
1540 }
1541 
1543 {
1544  if (d->m_doc) {
1545  return d->m_doc->docLoader()->autoloadImages();
1546  }
1547 
1548  return true;
1549 }
1550 
1551 void KHTMLPart::clear()
1552 {
1553  if (d->m_bCleared) {
1554  return;
1555  }
1556 
1557  d->m_bCleared = true;
1558 
1559  d->m_bClearing = true;
1560 
1561  {
1562  ConstFrameIt it = d->m_frames.constBegin();
1563  const ConstFrameIt end = d->m_frames.constEnd();
1564  for (; it != end; ++it) {
1565  // Stop HTMLRun jobs for frames
1566  if ((*it)->m_run) {
1567  (*it)->m_run.data()->abort();
1568  }
1569  }
1570  }
1571 
1572  {
1573  ConstFrameIt it = d->m_objects.constBegin();
1574  const ConstFrameIt end = d->m_objects.constEnd();
1575  for (; it != end; ++it) {
1576  // Stop HTMLRun jobs for objects
1577  if ((*it)->m_run) {
1578  (*it)->m_run.data()->abort();
1579  }
1580  }
1581  }
1582 
1583  findTextBegin(); // resets d->m_findNode and d->m_findPos
1584  d->m_mousePressNode = DOM::Node();
1585 
1586  if (d->m_doc) {
1587  if (d->m_doc->attached()) { //the view may have detached it already
1588  d->m_doc->detach();
1589  }
1590  }
1591 
1592  // Moving past doc so that onUnload works.
1593  if (d->m_frame && d->m_frame->m_jscript) {
1594  d->m_frame->m_jscript->clear();
1595  }
1596 
1597  // stopping marquees
1598  if (d->m_doc && d->m_doc->renderer() && d->m_doc->renderer()->layer()) {
1599  d->m_doc->renderer()->layer()->suspendMarquees();
1600  }
1601 
1602  if (d->m_view) {
1603  d->m_view->clear();
1604  }
1605 
1606  // do not dereference the document before the jscript and view are cleared, as some destructors
1607  // might still try to access the document.
1608  if (d->m_doc) {
1609  d->m_doc->deref();
1610  }
1611  d->m_doc = nullptr;
1612 
1613  delete d->m_decoder;
1614  d->m_decoder = nullptr;
1615 
1616  // We don't want to change between parts if we are going to delete all of them anyway
1617  if (partManager()) {
1618  disconnect(partManager(), SIGNAL(activePartChanged(KParts::Part*)),
1619  this, SLOT(slotActiveFrameChanged(KParts::Part*)));
1620  }
1621 
1622  if (d->m_frames.count()) {
1623  const KHTMLFrameList frames = d->m_frames;
1624  d->m_frames.clear();
1625  ConstFrameIt it = frames.begin();
1626  const ConstFrameIt end = frames.end();
1627  for (; it != end; ++it) {
1628  if ((*it)->m_part) {
1629  partManager()->removePart((*it)->m_part.data());
1630  delete(*it)->m_part.data();
1631  }
1632  delete *it;
1633  }
1634  }
1635  d->m_suppressedPopupOriginParts.clear();
1636 
1637  if (d->m_objects.count()) {
1638  KHTMLFrameList objects = d->m_objects;
1639  d->m_objects.clear();
1640  ConstFrameIt oi = objects.constBegin();
1641  const ConstFrameIt oiEnd = objects.constEnd();
1642 
1643  for (; oi != oiEnd; ++oi) {
1644  delete(*oi)->m_part.data();
1645  delete *oi;
1646  }
1647  }
1648 
1649  // Listen to part changes again
1650  if (partManager()) {
1651  connect(partManager(), SIGNAL(activePartChanged(KParts::Part*)),
1652  this, SLOT(slotActiveFrameChanged(KParts::Part*)));
1653  }
1654 
1655  d->clearRedirection();
1656  d->m_redirectLockHistory = true;
1657  d->m_bClearing = false;
1658  d->m_frameNameId = 1;
1659  d->m_bFirstData = true;
1660 
1661  d->m_bMousePressed = false;
1662 
1663  if (d->editor_context.m_caretBlinkTimer >= 0) {
1664  killTimer(d->editor_context.m_caretBlinkTimer);
1665  }
1666  d->editor_context.reset();
1667 #ifndef QT_NO_CLIPBOARD
1668  connect(qApp->clipboard(), SIGNAL(selectionChanged()), SLOT(slotClearSelection()));
1669 #endif
1670 
1671  d->m_jobPercent = 0;
1672 
1673  if (!d->m_haveEncoding) {
1674  d->m_encoding.clear();
1675  }
1676 
1677  d->m_DNSPrefetchQueue.clear();
1678  if (d->m_DNSPrefetchTimer > 0) {
1679  killTimer(d->m_DNSPrefetchTimer);
1680  }
1681  d->m_DNSPrefetchTimer = -1;
1682  d->m_lookedupHosts.clear();
1683  if (d->m_DNSTTLTimer > 0) {
1684  killTimer(d->m_DNSTTLTimer);
1685  }
1686  d->m_DNSTTLTimer = -1;
1687  d->m_numDNSPrefetchedNames = 0;
1688 
1689 #ifdef SPEED_DEBUG
1690  d->m_parsetime.restart();
1691 #endif
1692 }
1693 
1695 {
1696  return true;
1697 }
1698 
1699 DOM::HTMLDocumentImpl *KHTMLPart::docImpl() const
1700 {
1701  if (d && d->m_doc && d->m_doc->isHTMLDocument()) {
1702  return static_cast<HTMLDocumentImpl *>(d->m_doc);
1703  }
1704  return nullptr;
1705 }
1706 
1707 DOM::DocumentImpl *KHTMLPart::xmlDocImpl() const
1708 {
1709  if (d) {
1710  return d->m_doc;
1711  }
1712  return nullptr;
1713 }
1714 
1715 void KHTMLPart::slotInfoMessage(KJob *kio_job, const QString &msg)
1716 {
1717  assert(d->m_job == kio_job);
1718  Q_ASSERT(kio_job);
1719  Q_UNUSED(kio_job);
1720 
1721  if (!parentPart()) {
1722  setStatusBarText(msg, BarDefaultText);
1723  }
1724 }
1725 
1726 void KHTMLPart::setPageSecurity(PageSecurity sec)
1727 {
1728  emit d->m_extension->setPageSecurity(sec);
1729 }
1730 
1731 void KHTMLPart::slotData(KIO::Job *kio_job, const QByteArray &data)
1732 {
1733  assert(d->m_job == kio_job);
1734  Q_ASSERT(kio_job);
1735  Q_UNUSED(kio_job);
1736 
1737  //qCDebug(KHTML_LOG) << "slotData: " << data.size();
1738  // The first data ?
1739  if (!d->m_workingURL.isEmpty()) {
1740  //qCDebug(KHTML_LOG) << "begin!";
1741 
1742  // We must suspend KIO while we're inside begin() because it can cause
1743  // crashes if a window (such as kjsdebugger) goes back into the event loop,
1744  // more data arrives, and begin() gets called again (re-entered).
1745  d->m_job->suspend();
1746  begin(d->m_workingURL, arguments().xOffset(), arguments().yOffset());
1747  d->m_job->resume();
1748 
1749  // CC_Refresh means : always send the server an If-Modified-Since conditional request.
1750  // This is the default cache setting and correspond to the KCM's "Keep cache in sync".
1751  // CC_Verify means : only send a conditional request if the cache expiry date is passed.
1752  // It doesn't have a KCM setter.
1753  // We override the first to the second, except when doing a soft-reload.
1754  if (d->m_cachePolicy == KIO::CC_Refresh && !d->m_extension->browserArguments().softReload) {
1755  d->m_doc->docLoader()->setCachePolicy(KIO::CC_Verify);
1756  } else {
1757  d->m_doc->docLoader()->setCachePolicy(d->m_cachePolicy);
1758  }
1759 
1760  d->m_workingURL = QUrl();
1761 
1762  d->m_cacheId = KHTMLPageCache::self()->createCacheEntry();
1763 
1764  // When the first data arrives, the metadata has just been made available
1765  d->m_httpHeaders = d->m_job->queryMetaData("HTTP-Headers");
1766  QDateTime cacheCreationDate = QDateTime::fromTime_t(d->m_job->queryMetaData("cache-creation-date").toLong());
1767  d->m_doc->docLoader()->setCacheCreationDate(cacheCreationDate);
1768 
1769  d->m_pageServices = d->m_job->queryMetaData("PageServices");
1770  d->m_pageReferrer = d->m_job->queryMetaData("referrer");
1771  d->m_ssl_in_use = (d->m_job->queryMetaData("ssl_in_use") == "TRUE");
1772 
1773  {
1774  KHTMLPart *p = parentPart();
1775  if (p && p->d->m_ssl_in_use != d->m_ssl_in_use) {
1776  while (p->parentPart()) {
1777  p = p->parentPart();
1778  }
1779 
1780  p->setPageSecurity(NotCrypted);
1781  }
1782  }
1783 
1784  setPageSecurity(d->m_ssl_in_use ? Encrypted : NotCrypted);
1785 
1786  // Shouldn't all of this be done only if ssl_in_use == true ? (DF)
1787  d->m_ssl_parent_ip = d->m_job->queryMetaData("ssl_parent_ip");
1788  d->m_ssl_parent_cert = d->m_job->queryMetaData("ssl_parent_cert");
1789  d->m_ssl_peer_chain = d->m_job->queryMetaData("ssl_peer_chain");
1790  d->m_ssl_peer_ip = d->m_job->queryMetaData("ssl_peer_ip");
1791  d->m_ssl_cipher = d->m_job->queryMetaData("ssl_cipher");
1792  d->m_ssl_protocol_version = d->m_job->queryMetaData("ssl_protocol_version");
1793  d->m_ssl_cipher_used_bits = d->m_job->queryMetaData("ssl_cipher_used_bits");
1794  d->m_ssl_cipher_bits = d->m_job->queryMetaData("ssl_cipher_bits");
1795  d->m_ssl_cert_errors = d->m_job->queryMetaData("ssl_cert_errors");
1796 
1797  // Check for charset meta-data
1798  QString qData = d->m_job->queryMetaData("charset");
1799  if (!qData.isEmpty() && !d->m_haveEncoding) { // only use information if the user didn't override the settings
1800  d->m_encoding = qData;
1801  }
1802 
1803  // Support for http-refresh
1804  qData = d->m_job->queryMetaData("http-refresh");
1805  if (!qData.isEmpty()) {
1806  d->m_doc->processHttpEquiv("refresh", qData);
1807  }
1808 
1809  // DISABLED: Support Content-Location per section 14.14 of RFC 2616.
1810  // See BR# 51185,BR# 82747
1811  /*
1812  QString baseURL = d->m_job->queryMetaData ("content-location");
1813  if (!baseURL.isEmpty())
1814  d->m_doc->setBaseURL(QUrl( d->m_doc->completeURL(baseURL) ));
1815  */
1816 
1817  // Support for Content-Language
1818  QString language = d->m_job->queryMetaData("content-language");
1819  if (!language.isEmpty()) {
1820  d->m_doc->setContentLanguage(language);
1821  }
1822 
1823  if (!url().isLocalFile()) {
1824  // Support for http last-modified
1825  d->m_lastModified = d->m_job->queryMetaData("modified");
1826  } else {
1827  d->m_lastModified.clear(); // done on-demand by lastModified()
1828  }
1829  }
1830 
1831  KHTMLPageCache::self()->addData(d->m_cacheId, data);
1832  write(data.data(), data.size());
1833 }
1834 
1835 void KHTMLPart::slotRestoreData(const QByteArray &data)
1836 {
1837  // The first data ?
1838  if (!d->m_workingURL.isEmpty()) {
1839  long saveCacheId = d->m_cacheId;
1840  QString savePageReferrer = d->m_pageReferrer;
1841  QString saveEncoding = d->m_encoding;
1842  begin(d->m_workingURL, arguments().xOffset(), arguments().yOffset());
1843  d->m_encoding = saveEncoding;
1844  d->m_pageReferrer = savePageReferrer;
1845  d->m_cacheId = saveCacheId;
1846  d->m_workingURL = QUrl();
1847  }
1848 
1849  //qCDebug(KHTML_LOG) << data.size();
1850  write(data.data(), data.size());
1851 
1852  if (data.size() == 0) {
1853  //qCDebug(KHTML_LOG) << "<<end of data>>";
1854  // End of data.
1855  if (d->m_doc && d->m_doc->parsing()) {
1856  end(); //will emit completed()
1857  }
1858  }
1859 }
1860 
1862 {
1863  // qCDebug(KHTML_LOG) << "d->m_bParsing=" << (d->m_doc && d->m_doc->parsing()) << " d->m_bComplete=" << d->m_bComplete
1864  // << " d->m_bCleared=" << d->m_bCleared;
1865 
1866  if (job->error() == KIO::ERR_NO_CONTENT) {
1867  return;
1868  }
1869 
1870  if ((d->m_doc && d->m_doc->parsing()) || d->m_workingURL.isEmpty()) { // if we got any data already
1871  job->uiDelegate()->showErrorMessage();
1872  } else {
1873  htmlError(job->error(), job->errorText(), d->m_workingURL);
1874  }
1875 }
1876 
1877 // This is a protected method, placed here because of it's relevance to showError
1878 void KHTMLPart::htmlError(int errorCode, const QString &text, const QUrl &reqUrl)
1879 {
1880  // qCDebug(KHTML_LOG) << "errorCode" << errorCode << "text" << text;
1881  // make sure we're not executing any embedded JS
1882  bool bJSFO = d->m_bJScriptForce;
1883  bool bJSOO = d->m_bJScriptOverride;
1884  d->m_bJScriptForce = false;
1885  d->m_bJScriptOverride = true;
1886  begin();
1887 
1888  QString errorName, techName, description;
1889  QStringList causes, solutions;
1890 
1891  QByteArray raw = KIO::rawErrorDetail(errorCode, text, &reqUrl);
1892  QDataStream stream(raw);
1893 
1894  stream >> errorName >> techName >> description >> causes >> solutions;
1895 
1896  QString url, protocol, datetime;
1897 
1898  // This is somewhat confusing, but we have to escape the externally-
1899  // controlled URL twice: once for i18n, and once for HTML.
1900  url = reqUrl.toDisplayString().toHtmlEscaped().toHtmlEscaped();
1901  protocol = reqUrl.scheme();
1903 
1904  QString filename(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kf5/khtml/error.html"));
1905  QFile file(filename);
1906  bool isOpened = file.open(QIODevice::ReadOnly);
1907  if (!isOpened) {
1908  qCWarning(KHTML_LOG) << "Could not open error html template:" << filename;
1909  }
1910 
1911  QString html = QString(QLatin1String(file.readAll()));
1912 
1913  html.replace(QLatin1String("TITLE"), i18n("Error: %1 - %2", errorName, url));
1914  html.replace(QLatin1String("DIRECTION"), QApplication::isRightToLeft() ? "rtl" : "ltr");
1915  html.replace(QLatin1String("ICON_PATH"), QUrl::fromLocalFile(KIconLoader::global()->iconPath("dialog-warning", -KIconLoader::SizeHuge)).url());
1916 
1917  QString doc = QLatin1String("<h1>");
1918  doc += i18n("The requested operation could not be completed");
1919  doc += QLatin1String("</h1><h2>");
1920  doc += errorName;
1921  doc += QLatin1String("</h2>");
1922  if (!techName.isNull()) {
1923  doc += QLatin1String("<h2>");
1924  doc += i18n("Technical Reason: ");
1925  doc += techName;
1926  doc += QLatin1String("</h2>");
1927  }
1928  doc += QLatin1String("<br clear=\"all\">");
1929  doc += QLatin1String("<h3>");
1930  doc += i18n("Details of the Request:");
1931  doc += QLatin1String("</h3><ul><li>");
1932  doc += i18n("URL: %1", url);
1933  doc += QLatin1String("</li><li>");
1934  if (!protocol.isNull()) {
1935  doc += i18n("Protocol: %1", protocol);
1936  doc += QLatin1String("</li><li>");
1937  }
1938  doc += i18n("Date and Time: %1", datetime);
1939  doc += QLatin1String("</li><li>");
1940  doc += i18n("Additional Information: %1", text);
1941  doc += QLatin1String("</li></ul><h3>");
1942  doc += i18n("Description:");
1943  doc += QLatin1String("</h3><p>");
1944  doc += description;
1945  doc += QLatin1String("</p>");
1946  if (causes.count()) {
1947  doc += QLatin1String("<h3>");
1948  doc += i18n("Possible Causes:");
1949  doc += QLatin1String("</h3><ul><li>");
1950  doc += causes.join("</li><li>");
1951  doc += QLatin1String("</li></ul>");
1952  }
1953  if (solutions.count()) {
1954  doc += QLatin1String("<h3>");
1955  doc += i18n("Possible Solutions:");
1956  doc += QLatin1String("</h3><ul><li>");
1957  doc += solutions.join("</li><li>");
1958  doc += QLatin1String("</li></ul>");
1959  }
1960 
1961  html.replace(QLatin1String("TEXT"), doc);
1962 
1963  write(html);
1964  end();
1965 
1966  d->m_bJScriptForce = bJSFO;
1967  d->m_bJScriptOverride = bJSOO;
1968 
1969  // make the working url the current url, so that reload works and
1970  // emit the progress signals to advance one step in the history
1971  // (so that 'back' works)
1972  setUrl(reqUrl); // same as d->m_workingURL
1973  d->m_workingURL = QUrl();
1974  emit started(nullptr);
1975  emit completed();
1976 }
1977 
1979 {
1980  d->m_job = nullptr;
1981  d->m_jobspeed = 0L;
1982 
1983  if (job->error()) {
1984  KHTMLPageCache::self()->cancelEntry(d->m_cacheId);
1985 
1986  // The following catches errors that occur as a result of HTTP
1987  // to FTP redirections where the FTP URL is a directory. Since
1988  // KIO cannot change a redirection request from GET to LISTDIR,
1989  // we have to take care of it here once we know for sure it is
1990  // a directory...
1991  if (job->error() == KIO::ERR_IS_DIRECTORY) {
1992  emit canceled(job->errorString());
1993  emit d->m_extension->openUrlRequest(d->m_workingURL);
1994  } else {
1995  emit canceled(job->errorString());
1996  // TODO: what else ?
1997  checkCompleted();
1998  showError(job);
1999  }
2000 
2001  return;
2002  }
2004  if (tjob && tjob->isErrorPage()) {
2005  HTMLPartContainerElementImpl *elt = d->m_frame ?
2006  d->m_frame->m_partContainerElement.data() : nullptr;
2007 
2008  if (!elt) {
2009  return;
2010  }
2011 
2012  elt->partLoadingErrorNotify();
2013  checkCompleted();
2014  if (d->m_bComplete) {
2015  return;
2016  }
2017  }
2018 
2019  //qCDebug(KHTML_LOG) << "slotFinished";
2020 
2021  KHTMLPageCache::self()->endData(d->m_cacheId);
2022 
2023  if (d->m_doc && d->m_doc->docLoader()->expireDate().isValid() && url().scheme().startsWith("http")) {
2024  KIO::http_update_cache(url(), false, d->m_doc->docLoader()->expireDate());
2025  }
2026 
2027  d->m_workingURL = QUrl();
2028 
2029  if (d->m_doc && d->m_doc->parsing()) {
2030  end(); //will emit completed()
2031  }
2032 }
2033 
2034 MimeType KHTMLPartPrivate::classifyMimeType(const QString &mimeStr)
2035 {
2036  // See HTML5's "5.5.1 Navigating across documents" section.
2037  if (mimeStr == "application/xhtml+xml") {
2038  return MimeXHTML;
2039  }
2040  if (mimeStr == "image/svg+xml") {
2041  return MimeSVG;
2042  }
2043  if (mimeStr == "text/html" || mimeStr.isEmpty()) {
2044  return MimeHTML;
2045  }
2046 
2047  QMimeDatabase db;
2048  QMimeType mime = db.mimeTypeForName(mimeStr);
2049  if (mime.inherits("text/xml") || mimeStr.endsWith("+xml")) {
2050  return MimeXML;
2051  }
2052 
2053  if (mime.inherits("text/plain")) {
2054  return MimeText;
2055  }
2056 
2057  if (khtmlImLoad::ImageManager::loaderDatabase()->supportedMimeTypes().contains(mimeStr)) {
2058  return MimeImage;
2059  }
2060 
2061  // Sometimes our subclasses like to handle custom mimetypes. In that case,
2062  // we want to handle them as HTML. We do that in the following cases:
2063  // 1) We're at top-level, so we were forced to open something
2064  // 2) We're an object --- this again means we were forced to open something,
2065  // as an iframe-generating-an-embed case would have us as an iframe
2066  if (!q->parentPart() || (m_frame && m_frame->m_type == khtml::ChildFrame::Object)) {
2067  return MimeHTML;
2068  }
2069 
2070  return MimeOther;
2071 }
2072 
2073 void KHTMLPart::begin(const QUrl &url, int xOffset, int yOffset)
2074 {
2075  if (d->m_view->underMouse()) {
2076  QToolTip::hideText(); // in case a previous tooltip is still shown
2077  }
2078 
2079  // No need to show this for a new page until an error is triggered
2080  if (!parentPart()) {
2081  removeJSErrorExtension();
2083  d->m_openableSuppressedPopups = 0;
2084  foreach (KHTMLPart *part, d->m_suppressedPopupOriginParts) {
2085  if (part) {
2086  KJS::Window *w = KJS::Window::retrieveWindow(part);
2087  if (w) {
2088  w->forgetSuppressedWindows();
2089  }
2090  }
2091  }
2092  }
2093 
2094  d->m_bCleared = false;
2095  d->m_cacheId = 0;
2096  d->m_bComplete = false;
2097  d->m_bLoadEventEmitted = false;
2098  clear();
2099  d->m_bCleared = false;
2100 
2101  if (url.isValid()) {
2102  QString urlString = url.toString();
2103  KHTMLGlobal::vLinks()->insert(urlString);
2104  QString urlString2 = url.toDisplayString();
2105  if (urlString != urlString2) {
2106  KHTMLGlobal::vLinks()->insert(urlString2);
2107  }
2108  }
2109 
2110  // ###
2111  //stopParser();
2112 
2114  args.setXOffset(xOffset);
2115  args.setYOffset(yOffset);
2116  setArguments(args);
2117 
2118  d->m_pageReferrer.clear();
2119 
2120  d->m_referrer = url.scheme().startsWith("http") ? url.toString() : "";
2121 
2122  setUrl(url);
2123 
2124  // Note: by now, any special mimetype besides plaintext would have been
2125  // handled specially inside openURL, so we handle their cases the same
2126  // as HTML.
2127  MimeType type = d->classifyMimeType(args.mimeType());
2128  switch (type) {
2129  case MimeSVG:
2130  d->m_doc = DOMImplementationImpl::createSVGDocument(d->m_view);
2131  break;
2132  case MimeXML: // any XML derivative, except XHTML or SVG
2133  // ### not sure if XHTML documents served as text/xml should use DocumentImpl or HTMLDocumentImpl
2134  d->m_doc = DOMImplementationImpl::createXMLDocument(d->m_view);
2135  break;
2136  case MimeText:
2137  d->m_doc = new HTMLTextDocumentImpl(d->m_view);
2138  break;
2139  case MimeXHTML:
2140  case MimeHTML:
2141  default:
2142  d->m_doc = DOMImplementationImpl::createHTMLDocument(d->m_view);
2143  // HTML or XHTML? (#86446)
2144  static_cast<HTMLDocumentImpl *>(d->m_doc)->setHTMLRequested(type != MimeXHTML);
2145  }
2146 
2147  d->m_doc->ref();
2148  d->m_doc->setURL(url.toString());
2149  d->m_doc->open();
2150  if (!d->m_doc->attached()) {
2151  d->m_doc->attach();
2152  }
2153  d->m_doc->setBaseURL(QUrl());
2154  d->m_doc->docLoader()->setShowAnimations(KHTMLGlobal::defaultHTMLSettings()->showAnimations());
2155  emit docCreated();
2156 
2157  d->m_paUseStylesheet->setItems(QStringList());
2158  d->m_paUseStylesheet->setEnabled(false);
2159 
2160  setAutoloadImages(KHTMLGlobal::defaultHTMLSettings()->autoLoadImages());
2161  QString userStyleSheet = KHTMLGlobal::defaultHTMLSettings()->userStyleSheet();
2162  if (!userStyleSheet.isEmpty()) {
2163  setUserStyleSheet(QUrl(userStyleSheet));
2164  }
2165 
2166  d->m_doc->setRestoreState(d->m_extension->browserArguments().docState);
2167  connect(d->m_doc, SIGNAL(finishedParsing()), this, SLOT(slotFinishedParsing()));
2168 
2169  emit d->m_extension->enableAction("print", true);
2170 
2171  d->m_doc->setParsing(true);
2172 }
2173 
2174 void KHTMLPart::write(const char *data, int len)
2175 {
2176  if (!d->m_decoder) {
2177  d->m_decoder = createDecoder();
2178  }
2179 
2180  if (len == -1) {
2181  len = strlen(data);
2182  }
2183 
2184  if (len == 0) {
2185  return;
2186  }
2187 
2188  QString decoded = d->m_decoder->decodeWithBuffering(data, len);
2189 
2190  if (decoded.isEmpty()) {
2191  return;
2192  }
2193 
2194  if (d->m_bFirstData) {
2195  onFirstData();
2196  }
2197 
2198  khtml::Tokenizer *t = d->m_doc->tokenizer();
2199  if (t) {
2200  t->write(decoded, true);
2201  }
2202 }
2203 
2204 // ### KDE5: remove
2206 {
2207  d->m_bStrictModeQuirk = !b;
2208 }
2209 
2210 void KHTMLPart::write(const QString &str)
2211 {
2212  if (str.isNull()) {
2213  return;
2214  }
2215 
2216  if (d->m_bFirstData) {
2217  // determine the parse mode
2218  if (d->m_bStrictModeQuirk) {
2219  d->m_doc->setParseMode(DocumentImpl::Strict);
2220  d->m_bFirstData = false;
2221  } else {
2222  onFirstData();
2223  }
2224  }
2225  khtml::Tokenizer *t = d->m_doc->tokenizer();
2226  if (t) {
2227  t->write(str, true);
2228  }
2229 }
2230 
2232 {
2233  if (d->m_doc) {
2234  if (d->m_decoder) {
2235  QString decoded = d->m_decoder->flush();
2236  if (d->m_bFirstData) {
2237  onFirstData();
2238  }
2239  if (!decoded.isEmpty()) {
2240  write(decoded);
2241  }
2242  }
2243  d->m_doc->finishParsing();
2244  }
2245 }
2246 
2247 void KHTMLPart::onFirstData()
2248 {
2249  assert(d->m_bFirstData);
2250 
2251  // determine the parse mode
2252  d->m_doc->determineParseMode();
2253  d->m_bFirstData = false;
2254 
2255  // ### this is still quite hacky, but should work a lot better than the old solution
2256  // Note: decoder may be null if only write(QString) is used.
2257  if (d->m_decoder && d->m_decoder->visuallyOrdered()) {
2258  d->m_doc->setVisuallyOrdered();
2259  }
2260  // ensure part and view shares zoom-level before styling
2261  updateZoomFactor();
2262  d->m_doc->recalcStyle(NodeImpl::Force);
2263 }
2264 
2265 bool KHTMLPart::doOpenStream(const QString &mimeType)
2266 {
2267  QMimeDatabase db;
2268  QMimeType mime = db.mimeTypeForName(mimeType);
2269  if (mime.inherits("text/html") || mime.inherits("text/xml")) {
2270  begin(url());
2271  return true;
2272  }
2273  return false;
2274 }
2275 
2277 {
2278  write(data.data(), data.size());
2279  return true;
2280 }
2281 
2283 {
2284  end();
2285  return true;
2286 }
2287 
2288 void KHTMLPart::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
2289 {
2290  if (!d->m_view) {
2291  return;
2292  }
2293  d->m_view->paint(p, rc, yOff, more);
2294 }
2295 
2297 {
2298  if (d->m_doc) {
2299  d->m_doc->docLoader()->setShowAnimations(KHTMLSettings::KAnimationDisabled);
2300  }
2301 
2302  ConstFrameIt it = d->m_frames.constBegin();
2303  const ConstFrameIt end = d->m_frames.constEnd();
2304  for (; it != end; ++it) {
2305  if (KHTMLPart *p = qobject_cast<KHTMLPart *>((*it)->m_part.data())) {
2306  p->stopAnimations();
2307  }
2308  }
2309 }
2310 
2311 void KHTMLPart::resetFromScript()
2312 {
2313  closeUrl();
2314  d->m_bComplete = false;
2315  d->m_bLoadEventEmitted = false;
2316  disconnect(d->m_doc, SIGNAL(finishedParsing()), this, SLOT(slotFinishedParsing()));
2317  connect(d->m_doc, SIGNAL(finishedParsing()), this, SLOT(slotFinishedParsing()));
2318  d->m_doc->setParsing(true);
2319 
2320  emit started(nullptr);
2321 }
2322 
2323 void KHTMLPart::slotFinishedParsing()
2324 {
2325  d->m_doc->setParsing(false);
2326  d->m_doc->dispatchHTMLEvent(EventImpl::KHTML_CONTENTLOADED_EVENT, true, false);
2327  checkEmitLoadEvent();
2328  disconnect(d->m_doc, SIGNAL(finishedParsing()), this, SLOT(slotFinishedParsing()));
2329 
2330  if (!d->m_view) {
2331  return; // We are probably being destructed.
2332  }
2333 
2334  checkCompleted();
2335 }
2336 
2337 void KHTMLPart::slotLoaderRequestStarted(khtml::DocLoader *dl, khtml::CachedObject *obj)
2338 {
2339  if (obj && obj->type() == khtml::CachedObject::Image && d->m_doc && d->m_doc->docLoader() == dl) {
2340  KHTMLPart *p = this;
2341  while (p) {
2342  KHTMLPart *const op = p;
2343  ++(p->d->m_totalObjectCount);
2344  p = p->parentPart();
2345  if (!p && op->d->m_loadedObjects <= op->d->m_totalObjectCount
2346  && !op->d->m_progressUpdateTimer.isActive()) {
2347  op->d->m_progressUpdateTimer.setSingleShot(true);
2348  op->d->m_progressUpdateTimer.start(200);
2349  }
2350  }
2351  }
2352 }
2353 
2354 static bool isAncestorOrSamePart(KHTMLPart *p1, KHTMLPart *p2)
2355 {
2356  KHTMLPart *p = p2;
2357  do {
2358  if (p == p1) {
2359  return true;
2360  }
2361  } while ((p = p->parentPart()));
2362  return false;
2363 }
2364 
2365 void KHTMLPart::slotLoaderRequestDone(khtml::DocLoader *dl, khtml::CachedObject *obj)
2366 {
2367  if (obj && obj->type() == khtml::CachedObject::Image && d->m_doc && d->m_doc->docLoader() == dl) {
2368  KHTMLPart *p = this;
2369  while (p) {
2370  KHTMLPart *const op = p;
2371  ++(p->d->m_loadedObjects);
2372  p = p->parentPart();
2373  if (!p && op->d->m_loadedObjects <= op->d->m_totalObjectCount && op->d->m_jobPercent <= 100
2374  && !op->d->m_progressUpdateTimer.isActive()) {
2375  op->d->m_progressUpdateTimer.setSingleShot(true);
2376  op->d->m_progressUpdateTimer.start(200);
2377  }
2378  }
2379  }
2381  // then our loading state can't possibly be affected : don't waste time checking for completion.
2382  if (!d->m_doc || !dl->doc()->part() || !isAncestorOrSamePart(this, dl->doc()->part())) {
2383  return;
2384  }
2385  checkCompleted();
2386 }
2387 
2388 void KHTMLPart::slotProgressUpdate()
2389 {
2390  int percent;
2391  if (d->m_loadedObjects < d->m_totalObjectCount) {
2392  percent = d->m_jobPercent / 4 + (d->m_loadedObjects * 300) / (4 * d->m_totalObjectCount);
2393  } else {
2394  percent = d->m_jobPercent;
2395  }
2396 
2397  if (d->m_bComplete) {
2398  percent = 100;
2399  }
2400 
2401  if (d->m_statusMessagesEnabled) {
2402  if (d->m_bComplete) {
2403  emit d->m_extension->infoMessage(i18n("Page loaded."));
2404  } else if (d->m_loadedObjects < d->m_totalObjectCount && percent >= 75) {
2405  emit d->m_extension->infoMessage(i18np("%1 Image of %2 loaded.", "%1 Images of %2 loaded.", d->m_loadedObjects, d->m_totalObjectCount));
2406  }
2407  }
2408 
2409  emit d->m_extension->loadingProgress(percent);
2410 }
2411 
2412 void KHTMLPart::slotJobSpeed(KJob * /*job*/, unsigned long speed)
2413 {
2414  d->m_jobspeed = speed;
2415  if (!parentPart()) {
2416  setStatusBarText(jsStatusBarText(), BarOverrideText);
2417  }
2418 }
2419 
2420 void KHTMLPart::slotJobPercent(KJob * /*job*/, unsigned long percent)
2421 {
2422  d->m_jobPercent = percent;
2423 
2424  if (!parentPart()) {
2425  d->m_progressUpdateTimer.setSingleShot(true);
2426  d->m_progressUpdateTimer.start(0);
2427  }
2428 }
2429 
2430 void KHTMLPart::slotJobDone(KJob * /*job*/)
2431 {
2432  d->m_jobPercent = 100;
2433 
2434  if (!parentPart()) {
2435  d->m_progressUpdateTimer.setSingleShot(true);
2436  d->m_progressUpdateTimer.start(0);
2437  }
2438 }
2439 
2440 void KHTMLPart::slotUserSheetStatDone(KJob *_job)
2441 {
2442  using namespace KIO;
2443 
2444  if (_job->error()) {
2445  showError(_job);
2446  return;
2447  }
2448 
2449  const UDSEntry entry = dynamic_cast<KIO::StatJob *>(_job)->statResult();
2451 
2452  // If the filesystem supports modification times, only reload the
2453  // user-defined stylesheet if necessary - otherwise always reload.
2454  if (lastModified.isValid()) {
2455  if (d->m_userStyleSheetLastModified >= lastModified) {
2456  return;
2457  }
2458  d->m_userStyleSheetLastModified = lastModified;
2459  }
2460 
2461  setUserStyleSheet(QUrl(settings()->userStyleSheet()));
2462 }
2463 
2464 bool KHTMLPartPrivate::isFullyLoaded(bool *pendingRedirections) const
2465 {
2466  *pendingRedirections = false;
2467 
2468  // Any frame that hasn't completed yet ?
2469  ConstFrameIt it = m_frames.constBegin();
2470  const ConstFrameIt end = m_frames.constEnd();
2471  for (; it != end; ++it) {
2472  if (!(*it)->m_bCompleted || (*it)->m_run) {
2473  //qCDebug(KHTML_LOG) << this << " is waiting for " << (*it)->m_part;
2474  return false;
2475  }
2476  // Check for frames with pending redirections
2477  if ((*it)->m_bPendingRedirection) {
2478  *pendingRedirections = true;
2479  }
2480  }
2481 
2482  // Any object that hasn't completed yet ?
2483  {
2484  ConstFrameIt oi = m_objects.constBegin();
2485  const ConstFrameIt oiEnd = m_objects.constEnd();
2486 
2487  for (; oi != oiEnd; ++oi)
2488  if (!(*oi)->m_bCompleted) {
2489  return false;
2490  }
2491  }
2492 
2493  // Are we still parsing
2494  if (m_doc && m_doc->parsing()) {
2495  return false;
2496  }
2497 
2498  // Still waiting for images/scripts from the loader ?
2499  int requests = 0;
2500  if (m_doc && m_doc->docLoader()) {
2501  requests = khtml::Cache::loader()->numRequests(m_doc->docLoader());
2502  }
2503 
2504  if (requests > 0) {
2505  //qCDebug(KHTML_LOG) << "still waiting for images/scripts from the loader - requests:" << requests;
2506  return false;
2507  }
2508 
2509  return true;
2510 }
2511 
2512 void KHTMLPart::checkCompleted()
2513 {
2514 // qCDebug(KHTML_LOG) << this;
2515 // qCDebug(KHTML_LOG) << " parsing: " << (d->m_doc && d->m_doc->parsing());
2516 // qCDebug(KHTML_LOG) << " complete: " << d->m_bComplete;
2517 
2518  // restore the cursor position
2519  if (d->m_doc && !d->m_doc->parsing() && !d->m_focusNodeRestored) {
2520  if (d->m_focusNodeNumber >= 0) {
2521  d->m_doc->setFocusNode(d->m_doc->nodeWithAbsIndex(d->m_focusNodeNumber));
2522  }
2523 
2524  d->m_focusNodeRestored = true;
2525  }
2526 
2527  bool fullyLoaded, pendingChildRedirections;
2528  fullyLoaded = d->isFullyLoaded(&pendingChildRedirections);
2529 
2530  // Are we still loading, or already have done the relevant work?
2531  if (!fullyLoaded || d->m_bComplete) {
2532  return;
2533  }
2534 
2535  // OK, completed.
2536  // Now do what should be done when we are really completed.
2537  d->m_bComplete = true;
2538  d->m_cachePolicy = KProtocolManager::cacheControl(); // reset cache policy
2539  d->m_totalObjectCount = 0;
2540  d->m_loadedObjects = 0;
2541 
2542  KHTMLPart *p = this;
2543  while (p) {
2544  KHTMLPart *op = p;
2545  p = p->parentPart();
2546  if (!p && !op->d->m_progressUpdateTimer.isActive()) {
2547  op->d->m_progressUpdateTimer.setSingleShot(true);
2548  op->d->m_progressUpdateTimer.start(0);
2549  }
2550  }
2551 
2552  checkEmitLoadEvent(); // if we didn't do it before
2553 
2554  bool pendingAction = false;
2555 
2556  if (!d->m_redirectURL.isEmpty()) {
2557  // DA: Do not start redirection for frames here! That action is
2558  // deferred until the parent emits a completed signal.
2559  if (parentPart() == nullptr) {
2560  //qCDebug(KHTML_LOG) << this << " starting redirection timer";
2561  d->m_redirectionTimer.setSingleShot(true);
2562  d->m_redirectionTimer.start(qMax(0, 1000 * d->m_delayRedirect));
2563  } else {
2564  //qCDebug(KHTML_LOG) << this << " not toplevel -> not starting redirection timer. Waiting for slotParentCompleted.";
2565  }
2566 
2567  pendingAction = true;
2568  } else if (pendingChildRedirections) {
2569  pendingAction = true;
2570  }
2571 
2572  // the view will emit completed on our behalf,
2573  // either now or at next repaint if one is pending
2574 
2575  //qCDebug(KHTML_LOG) << this << " asks the view to emit completed. pendingAction=" << pendingAction;
2576  d->m_view->complete(pendingAction);
2577 
2578  // find the alternate stylesheets
2579  QStringList sheets;
2580  if (d->m_doc) {
2581  sheets = d->m_doc->availableStyleSheets();
2582  }
2583  sheets.prepend(i18n("Automatic Detection"));
2584  d->m_paUseStylesheet->setItems(sheets);
2585 
2586  d->m_paUseStylesheet->setEnabled(sheets.count() > 2);
2587  if (sheets.count() > 2) {
2588  d->m_paUseStylesheet->setCurrentItem(qMax(sheets.indexOf(d->m_sheetUsed), 0));
2589  slotUseStylesheet();
2590  }
2591 
2593 
2594 #ifdef SPEED_DEBUG
2595  if (!parentPart()) {
2596  qCDebug(KHTML_LOG) << "DONE:" << d->m_parsetime.elapsed();
2597  }
2598 #endif
2599 }
2600 
2601 void KHTMLPart::checkEmitLoadEvent()
2602 {
2603  bool fullyLoaded, pendingChildRedirections;
2604  fullyLoaded = d->isFullyLoaded(&pendingChildRedirections);
2605 
2606  // ### might want to wait on pendingChildRedirections here, too
2607  if (d->m_bLoadEventEmitted || !d->m_doc || !fullyLoaded) {
2608  return;
2609  }
2610 
2611  d->m_bLoadEventEmitted = true;
2612  if (d->m_doc) {
2613  d->m_doc->close();
2614  }
2615 }
2616 
2618 {
2619  return d->m_settings;
2620 }
2621 
2622 #ifndef KDE_NO_COMPAT // KDE5: remove this ifndef, keep the method (renamed to baseUrl)
2623 QUrl KHTMLPart::baseURL() const
2624 {
2625  if (!d->m_doc) {
2626  return QUrl();
2627  }
2628 
2629  return d->m_doc->baseURL();
2630 }
2631 #endif
2632 
2634 {
2635  if (!d->m_doc) {
2636  return QUrl(url);
2637  }
2638 
2639 #if 0
2640  if (d->m_decoder) {
2641  return QUrl(d->m_doc->completeURL(url), d->m_decoder->codec()->mibEnum());
2642  }
2643 #endif
2644 
2645  return QUrl(d->m_doc->completeURL(url));
2646 }
2647 
2648 QString KHTMLPartPrivate::codeForJavaScriptURL(const QString &u)
2649 {
2650  return QUrl::fromPercentEncoding(u.right(u.length() - 11).toUtf8());
2651 }
2652 
2653 void KHTMLPartPrivate::executeJavascriptURL(const QString &u)
2654 {
2655  QString script = codeForJavaScriptURL(u);
2656  // qCDebug(KHTML_LOG) << "script=" << script;
2657  QVariant res = q->executeScript(DOM::Node(), script);
2658  if (res.type() == QVariant::String) {
2659  q->begin(q->url());
2660  q->setAlwaysHonourDoctype(); // Disable public API compat; it messes with doctype
2661  q->write(res.toString());
2662  q->end();
2663  }
2664  emit q->completed();
2665 }
2666 
2667 bool KHTMLPartPrivate::isJavaScriptURL(const QString &url)
2668 {
2669  return url.indexOf(QLatin1String("javascript:"), 0, Qt::CaseInsensitive) == 0;
2670 }
2671 
2672 // Called by ecma/kjs_window in case of redirections from Javascript,
2673 // and by xml/dom_docimpl.cpp in case of http-equiv meta refresh.
2674 void KHTMLPart::scheduleRedirection(int delay, const QString &url, bool doLockHistory)
2675 {
2676  // qCDebug(KHTML_LOG) << "delay=" << delay << " url=" << url << " from=" << this->url() << "parent=" << parentPart();
2677  // qCDebug(KHTML_LOG) << "current redirectURL=" << d->m_redirectURL << " with delay " << d->m_delayRedirect;
2678 
2679  // In case of JS redirections, some, such as jump to anchors, and javascript:
2680  // evaluation should actually be handled immediately, and not waiting until
2681  // the end of the script. (Besides, we don't want to abort the tokenizer for those)
2682  if (delay == -1 && d->isInPageURL(url)) {
2683  d->executeInPageURL(url, doLockHistory);
2684  return;
2685  }
2686 
2687  if (delay < 24 * 60 * 60 &&
2688  (d->m_redirectURL.isEmpty() || delay <= d->m_delayRedirect)) {
2689  d->m_delayRedirect = delay;
2690  d->m_redirectURL = url;
2691  d->m_redirectLockHistory = doLockHistory;
2692  // qCDebug(KHTML_LOG) << " d->m_bComplete=" << d->m_bComplete;
2693 
2694  if (d->m_bComplete) {
2695  d->m_redirectionTimer.stop();
2696  d->m_redirectionTimer.setSingleShot(true);
2697  d->m_redirectionTimer.start(qMax(0, 1000 * d->m_delayRedirect));
2698  }
2699  }
2700 }
2701 
2702 void KHTMLPartPrivate::clearRedirection()
2703 {
2704  m_delayRedirect = 0;
2705  m_redirectURL.clear();
2706  m_redirectionTimer.stop();
2707 }
2708 
2709 void KHTMLPart::slotRedirect()
2710 {
2711  // qCDebug(KHTML_LOG) << this;
2712  QString u = d->m_redirectURL;
2713  QUrl url(u);
2714  d->clearRedirection();
2715 
2716  if (d->isInPageURL(u)) {
2717  d->executeInPageURL(u, d->m_redirectLockHistory);
2718  return;
2719  }
2720 
2722  QUrl cUrl(this->url());
2723 
2724  // handle windows opened by JS
2725  if (openedByJS() && d->m_opener) {
2726  cUrl = d->m_opener->url();
2727  }
2728 
2729  if (!KUrlAuthorized::authorizeUrlAction("redirect", cUrl, url)) {
2730  qCWarning(KHTML_LOG) << "KHTMLPart::scheduleRedirection: Redirection from " << cUrl << " to " << url << " REJECTED!";
2731  emit completed();
2732  return;
2733  }
2734 
2735  if (areUrlsForSamePage(url, this->url())) {
2736  args.metaData().insert("referrer", d->m_pageReferrer);
2737  }
2738 
2739  // For javascript and META-tag based redirections:
2740  // - We don't take cross-domain-ness in consideration if we are the
2741  // toplevel frame because the new URL may be in a different domain as the current URL
2742  // but that's ok.
2743  // - If we are not the toplevel frame then we check against the toplevelURL()
2744  if (parentPart()) {
2745  args.metaData().insert("cross-domain", toplevelURL().toString());
2746  }
2747 
2748  KParts::BrowserArguments browserArgs;
2749  browserArgs.setLockHistory(d->m_redirectLockHistory);
2750  // _self: make sure we don't use any <base target=>'s
2751 
2752  if (!urlSelected(u, 0, 0, "_self", args, browserArgs)) {
2753  // urlSelected didn't open a url, so emit completed ourselves
2754  emit completed();
2755  }
2756 }
2757 
2758 void KHTMLPart::slotRedirection(KIO::Job *, const QUrl &url)
2759 {
2760  // the slave told us that we got redirected
2761  //qCDebug(KHTML_LOG) << "redirection by KIO to" << url;
2762  emit d->m_extension->setLocationBarUrl(url.toDisplayString());
2763  d->m_workingURL = url;
2764 }
2765 
2766 bool KHTMLPart::setEncoding(const QString &name, bool override)
2767 {
2768  d->m_encoding = name;
2769  d->m_haveEncoding = override;
2770 
2771  if (!url().isEmpty()) {
2772  // reload document
2773  closeUrl();
2774  QUrl oldUrl = url();
2775  setUrl(QUrl());
2776  d->m_restored = true;
2777  openUrl(oldUrl);
2778  d->m_restored = false;
2779  }
2780 
2781  return true;
2782 }
2783 
2785 {
2786  if (d->m_haveEncoding && !d->m_encoding.isEmpty()) {
2787  return d->m_encoding;
2788  }
2789 
2790  if (d->m_decoder && d->m_decoder->encoding()) {
2791  return QString(d->m_decoder->encoding());
2792  }
2793 
2794  return defaultEncoding();
2795 }
2796 
2797 QString KHTMLPart::defaultEncoding() const
2798 {
2799  QString encoding = settings()->encoding();
2800  if (!encoding.isEmpty()) {
2801  return encoding;
2802  }
2803  // HTTP requires the default encoding to be latin1, when neither
2804  // the user nor the page requested a particular encoding.
2805  if (url().scheme().startsWith("http")) {
2806  return "iso-8859-1";
2807  } else {
2808  return QTextCodec::codecForLocale()->name().toLower();
2809  }
2810 }
2811 
2813 {
2814  if (d->m_doc && d->m_doc->docLoader()) {
2815  (void) new khtml::PartStyleSheetLoader(this, url.toString(), d->m_doc->docLoader());
2816  }
2817 }
2818 
2819 void KHTMLPart::setUserStyleSheet(const QString &styleSheet)
2820 {
2821  if (d->m_doc) {
2822  d->m_doc->setUserStyleSheet(styleSheet);
2823  }
2824 }
2825 
2827 {
2828  if (!d->m_doc) {
2829  return false;
2830  }
2831 
2832  HTMLCollectionImpl *anchors = new HTMLCollectionImpl(d->m_doc, HTMLCollectionImpl::DOC_ANCHORS);
2833  anchors->ref();
2834  NodeImpl *n = anchors->namedItem(name);
2835  anchors->deref();
2836 
2837  if (!n) {
2838  n = d->m_doc->getElementById(name);
2839  }
2840 
2841  d->m_doc->setCSSTarget(n); // Setting to null will clear the current target.
2842 
2843  // Implement the rule that "" and "top" both mean top of page.
2844  bool top = !n && (name.isEmpty() || name.toLower() == "top");
2845 
2846  if (top) {
2847  d->m_view->setContentsPos(d->m_view->contentsX(), 0);
2848  return true;
2849  } else if (!n) {
2850  // qCDebug(KHTML_LOG) << name << "not found";
2851  return false;
2852  }
2853 
2854  int x = 0, y = 0;
2855  int gox, dummy;
2856  HTMLElementImpl *a = static_cast<HTMLElementImpl *>(n);
2857 
2858  a->getUpperLeftCorner(x, y);
2859  if (x <= d->m_view->contentsX()) {
2860  gox = x - 10;
2861  } else {
2862  gox = d->m_view->contentsX();
2863  if (x + 10 > d->m_view->contentsX() + d->m_view->visibleWidth()) {
2864  a->getLowerRightCorner(x, dummy);
2865  gox = x - d->m_view->visibleWidth() + 10;
2866  }
2867  }
2868 
2869  d->m_view->setContentsPos(gox, y);
2870 
2871  return true;
2872 }
2873 
2875 {
2876  if (!d->m_doc) {
2877  return false;
2878  }
2879  d->m_view->focusNextPrevNode(true);
2880 
2881  return true;
2882 }
2883 
2885 {
2886  if (!d->m_doc) {
2887  return false;
2888  }
2889  d->m_view->focusNextPrevNode(false);
2890 
2891  return true;
2892 }
2893 
2895 {
2896  d->m_settings->setStdFontName(name);
2897 }
2898 
2900 {
2901  d->m_settings->setFixedFontName(name);
2902 }
2903 
2905 {
2906  d->m_linkCursor = c;
2907 }
2908 
2910 {
2911  return d->m_linkCursor;
2912 }
2913 
2915 {
2916  return d->m_onlyLocalReferences;
2917 }
2918 
2920 {
2921  d->m_onlyLocalReferences = enable;
2922 }
2923 
2925 {
2926  return d->m_forcePermitLocalImages;
2927 }
2928 
2930 {
2931  d->m_forcePermitLocalImages = enable;
2932 }
2933 
2934 void KHTMLPartPrivate::setFlagRecursively(
2935  bool KHTMLPartPrivate::*flag, bool value)
2936 {
2937  // first set it on the current one
2938  this->*flag = value;
2939 
2940  // descend into child frames recursively
2941  {
2943  const QList<khtml::ChildFrame *>::Iterator itEnd = m_frames.end();
2944  for (; it != itEnd; ++it) {
2945  KHTMLPart *const part = qobject_cast<KHTMLPart *>((*it)->m_part.data());
2946  if (part) {
2947  part->d->setFlagRecursively(flag, value);
2948  }
2949  }/*next it*/
2950  }
2951  // do the same again for objects
2952  {
2954  const QList<khtml::ChildFrame *>::Iterator itEnd = m_objects.end();
2955  for (; it != itEnd; ++it) {
2956  KHTMLPart *const part = qobject_cast<KHTMLPart *>((*it)->m_part.data());
2957  if (part) {
2958  part->d->setFlagRecursively(flag, value);
2959  }
2960  }/*next it*/
2961  }
2962 }
2963 
2964 void KHTMLPart::initCaret()
2965 {
2966  // initialize caret if not used yet
2967  if (d->editor_context.m_selection.state() == Selection::NONE) {
2968  if (d->m_doc) {
2969  NodeImpl *node;
2970  if (d->m_doc->isHTMLDocument()) {
2971  HTMLDocumentImpl *htmlDoc = static_cast<HTMLDocumentImpl *>(d->m_doc);
2972  node = htmlDoc->body();
2973  } else {
2974  node = d->m_doc;
2975  }
2976  if (!node) {
2977  return;
2978  }
2979  d->editor_context.m_selection.moveTo(Position(node, 0));
2980  d->editor_context.m_selection.setNeedsLayout();
2981  d->editor_context.m_selection.needsCaretRepaint();
2982  }
2983  }
2984 }
2985 
2986 static void setCaretInvisibleIfNeeded(KHTMLPart *part)
2987 {
2988  // On contenteditable nodes, don't hide the caret
2989  if (!khtml::KHTMLPartAccessor::caret(part).caretPos().node()->isContentEditable()) {
2990  part->setCaretVisible(false);
2991  }
2992 }
2993 
2994 void KHTMLPart::setCaretMode(bool enable)
2995 {
2996  // qCDebug(KHTML_LOG) << enable;
2997  if (isCaretMode() == enable) {
2998  return;
2999  }
3000  d->setFlagRecursively(&KHTMLPartPrivate::m_caretMode, enable);
3001  // FIXME: this won't work on frames as expected
3002  if (!isEditable()) {
3003  if (enable) {
3004  initCaret();
3005  setCaretVisible(true);
3006 // view()->ensureCaretVisible();
3007  } else {
3008  setCaretInvisibleIfNeeded(this);
3009  }
3010  }
3011 }
3012 
3014 {
3015  return d->m_caretMode;
3016 }
3017 
3018 void KHTMLPart::setEditable(bool enable)
3019 {
3020  if (isEditable() == enable) {
3021  return;
3022  }
3023  d->setFlagRecursively(&KHTMLPartPrivate::m_designMode, enable);
3024  // FIXME: this won't work on frames as expected
3025  if (!isCaretMode()) {
3026  if (enable) {
3027  initCaret();
3028  setCaretVisible(true);
3029 // view()->ensureCaretVisible();
3030  } else {
3031  setCaretInvisibleIfNeeded(this);
3032  }
3033  }
3034 }
3035 
3037 {
3038  return d->m_designMode;
3039 }
3040 
3041 khtml::EditorContext *KHTMLPart::editorContext() const
3042 {
3043  return &d->editor_context;
3044 }
3045 
3046 void KHTMLPart::setCaretPosition(DOM::Node node, long offset, bool extendSelection)
3047 {
3048  Q_UNUSED(node);
3049  Q_UNUSED(offset);
3050  Q_UNUSED(extendSelection);
3051 #ifndef KHTML_NO_CARET
3052 #if 0
3053  qCDebug(KHTML_LOG) << "node: " << node.handle() << " nodeName: "
3054  << node.nodeName().string() << " offset: " << offset
3055  << " extendSelection " << extendSelection;
3056  if (view()->moveCaretTo(node.handle(), offset, !extendSelection)) {
3057  emitSelectionChanged();
3058  }
3059  view()->ensureCaretVisible();
3060 #endif
3061 #endif // KHTML_NO_CARET
3062 }
3063 
3065 {
3066 #if 0
3067 #ifndef KHTML_NO_CARET
3068  return (CaretDisplayPolicy)view()->caretDisplayPolicyNonFocused();
3069 #else // KHTML_NO_CARET
3070  return CaretInvisible;
3071 #endif // KHTML_NO_CARET
3072 #endif
3073  return CaretInvisible;
3074 }
3075 
3077 {
3078  Q_UNUSED(policy);
3079 #if 0
3080 #ifndef KHTML_NO_CARET
3081  view()->setCaretDisplayPolicyNonFocused(policy);
3082 #endif // KHTML_NO_CARET
3083 #endif
3084 }
3085 
3087 {
3088  if (show) {
3089  NodeImpl *caretNode = d->editor_context.m_selection.caretPos().node();
3090  if (isCaretMode() || (caretNode && caretNode->isContentEditable())) {
3091  invalidateSelection();
3092  enableFindAheadActions(false);
3093  }
3094  } else {
3095 
3096  if (d->editor_context.m_caretBlinkTimer >= 0) {
3097  killTimer(d->editor_context.m_caretBlinkTimer);
3098  }
3099  clearCaretRectIfNeeded();
3100 
3101  }
3102 }
3103 
3105 {
3106  d->m_find.findTextBegin();
3107 }
3108 
3109 bool KHTMLPart::initFindNode(bool selection, bool reverse, bool fromCursor)
3110 {
3111  return d->m_find.initFindNode(selection, reverse, fromCursor);
3112 }
3113 
3114 void KHTMLPart::slotFind()
3115 {
3117  if (!part) {
3118  return;
3119  }
3120  if (!part->inherits("KHTMLPart")) {
3121  qCCritical(KHTML_LOG) << "part is a" << part->metaObject()->className() << ", can't do a search into it";
3122  return;
3123  }
3124  static_cast<KHTMLPart *>(part)->findText();
3125 }
3126 
3127 void KHTMLPart::slotFindNext()
3128 {
3130  if (!part) {
3131  return;
3132  }
3133  if (!part->inherits("KHTMLPart")) {
3134  qCCritical(KHTML_LOG) << "part is a" << part->metaObject()->className() << ", can't do a search into it";
3135  return;
3136  }
3137  static_cast<KHTMLPart *>(part)->findTextNext();
3138 }
3139 
3140 void KHTMLPart::slotFindPrev()
3141 {
3143  if (!part) {
3144  return;
3145  }
3146  if (!part->inherits("KHTMLPart")) {
3147  qCCritical(KHTML_LOG) << "part is a" << part->metaObject()->className() << ", can't do a search into it";
3148  return;
3149  }
3150  static_cast<KHTMLPart *>(part)->findTextNext(true); // reverse
3151 }
3152 
3153 void KHTMLPart::slotFindDone()
3154 {
3155  // ### remove me
3156 }
3157 
3158 void KHTMLPart::slotFindAheadText()
3159 {
3161  if (!part) {
3162  return;
3163  }
3164  part->findText();
3165  KHTMLFindBar *findBar = part->d->m_find.findBar();
3166  findBar->setOptions(findBar->options() & ~FindLinksOnly);
3167 }
3168 
3169 void KHTMLPart::slotFindAheadLink()
3170 {
3172  if (!part) {
3173  return;
3174  }
3175  part->findText();
3176  KHTMLFindBar *findBar = part->d->m_find.findBar();
3177  findBar->setOptions(findBar->options() | FindLinksOnly);
3178 }
3179 
3180 void KHTMLPart::enableFindAheadActions(bool)
3181 {
3182  // ### remove me
3183 }
3184 
3185 void KHTMLPart::slotFindDialogDestroyed()
3186 {
3187  // ### remove me
3188 }
3189 
3191 {
3192  if (parentPart()) {
3193  return parentPart()->findText();
3194  }
3195  d->m_find.activate();
3196 }
3197 
3198 void KHTMLPart::findText(const QString &str, long options, QWidget *parent, KFindDialog *findDialog)
3199 {
3200  if (parentPart()) {
3201  return parentPart()->findText(str, options, parent, findDialog);
3202  }
3203  d->m_find.createNewKFind(str, options, parent, findDialog);
3204 }
3205 
3206 // New method
3207 bool KHTMLPart::findTextNext(bool reverse)
3208 {
3209  if (parentPart()) {
3210  return parentPart()->findTextNext(reverse);
3211  }
3212  return d->m_find.findTextNext(reverse);
3213 }
3214 
3215 bool KHTMLPart::pFindTextNextInThisFrame(bool reverse)
3216 {
3217  return d->m_find.findTextNext(reverse);
3218 }
3219 
3221 {
3222  const Selection &sel = d->editor_context.m_selection;
3223  if (!hasSelection()) {
3224  // qCDebug(KHTML_LOG) << "Selection is not valid. Returning empty selection";
3225  return QString();
3226  }
3227  if (sel.start().offset() < 0 || sel.end().offset() < 0) {
3228  // qCDebug(KHTML_LOG) << "invalid values for end/startOffset " << sel.start().offset() << " " << sel.end().offset();
3229  return QString();
3230  }
3231  DOM::Range r = selection();
3232  if (r.isNull() || r.isDetached()) {
3233  return QString();
3234  }
3235  int exceptioncode = 0; //ignore the result
3236  return r.handle()->toHTML(exceptioncode).string();
3237 }
3238 
3240 {
3241  bool hasNewLine = true;
3242  bool seenTDTag = false;
3243  QString text;
3244  const Selection &sel = d->editor_context.m_selection;
3245  DOM::Node n = sel.start().node();
3246  while (!n.isNull()) {
3247  if (n.nodeType() == DOM::Node::TEXT_NODE && n.handle()->renderer()) {
3248  DOM::DOMStringImpl *dstr = static_cast<DOM::TextImpl *>(n.handle())->renderString();
3249  QString str(dstr->s, dstr->l);
3250  if (!str.isEmpty()) {
3251  if (seenTDTag) {
3252  text += " ";
3253  seenTDTag = false;
3254  }
3255  hasNewLine = false;
3256  if (n == sel.start().node() && n == sel.end().node()) {
3257  int s = khtml::RenderPosition::fromDOMPosition(sel.start()).renderedOffset();
3258  int e = khtml::RenderPosition::fromDOMPosition(sel.end()).renderedOffset();
3259  text = str.mid(s, e - s);
3260  } else if (n == sel.start().node()) {
3261  text = str.mid(khtml::RenderPosition::fromDOMPosition(sel.start()).renderedOffset());
3262  } else if (n == sel.end().node()) {
3263  text += str.left(khtml::RenderPosition::fromDOMPosition(sel.end()).renderedOffset());
3264  } else {
3265  text += str;
3266  }
3267  }
3268  } else {
3269  // This is our simple HTML -> ASCII transformation:
3270  unsigned short id = n.elementId();
3271  switch (id) {
3272  case ID_TEXTAREA:
3273  text += static_cast<HTMLTextAreaElementImpl *>(n.handle())->value().string();
3274  break;
3275  case ID_INPUT:
3276  if (static_cast<HTMLInputElementImpl *>(n.handle())->inputType() != HTMLInputElementImpl::PASSWORD) {
3277  text += static_cast<HTMLInputElementImpl *>(n.handle())->value().string();
3278  }
3279  break;
3280  case ID_SELECT:
3281  text += static_cast<HTMLSelectElementImpl *>(n.handle())->value().string();
3282  break;
3283  case ID_BR:
3284  text += "\n";
3285  hasNewLine = true;
3286  break;
3287  case ID_IMG:
3288  text += static_cast<HTMLImageElementImpl *>(n.handle())->altText().string();
3289  break;
3290  case ID_TD:
3291  break;
3292  case ID_TH:
3293  case ID_HR:
3294  case ID_OL:
3295  case ID_UL:
3296  case ID_LI:
3297  case ID_DD:
3298  case ID_DL:
3299  case ID_DT:
3300  case ID_PRE:
3301  case ID_LISTING:
3302  case ID_BLOCKQUOTE:
3303  case ID_DIV:
3304  if (!hasNewLine) {
3305  text += "\n";
3306  }
3307  hasNewLine = true;
3308  break;
3309  case ID_P:
3310  case ID_TR:
3311  case ID_H1:
3312  case ID_H2:
3313  case ID_H3:
3314  case ID_H4:
3315  case ID_H5:
3316  case ID_H6:
3317  if (!hasNewLine) {
3318  text += "\n";
3319  }
3320  hasNewLine = true;
3321  break;
3322  }
3323  }
3324  if (n == sel.end().node()) {
3325  break;
3326  }
3327  DOM::Node next = n.firstChild();
3328  if (next.isNull()) {
3329  next = n.nextSibling();
3330  }
3331  while (next.isNull() && !n.parentNode().isNull()) {
3332  n = n.parentNode();
3333  next = n.nextSibling();
3334  unsigned short id = n.elementId();
3335  switch (id) {
3336  case ID_TD:
3337  seenTDTag = true; //Add two spaces after a td if then followed by text.
3338  break;
3339  case ID_TH:
3340  case ID_HR:
3341  case ID_OL:
3342  case ID_UL:
3343  case ID_LI:
3344  case ID_DD:
3345  case ID_DL:
3346  case ID_DT:
3347  case ID_PRE:
3348  case ID_LISTING:
3349  case ID_BLOCKQUOTE:
3350  case ID_DIV:
3351  seenTDTag = false;
3352  if (!hasNewLine) {
3353  text += "\n";
3354  }
3355  hasNewLine = true;
3356  break;
3357  case ID_P:
3358  case ID_TR:
3359  case ID_H1:
3360  case ID_H2:
3361  case ID_H3:
3362  case ID_H4:
3363  case ID_H5:
3364  case ID_H6:
3365  if (!hasNewLine) {
3366  text += "\n";
3367  }
3368 // text += "\n";
3369  hasNewLine = true;
3370  break;
3371  }
3372  }
3373 
3374  n = next;
3375  }
3376 
3377  if (text.isEmpty()) {
3378  return QString();
3379  }
3380 
3381  int start = 0;
3382  int end = text.length();
3383 
3384  // Strip leading LFs
3385  while ((start < end) && (text[start] == '\n')) {
3386  ++start;
3387  }
3388 
3389  // Strip excessive trailing LFs
3390  while ((start < (end - 1)) && (text[end - 1] == '\n') && (text[end - 2] == '\n')) {
3391  --end;
3392  }
3393 
3394  return text.mid(start, end - start);
3395 }
3396 
3397 QString KHTMLPart::simplifiedSelectedText() const
3398 {
3399  QString text = selectedText();
3400  text.replace(QChar(0xa0), ' ');
3401  // remove leading and trailing whitespace
3402  while (!text.isEmpty() && text[0].isSpace()) {
3403  text = text.mid(1);
3404  }
3405  while (!text.isEmpty() && text[text.length() - 1].isSpace()) {
3406  text.truncate(text.length() - 1);
3407  }
3408  return text;
3409 }
3410 
3412 {
3413  return !d->editor_context.m_selection.isEmpty() && !d->editor_context.m_selection.isCollapsed();
3414 }
3415 
3416 DOM::Range KHTMLPart::selection() const
3417 {
3418  return d->editor_context.m_selection.toRange();
3419 }
3420 
3421 void KHTMLPart::selection(DOM::Node &s, long &so, DOM::Node &e, long &eo) const
3422 {
3423  DOM::Range r = d->editor_context.m_selection.toRange();
3424  s = r.startContainer();
3425  so = r.startOffset();
3426  e = r.endContainer();
3427  eo = r.endOffset();
3428 }
3429 
3430 void KHTMLPart::setSelection(const DOM::Range &r)
3431 {
3432  setCaret(r);
3433 }
3434 
3435 const Selection &KHTMLPart::caret() const
3436 {
3437  return d->editor_context.m_selection;
3438 }
3439 
3440 const Selection &KHTMLPart::dragCaret() const
3441 {
3442  return d->editor_context.m_dragCaret;
3443 }
3444 
3445 void KHTMLPart::setCaret(const Selection &s, bool closeTyping)
3446 {
3447  if (d->editor_context.m_selection != s) {
3448  clearCaretRectIfNeeded();
3449  setFocusNodeIfNeeded(s);
3450  d->editor_context.m_selection = s;
3451  notifySelectionChanged(closeTyping);
3452  }
3453 }
3454 
3455 void KHTMLPart::setDragCaret(const DOM::Selection &dragCaret)
3456 {
3457  if (d->editor_context.m_dragCaret != dragCaret) {
3458  d->editor_context.m_dragCaret.needsCaretRepaint();
3459  d->editor_context.m_dragCaret = dragCaret;
3460  d->editor_context.m_dragCaret.needsCaretRepaint();
3461  }
3462 }
3463 
3464 void KHTMLPart::clearSelection()
3465 {
3466  clearCaretRectIfNeeded();
3467  setFocusNodeIfNeeded(d->editor_context.m_selection);
3468 #ifdef APPLE_CHANGES
3469  d->editor_context.m_selection.clear();
3470 #else
3471  d->editor_context.m_selection.collapse();
3472 #endif
3473  notifySelectionChanged();
3474 }
3475 
3476 void KHTMLPart::invalidateSelection()
3477 {
3478  clearCaretRectIfNeeded();
3479  d->editor_context.m_selection.setNeedsLayout();
3480  selectionLayoutChanged();
3481 }
3482 
3483 void KHTMLPart::setSelectionVisible(bool flag)
3484 {
3485  if (d->editor_context.m_caretVisible == flag) {
3486  return;
3487  }
3488 
3489  clearCaretRectIfNeeded();
3490  setFocusNodeIfNeeded(d->editor_context.m_selection);
3491  d->editor_context.m_caretVisible = flag;
3492 // notifySelectionChanged();
3493 }
3494 
3495 #if 1
3496 void KHTMLPart::slotClearSelection()
3497 {
3498  if (!isCaretMode()
3499  && d->editor_context.m_selection.state() != Selection::NONE
3500  && !d->editor_context.m_selection.caretPos().node()->isContentEditable()) {
3501  clearCaretRectIfNeeded();
3502  }
3503  bool hadSelection = hasSelection();
3504 #ifdef APPLE_CHANGES
3505  d->editor_context.m_selection.clear();
3506 #else
3507  d->editor_context.m_selection.collapse();
3508 #endif
3509  if (hadSelection) {
3510  notifySelectionChanged();
3511  }
3512 }
3513 #endif
3514 
3515 void KHTMLPart::clearCaretRectIfNeeded()
3516 {
3517  if (d->editor_context.m_caretPaint) {
3518  d->editor_context.m_caretPaint = false;
3519  d->editor_context.m_selection.needsCaretRepaint();
3520  }
3521 }
3522 
3523 void KHTMLPart::setFocusNodeIfNeeded(const Selection &s)
3524 {
3525  if (!xmlDocImpl() || s.state() == Selection::NONE) {
3526  return;
3527  }
3528 
3529  NodeImpl *n = s.start().node();
3530  NodeImpl *target = (n && n->isContentEditable()) ? n : nullptr;
3531  if (!target) {
3532  while (n && n != s.end().node()) {
3533  if (n->isContentEditable()) {
3534  target = n;
3535  break;
3536  }
3537  n = n->traverseNextNode();
3538  }
3539  }
3540  assert(target == nullptr || target->isContentEditable());
3541 
3542  if (target) {
3543  for (; target && !target->isFocusable(); target = target->parentNode()) {
3544  }
3545  if (target && target->isMouseFocusable()) {
3546  xmlDocImpl()->setFocusNode(target);
3547  } else if (!target || !target->focused()) {
3548  xmlDocImpl()->setFocusNode(nullptr);
3549  }
3550  }
3551 }
3552 
3553 void KHTMLPart::selectionLayoutChanged()
3554 {
3555  // kill any caret blink timer now running
3556  if (d->editor_context.m_caretBlinkTimer >= 0) {
3557  killTimer(d->editor_context.m_caretBlinkTimer);
3558  d->editor_context.m_caretBlinkTimer = -1;
3559  }
3560 
3561  // see if a new caret blink timer needs to be started
3562  if (d->editor_context.m_caretVisible
3563  && d->editor_context.m_selection.state() != Selection::NONE) {
3564  d->editor_context.m_caretPaint = isCaretMode()
3565  || d->editor_context.m_selection.caretPos().node()->isContentEditable();
3566  if (d->editor_context.m_caretBlinks && d->editor_context.m_caretPaint) {
3567  d->editor_context.m_caretBlinkTimer = startTimer(qApp->cursorFlashTime() / 2);
3568  }
3569  d->editor_context.m_selection.needsCaretRepaint();
3570  // make sure that caret is visible
3571  QRect r(d->editor_context.m_selection.getRepaintRect());
3572  if (d->editor_context.m_caretPaint) {
3573  d->m_view->ensureVisible(r.x(), r.y());
3574  }
3575  }
3576 
3577  if (d->m_doc) {
3578  d->m_doc->updateSelection();
3579  }
3580 
3581  // Always clear the x position used for vertical arrow navigation.
3582  // It will be restored by the vertical arrow navigation code if necessary.
3583  d->editor_context.m_xPosForVerticalArrowNavigation = d->editor_context.NoXPosForVerticalArrowNavigation;
3584 }
3585 
3586 void KHTMLPart::notifySelectionChanged(bool closeTyping)
3587 {
3588  Editor *ed = d->editor_context.m_editor;
3589  selectionLayoutChanged();
3590  if (ed) {
3591  ed->clearTypingStyle();
3592 
3593  if (closeTyping) {
3594  ed->closeTyping();
3595  }
3596  }
3597 
3598  emitSelectionChanged();
3599 }
3600 
3602 {
3603  if (e->timerId() == d->editor_context.m_caretBlinkTimer) {
3604  if (d->editor_context.m_caretBlinks &&
3605  d->editor_context.m_selection.state() != Selection::NONE) {
3606  d->editor_context.m_caretPaint = !d->editor_context.m_caretPaint;
3607  d->editor_context.m_selection.needsCaretRepaint();
3608  }
3609  } else if (e->timerId() == d->m_DNSPrefetchTimer) {
3610  // qCDebug(KHTML_LOG) << "will lookup " << d->m_DNSPrefetchQueue.head() << d->m_numDNSPrefetchedNames;
3611  KIO::HostInfo::prefetchHost(d->m_DNSPrefetchQueue.dequeue());
3612  if (d->m_DNSPrefetchQueue.isEmpty()) {
3613  killTimer(d->m_DNSPrefetchTimer);
3614  d->m_DNSPrefetchTimer = -1;
3615  }
3616  } else if (e->timerId() == d->m_DNSTTLTimer) {
3617  foreach (const QString &name, d->m_lookedupHosts) {
3618  d->m_DNSPrefetchQueue.enqueue(name);
3619  }
3620  if (d->m_DNSPrefetchTimer <= 0) {
3621  d->m_DNSPrefetchTimer = startTimer(sDNSPrefetchTimerDelay);
3622  }
3623  }
3624 }
3625 
3627 {
3628  if (d->m_bDNSPrefetch == DNSPrefetchDisabled) {
3629  return false;
3630  }
3631 
3632  if (d->m_numDNSPrefetchedNames >= sMaxDNSPrefetchPerPage) {
3633  return false;
3634  }
3635 
3636  if (d->m_bDNSPrefetch == DNSPrefetchOnlyWWWAndSLD) {
3637  int dots = name.count('.');
3638  if (dots > 2 || (dots == 2 && !name.startsWith("www."))) {
3639  return false;
3640  }
3641  }
3642 
3643  if (d->m_lookedupHosts.contains(name)) {
3644  return false;
3645  }
3646 
3647  d->m_DNSPrefetchQueue.enqueue(name);
3648  d->m_lookedupHosts.insert(name);
3649  d->m_numDNSPrefetchedNames++;
3650 
3651  if (d->m_DNSPrefetchTimer < 1) {
3652  d->m_DNSPrefetchTimer = startTimer(sDNSPrefetchTimerDelay);
3653  }
3654  if (d->m_DNSTTLTimer < 1) {
3655  d->m_DNSTTLTimer = startTimer(sDNSTTLSeconds * 1000 + 1);
3656  }
3657 
3658  return true;
3659 }
3660 
3661 void KHTMLPart::paintCaret(QPainter *p, const QRect &rect) const
3662 {
3663  if (d->editor_context.m_caretPaint) {
3664  d->editor_context.m_selection.paintCaret(p, rect);
3665  }
3666 }
3667 
3668 void KHTMLPart::paintDragCaret(QPainter *p, const QRect &rect) const
3669 {
3670  d->editor_context.m_dragCaret.paintCaret(p, rect);
3671 }
3672 
3674 {
3675  if (!d->editor_context.m_editor) {
3676  const_cast<KHTMLPart *>(this)->d->editor_context.m_editor = new DOM::Editor(const_cast<KHTMLPart *>(this));
3677  }
3678  return d->editor_context.m_editor;
3679 }
3680 
3681 void KHTMLPart::resetHoverText()
3682 {
3683  if (!d->m_overURL.isEmpty()) { // Only if we were showing a link
3684  d->m_overURL.clear();
3685  d->m_overURLTarget.clear();
3686  emit onURL(QString());
3687  // revert to default statusbar text
3688  setStatusBarText(QString(), BarHoverText);
3689  emit d->m_extension->mouseOverInfo(KFileItem());
3690  }
3691 }
3692 
3693 void KHTMLPart::overURL(const QString &url, const QString &target, bool /*shiftPressed*/)
3694 {
3695  QUrl u = completeURL(url);
3696 
3697  // special case for <a href="">
3698  if (url.isEmpty()) {
3700  }
3701 
3702  emit onURL(url);
3703 
3704  if (url.isEmpty()) {
3705  setStatusBarText(u.toDisplayString().toHtmlEscaped(), BarHoverText);
3706  return;
3707  }
3708 
3709  if (d->isJavaScriptURL(url)) {
3710  QString jscode = d->codeForJavaScriptURL(url);
3711  jscode = KStringHandler::rsqueeze(jscode, 80); // truncate if too long
3712  if (url.startsWith("javascript:window.open")) {
3713  jscode += i18n(" (In new window)");
3714  }
3715  setStatusBarText(jscode.toHtmlEscaped(), BarHoverText);
3716  return;
3717  }
3718 
3719  KFileItem item(u, QString(), KFileItem::Unknown);
3720  emit d->m_extension->mouseOverInfo(item);
3721  const QString com = item.mimeComment();
3722 
3723  if (!u.isValid()) {
3724  setStatusBarText(u.toDisplayString().toHtmlEscaped(), BarHoverText);
3725  return;
3726  }
3727 
3728  if (u.isLocalFile()) {
3729  // TODO : use KIO::stat() and create a KFileItem out of its result,
3730  // to use KFileItem::statusBarText()
3731 
3732  QFileInfo info(u.toLocalFile());
3733  bool ok = info.exists();
3734 
3735  QString text = u.toDisplayString().toHtmlEscaped();
3736  QString text2 = text;
3737 
3738  if (info.isSymLink()) {
3739  QString tmp;
3740  if (com.isEmpty()) {
3741  tmp = i18n("Symbolic Link");
3742  } else {
3743  tmp = i18n("%1 (Link)", com);
3744  }
3745  text += " -> ";
3746  QString target = info.symLinkTarget();
3747  if (target.isEmpty()) {
3748  text2 += " ";
3749  text2 += tmp;
3750  setStatusBarText(text2, BarHoverText);
3751  return;
3752  }
3753 
3754  text += target;
3755  text += " ";
3756  text += tmp;
3757  } else if (ok && info.isFile()) {
3758  if (info.size() < 1024) {
3759  text = i18np("%2 (%1 byte)", "%2 (%1 bytes)", (long) info.size(), text2); // always put the URL last, in case it contains '%'
3760  } else {
3761  float d = (float) info.size() / 1024.0;
3762  text = i18n("%2 (%1 K)", QLocale().toString(d, 'f', 2), text2); // was %.2f
3763  }
3764  text += " ";
3765  text += com;
3766  } else if (ok && info.isDir()) {
3767  text += " ";
3768  text += com;
3769  } else {
3770  text += " ";
3771  text += com;
3772  }
3773  setStatusBarText(text, BarHoverText);
3774  } else {
3775  QString extra;
3776  if (target.toLower() == "_blank") {
3777  extra = i18n(" (In new window)");
3778  } else if (!target.isEmpty() &&
3779  (target.toLower() != "_top") &&
3780  (target.toLower() != "_self") &&
3781  (target.toLower() != "_parent")) {
3782  KHTMLPart *p = this;
3783  while (p->parentPart()) {
3784  p = p->parentPart();
3785  }
3786  if (!p->frameExists(target)) {
3787  extra = i18n(" (In new window)");
3788  } else {
3789  extra = i18n(" (In other frame)");
3790  }
3791  }
3792 
3793  if (u.scheme() == QLatin1String("mailto")) {
3794  QString mailtoMsg /* = QString::fromLatin1("<img src=%1>").arg(locate("icon", QString::fromLatin1("locolor/16x16/actions/mail_send.png")))*/;
3795  mailtoMsg += i18n("Email to: ") + QUrl::fromPercentEncoding(u.path().toLatin1());
3796  const QStringList queries = u.query().mid(1).split('&');
3797  QStringList::ConstIterator it = queries.begin();
3798  const QStringList::ConstIterator itEnd = queries.end();
3799  for (; it != itEnd; ++it)
3800  if ((*it).startsWith(QLatin1String("subject="))) {
3801  mailtoMsg += i18n(" - Subject: ") + QUrl::fromPercentEncoding((*it).mid(8).toLatin1());
3802  } else if ((*it).startsWith(QLatin1String("cc="))) {
3803  mailtoMsg += i18n(" - CC: ") + QUrl::fromPercentEncoding((*it).mid(3).toLatin1());
3804  } else if ((*it).startsWith(QLatin1String("bcc="))) {
3805  mailtoMsg += i18n(" - BCC: ") + QUrl::fromPercentEncoding((*it).mid(4).toLatin1());
3806  }
3807  mailtoMsg = mailtoMsg.toHtmlEscaped();
3808  mailtoMsg.replace(QRegExp("([\n\r\t]|[ ]{10})"), QString());
3809  setStatusBarText("<qt>" + mailtoMsg, BarHoverText);
3810  return;
3811  }
3812  // Is this check necessary at all? (Frerich)
3813 #if 0
3814  else if (u.scheme() == QLatin1String("http")) {
3815  DOM::Node hrefNode = nodeUnderMouse().parentNode();
3816  while (hrefNode.nodeName().string() != QLatin1String("A") && !hrefNode.isNull()) {
3817  hrefNode = hrefNode.parentNode();
3818  }
3819 
3820  if (!hrefNode.isNull()) {
3821  DOM::Node hreflangNode = hrefNode.attributes().getNamedItem("HREFLANG");
3822  if (!hreflangNode.isNull()) {
3823  QString countryCode = hreflangNode.nodeValue().string().toLower();
3824  // Map the language code to an appropriate country code.
3825  if (countryCode == QLatin1String("en")) {
3826  countryCode = QLatin1String("gb");
3827  }
3828  QString flagImg = QLatin1String("<img src=%1>").arg(
3829  locate("locale", QLatin1String("l10n/")
3830  + countryCode
3831  + QLatin1String("/kf5_flag.png")));
3832  emit setStatusBarText(flagImg + u.toDisplayString() + extra);
3833  }
3834  }
3835  }
3836 #endif
3837  setStatusBarText(u.toDisplayString().toHtmlEscaped() + extra, BarHoverText);
3838  }
3839 }
3840 
3841 //
3842 // This executes in the active part on a click or other url selection action in
3843 // that active part.
3844 //
3845 bool KHTMLPart::urlSelected(const QString &url, int button, int state, const QString &_target, const KParts::OpenUrlArguments &_args, const KParts::BrowserArguments &_browserArgs)
3846 {
3847  KParts::OpenUrlArguments args = _args;
3848  KParts::BrowserArguments browserArgs = _browserArgs;
3849  bool hasTarget = false;
3850 
3851  QString target = _target;
3852  if (target.isEmpty() && d->m_doc) {
3853  target = d->m_doc->baseTarget();
3854  }
3855  if (!target.isEmpty()) {
3856  hasTarget = true;
3857  }
3858 
3859  if (d->isJavaScriptURL(url)) {
3860  crossFrameExecuteScript(target, d->codeForJavaScriptURL(url));
3861  return false;
3862  }
3863 
3864  QUrl cURL = completeURL(url);
3865  // special case for <a href=""> (IE removes filename, mozilla doesn't)
3866  if (url.isEmpty()) {
3867  cURL = cURL.adjusted(QUrl::RemoveFilename);
3868  }
3869 
3870  if (!cURL.isValid())
3871  // ### ERROR HANDLING
3872  {
3873  return false;
3874  }
3875 
3876  // qCDebug(KHTML_LOG) << this << "complete URL:" << cURL << "target=" << target;
3877 
3878  if (state & Qt::ControlModifier) {
3879  emit d->m_extension->createNewWindow(cURL, args, browserArgs);
3880  return true;
3881  }
3882 
3883  if (button == Qt::LeftButton && (state & Qt::ShiftModifier)) {
3884  KIO::MetaData metaData;
3885  metaData.insert("referrer", d->m_referrer);
3886  KHTMLPopupGUIClient::saveURL(d->m_view, i18n("Save As"), cURL, metaData);
3887  return false;
3888  }
3889 
3890  if (!checkLinkSecurity(cURL,
3891  ki18n("<qt>This untrusted page links to<br /><b>%1</b>.<br />Do you want to follow the link?</qt>"),
3892  i18n("Follow"))) {
3893  return false;
3894  }
3895 
3896  browserArgs.frameName = target;
3897 
3898  args.metaData().insert("main_frame_request",
3899  parentPart() == nullptr ? "TRUE" : "FALSE");
3900  args.metaData().insert("ssl_parent_ip", d->m_ssl_parent_ip);
3901  args.metaData().insert("ssl_parent_cert", d->m_ssl_parent_cert);
3902  args.metaData().insert("PropagateHttpHeader", "true");
3903  args.metaData().insert("ssl_was_in_use", d->m_ssl_in_use ? "TRUE" : "FALSE");
3904  args.metaData().insert("ssl_activate_warnings", "TRUE");
3905 
3906  if (hasTarget && target != "_self" && target != "_top" && target != "_blank" && target != "_parent") {
3907  // unknown frame names should open in a new window.
3908  khtml::ChildFrame *frame = recursiveFrameRequest(this, cURL, args, browserArgs, false);
3909  if (frame) {
3910  args.metaData()["referrer"] = d->m_referrer;
3911  requestObject(frame, cURL, args, browserArgs);
3912  return true;
3913  }
3914  }
3915 
3916  if (!d->m_referrer.isEmpty() && !args.metaData().contains("referrer")) {
3917  args.metaData()["referrer"] = d->m_referrer;
3918  }
3919 
3920  if (button == Qt::NoButton && (state & Qt::ShiftModifier) && (state & Qt::ControlModifier)) {
3921  emit d->m_extension->createNewWindow(cURL, args, browserArgs);
3922  return true;
3923  }
3924 
3925  if (state & Qt::ShiftModifier) {
3926  KParts::WindowArgs winArgs;
3927  winArgs.setLowerWindow(true);
3928  emit d->m_extension->createNewWindow(cURL, args, browserArgs, winArgs);
3929  return true;
3930  }
3931 
3932  //If we're asked to open up an anchor in the current URL, in current window,
3933  //merely gotoanchor, and do not reload the new page. Note that this does
3934  //not apply if the URL is the same page, but without a ref
3935  if (cURL.hasFragment() && (!hasTarget || target == "_self")) {
3936  if (d->isLocalAnchorJump(cURL)) {
3937  d->executeAnchorJump(cURL, browserArgs.lockHistory());
3938  return false; // we jumped, but we didn't open a URL
3939  }
3940  }
3941 
3942  if (!d->m_bComplete && !hasTarget) {
3943  closeUrl();
3944  }
3945 
3946  view()->viewport()->unsetCursor();
3947  emit d->m_extension->openUrlRequest(cURL, args, browserArgs);
3948  return true;
3949 }
3950 
3951 void KHTMLPart::slotViewDocumentSource()
3952 {
3953  QUrl currentUrl(this->url());
3954  KRun::RunFlags runFlags;
3955  if (!(currentUrl.isLocalFile()) && KHTMLPageCache::self()->isComplete(d->m_cacheId)) {
3956  QTemporaryFile sourceFile(QDir::tempPath() + QLatin1String("/XXXXXX") + defaultExtension());
3957  sourceFile.setAutoRemove(false);
3958  if (sourceFile.open()) {
3959  QDataStream stream(&sourceFile);
3960  KHTMLPageCache::self()->saveData(d->m_cacheId, &stream);
3961  currentUrl = QUrl::fromLocalFile(sourceFile.fileName());
3962  runFlags |= KRun::DeleteTemporaryFiles;
3963  }
3964  }
3965 
3966  (void) KRun::runUrl(currentUrl, QLatin1String("text/plain"), view(), runFlags);
3967 }
3968 
3969 void KHTMLPart::slotViewPageInfo()
3970 {
3971  Ui_KHTMLInfoDlg ui;
3972 
3973  QDialog *dlg = new QDialog(nullptr);
3975  dlg->setObjectName("KHTML Page Info Dialog");
3976  ui.setupUi(dlg);
3977 
3978  KGuiItem::assign(ui._close, KStandardGuiItem::close());
3979  connect(ui._close, SIGNAL(clicked()), dlg, SLOT(accept()));
3980 
3981  if (d->m_doc) {
3982  ui._title->setText(d->m_doc->title().string().trimmed());
3983  }
3984 
3985  // If it's a frame, set the caption to "Frame Information"
3986  if (parentPart() && d->m_doc && d->m_doc->isHTMLDocument()) {
3987  dlg->setWindowTitle(i18n("Frame Information"));
3988  }
3989 
3990  QString editStr;
3991 
3992  if (!d->m_pageServices.isEmpty()) {
3993  editStr = i18n(" <a href=\"%1\">[Properties]</a>", d->m_pageServices);
3994  }
3995 
3996  QString squeezedURL = KStringHandler::csqueeze(url().toDisplayString(), 80);
3997  ui._url->setText("<a href=\"" + url().toString() + "\">" + squeezedURL + "</a>" + editStr);
3998  if (lastModified().isEmpty()) {
3999  ui._lastModified->hide();
4000  ui._lmLabel->hide();
4001  } else {
4002  ui._lastModified->setText(lastModified());
4003  }
4004 
4005  const QString &enc = encoding();
4006  if (enc.isEmpty()) {
4007  ui._eLabel->hide();
4008  ui._encoding->hide();
4009  } else {
4010  ui._encoding->setText(enc);
4011  }
4012 
4013  if (!xmlDocImpl() || xmlDocImpl()->parseMode() == DOM::DocumentImpl::Unknown) {
4014  ui._mode->hide();
4015  ui._modeLabel->hide();
4016  } else {
4017  switch (xmlDocImpl()->parseMode()) {
4018  case DOM::DocumentImpl::Compat:
4019  ui._mode->setText(i18nc("HTML rendering mode (see https://en.wikipedia.org/wiki/Quirks_mode)", "Quirks"));
4020  break;
4021  case DOM::DocumentImpl::Transitional:
4022  ui._mode->setText(i18nc("HTML rendering mode (see https://en.wikipedia.org/wiki/Quirks_mode)", "Almost standards"));
4023  break;
4024  case DOM::DocumentImpl::Strict:
4025  default: // others handled above
4026  ui._mode->setText(i18nc("HTML rendering mode (see https://en.wikipedia.org/wiki/Quirks_mode)", "Strict"));
4027  break;
4028  }
4029  }
4030 
4031  /* populate the list view now */
4032  const QStringList headers = d->m_httpHeaders.split("\n");
4033 
4034  QStringList::ConstIterator it = headers.begin();
4035  const QStringList::ConstIterator itEnd = headers.end();
4036 
4037  for (; it != itEnd; ++it) {
4038  const QStringList header = (*it).split(QRegExp(":[ ]+"));
4039  if (header.count() != 2) {
4040  continue;
4041  }
4042  QTreeWidgetItem *item = new QTreeWidgetItem(ui._headers);
4043  item->setText(0, header[0]);
4044  item->setText(1, header[1]);
4045  }
4046 
4047  dlg->show();
4048  /* put no code here */
4049 }
4050 
4051 void KHTMLPart::slotViewFrameSource()
4052 {
4054  if (!frame) {
4055  return;
4056  }
4057 
4058  QUrl url = frame->url();
4059  KRun::RunFlags runFlags;
4060  if (!(url.isLocalFile()) && frame->inherits("KHTMLPart")) {
4061  long cacheId = static_cast<KHTMLPart *>(frame)->d->m_cacheId;
4062 
4063  if (KHTMLPageCache::self()->isComplete(cacheId)) {
4064  QTemporaryFile sourceFile(QDir::tempPath() + QLatin1String("/XXXXXX") + defaultExtension());
4065  sourceFile.setAutoRemove(false);
4066  if (sourceFile.open()) {
4067  QDataStream stream(&sourceFile);
4068  KHTMLPageCache::self()->saveData(cacheId, &stream);
4069  url = QUrl();
4070  url.setPath(sourceFile.fileName());
4071  runFlags |= KRun::DeleteTemporaryFiles;
4072  }
4073  }
4074  }
4075 
4076  (void) KRun::runUrl(url, QLatin1String("text/plain"), view(), runFlags);
4077 }
4078 
4080 {
4081  // ### what about XML documents? get from CSS?
4082  if (!d->m_doc || !d->m_doc->isHTMLDocument()) {
4083  return QUrl();
4084  }
4085 
4086  QString relURL = static_cast<HTMLDocumentImpl *>(d->m_doc)->body()->getAttribute(ATTR_BACKGROUND).string();
4087 
4088  return url().resolved(QUrl(relURL));
4089 }
4090 
4091 void KHTMLPart::slotSaveBackground()
4092 {
4093  KIO::MetaData metaData;
4094  metaData["referrer"] = d->m_referrer;
4095  KHTMLPopupGUIClient::saveURL(d->m_view, i18n("Save Background Image As"), backgroundURL(), metaData);
4096 }
4097 
4098 void KHTMLPart::slotSaveDocument()
4099 {
4100  QUrl srcURL(url());
4101 
4102  if (srcURL.fileName().isEmpty()) {
4103  srcURL.setPath(srcURL.path() + "index" + defaultExtension());
4104  }
4105 
4106  KIO::MetaData metaData;
4107  // Referre unknown?
4108  KHTMLPopupGUIClient::saveURL(d->m_view, i18n("Save As"), srcURL, metaData, "text/html", d->m_cacheId);
4109 }
4110 
4111 void KHTMLPart::slotSecurity()
4112 {
4113 // qCDebug(KHTML_LOG) << "Meta Data:" << endl
4114 // << d->m_ssl_peer_cert_subject
4115 // << endl
4116 // << d->m_ssl_peer_cert_issuer
4117 // << endl
4118 // << d->m_ssl_cipher
4119 // << endl
4120 // << d->m_ssl_cipher_desc
4121 // << endl
4122 // << d->m_ssl_cipher_version
4123 // << endl
4124 // << d->m_ssl_good_from
4125 // << endl
4126 // << d->m_ssl_good_until
4127 // << endl
4128 // << d->m_ssl_cert_state
4129 // << endl;
4130 
4131  //### reenable with new signature
4132 #if 0
4133  KSslInfoDialog *kid = new KSslInfoDialog(d->m_ssl_in_use, widget(), "kssl_info_dlg", true);
4134 
4135  const QStringList sl = d->m_ssl_peer_chain.split('\n', QString::SkipEmptyParts);
4136  QList<QSslCertificate> certChain;
4137  bool certChainOk = d->m_ssl_in_use;
4138  if (certChainOk) {
4139  foreach (const QString &s, sl) {
4140  certChain.append(QSslCertificate(s.toLatin1())); //or is it toLocal8Bit or whatever?
4141  if (certChain.last().isNull()) {
4142  certChainOk = false;
4143  break;
4144  }
4145  }
4146  }
4147  if (certChainOk) {
4148  kid->setup(certChain,
4149  d->m_ssl_peer_ip,
4150  url().toString(),
4151  d->m_ssl_cipher,
4152  d->m_ssl_cipher_desc,
4153  d->m_ssl_cipher_version,
4154  d->m_ssl_cipher_used_bits.toInt(),
4155  d->m_ssl_cipher_bits.toInt(),
4156  (KSSLCertificate::KSSLValidation) d->m_ssl_cert_state.toInt());
4157  }
4158  kid->exec();
4159  //the dialog deletes itself on close
4160 #endif
4161 
4162  KSslInfoDialog *kid = new KSslInfoDialog(nullptr);
4163  //### This is boilerplate code and it's copied from SlaveInterface.
4164  QStringList sl = d->m_ssl_peer_chain.split('\x01', QString::SkipEmptyParts);
4165  QList<QSslCertificate> certChain;
4166  bool decodedOk = true;
4167  foreach (const QString &s, sl) {
4168  certChain.append(QSslCertificate(s.toLatin1())); //or is it toLocal8Bit or whatever?
4169  if (certChain.last().isNull()) {
4170  decodedOk = false;
4171  break;
4172  }
4173  }
4174 
4175  if (decodedOk || true /*H4X*/) {
4176  kid->setSslInfo(certChain,
4177  d->m_ssl_peer_ip,
4178  url().host(),
4179  d->m_ssl_protocol_version,
4180  d->m_ssl_cipher,
4181  d->m_ssl_cipher_used_bits.toInt(),
4182  d->m_ssl_cipher_bits.toInt(),
4183  KSslInfoDialog::errorsFromString(d->m_ssl_cert_errors));
4184  // qCDebug(KHTML_LOG) << "Showing SSL Info dialog";
4185  kid->exec();
4186  // qCDebug(KHTML_LOG) << "SSL Info dialog closed";
4187  } else {
4188  KMessageBox::information(nullptr, i18n("The peer SSL certificate chain "
4189  "appears to be corrupt."),
4190  i18n("SSL"));
4191  }
4192 }
4193 
4194 void KHTMLPart::slotSaveFrame()
4195 {
4197  if (!frame) {
4198  return;
4199  }
4200 
4201  QUrl srcURL(frame->url());
4202 
4203  if (srcURL.fileName().isEmpty()) {
4204  srcURL.setPath(srcURL.path() + "index" + defaultExtension());
4205  }
4206 
4207  KIO::MetaData metaData;
4208  // Referrer unknown?
4209  KHTMLPopupGUIClient::saveURL(d->m_view, i18n("Save Frame As"), srcURL, metaData, "text/html");
4210 }
4211 
4212 void KHTMLPart::slotSetEncoding(const QString &enc)
4213 {
4214  d->m_autoDetectLanguage = KEncodingProber::None;
4215  setEncoding(enc, true);
4216 }
4217 
4218 void KHTMLPart::slotAutomaticDetectionLanguage(KEncodingProber::ProberType scri)
4219 {
4220  d->m_autoDetectLanguage = scri;
4221  setEncoding(QString(), false);
4222 }
4223 
4224 void KHTMLPart::slotUseStylesheet()
4225 {
4226  if (d->m_doc) {
4227  bool autoselect = (d->m_paUseStylesheet->currentItem() == 0);
4228  d->m_sheetUsed = autoselect ? QString() : d->m_paUseStylesheet->currentText();
4229  d->m_doc->updateStyleSelector();
4230  }
4231 }
4232 
4233 void KHTMLPart::updateActions()
4234 {
4235  bool frames = false;
4236 
4237  QList<khtml::ChildFrame *>::ConstIterator it = d->m_frames.constBegin();
4238  const QList<khtml::ChildFrame *>::ConstIterator end = d->m_frames.constEnd();
4239  for (; it != end; ++it)
4240  if ((*it)->m_type == khtml::ChildFrame::Frame) {
4241  frames = true;
4242  break;
4243  }
4244 
4245  if (d->m_paViewFrame) {
4246  d->m_paViewFrame->setEnabled(frames);
4247  }
4248  if (d->m_paSaveFrame) {
4249  d->m_paSaveFrame->setEnabled(frames);
4250  }
4251 
4252  if (frames) {
4253  d->m_paFind->setText(i18n("&Find in Frame..."));
4254  } else {
4255  d->m_paFind->setText(i18n("&Find..."));
4256  }
4257 
4258  KParts::Part *frame = nullptr;
4259 
4260  if (frames) {
4261  frame = currentFrame();
4262  }
4263 
4264  bool enableFindAndSelectAll = true;
4265 
4266  if (frame) {
4267  enableFindAndSelectAll = frame->inherits("KHTMLPart");
4268  }
4269 
4270  d->m_paFind->setEnabled(enableFindAndSelectAll);
4271  d->m_paSelectAll->setEnabled(enableFindAndSelectAll);
4272 
4273  bool enablePrintFrame = false;
4274 
4275  if (frame) {
4277  if (ext) {
4278  enablePrintFrame = ext->metaObject()->indexOfSlot("print()") != -1;
4279  }
4280  }
4281 
4282  d->m_paPrintFrame->setEnabled(enablePrintFrame);
4283 
4284  QString bgURL;
4285 
4286  // ### frames
4287  if (d->m_doc && d->m_doc->isHTMLDocument() && static_cast<HTMLDocumentImpl *>(d->m_doc)->body() && !d->m_bClearing) {
4288  bgURL = static_cast<HTMLDocumentImpl *>(d->m_doc)->body()->getAttribute(ATTR_BACKGROUND).string();
4289  }
4290 
4291  if (d->m_paSaveBackground) {
4292  d->m_paSaveBackground->setEnabled(!bgURL.isEmpty());
4293  }
4294 
4295  if (d->m_paDebugScript) {
4296  d->m_paDebugScript->setEnabled(d->m_frame ? d->m_frame->m_jscript : nullptr);
4297  }
4298 }
4299 
4300 KParts::ScriptableExtension *KHTMLPart::scriptableExtension(const DOM::NodeImpl *frame)
4301 {
4302  const ConstFrameIt end = d->m_objects.constEnd();
4303  for (ConstFrameIt it = d->m_objects.constBegin(); it != end; ++it)
4304  if ((*it)->m_partContainerElement.data() == frame) {
4305  return (*it)->m_scriptable.data();
4306  }
4307  return nullptr;
4308 }
4309 
4310 void KHTMLPart::loadFrameElement(DOM::HTMLPartContainerElementImpl *frame, const QString &url,
4311  const QString &frameName, const QStringList &params, bool isIFrame)
4312 {
4313  //qCDebug(KHTML_LOG) << this << " requestFrame( ..., " << url << ", " << frameName << " )";
4314  khtml::ChildFrame *child;
4315 
4316  FrameIt it = d->m_frames.find(frameName);
4317  if (it == d->m_frames.end()) {
4318  child = new khtml::ChildFrame;
4319  //qCDebug(KHTML_LOG) << "inserting new frame into frame map " << frameName;
4320  child->m_name = frameName;
4321  d->m_frames.insert(d->m_frames.end(), child);
4322  } else {
4323  child = *it;
4324  }
4325 
4326  child->m_type = isIFrame ? khtml::ChildFrame::IFrame : khtml::ChildFrame::Frame;
4327  child->m_partContainerElement = frame;
4328  child->m_params = params;
4329 
4330  // If we do not have a part, make sure we create one.
4331  if (!child->m_part) {
4332  QStringList dummy; // the list of servicetypes handled by the part is now unused.
4333  QString khtml = QString::fromLatin1("khtml");
4334  KParts::ReadOnlyPart *part = createPart(d->m_view->viewport(), this,
4335  QString::fromLatin1("text/html"),
4336  khtml, dummy, QStringList());
4337  // We navigate it to about:blank to setup an empty one, but we do it
4338  // before hooking up the signals and extensions, so that any sync emit
4339  // of completed by the kid doesn't cause us to be marked as completed.
4340  // (async ones are discovered by the presence of the KHTMLRun)
4341  // ### load event on the kid?
4342  navigateLocalProtocol(child, part, QUrl("about:blank"));
4343  connectToChildPart(child, part, "text/html" /* mimetype of the part, not what's being loaded */);
4344  }
4345 
4346  QUrl u = url.isEmpty() ? QUrl() : completeURL(url);
4347 
4348  // Since we don't specify args here a KHTMLRun will be used to determine the
4349  // mimetype, which will then be passed down at the bottom of processObjectRequest
4350  // inside URLArgs to the part. In our particular case, this means that we can
4351  // use that inside KHTMLPart::openUrl to route things appropriately.
4352  child->m_bCompleted = false;
4353  if (!requestObject(child, u) && !child->m_run) {
4354  child->m_bCompleted = true;
4355  }
4356 }
4357 
4358 QString KHTMLPart::requestFrameName()
4359 {
4360  return QString::fromLatin1("<!--frame %1-->").arg(d->m_frameNameId++);
4361 }
4362 
4363 bool KHTMLPart::loadObjectElement(DOM::HTMLPartContainerElementImpl *frame, const QString &url,
4364  const QString &serviceType, const QStringList &params)
4365 {
4366  //qCDebug(KHTML_LOG) << this << "frame=" << frame;
4367  khtml::ChildFrame *child = new khtml::ChildFrame;
4368  FrameIt it = d->m_objects.insert(d->m_objects.end(), child);
4369  (*it)->m_partContainerElement = frame;
4370  (*it)->m_type = khtml::ChildFrame::Object;
4371  (*it)->m_params = params;
4372 
4374  args.setMimeType(serviceType);
4375  if (!requestObject(*it, completeURL(url), args) && !(*it)->m_run) {
4376  (*it)->m_bCompleted = true;
4377  return false;
4378  }
4379  return true;
4380 }
4381 
4382 bool KHTMLPart::requestObject(khtml::ChildFrame *child, const QUrl &url, const KParts::OpenUrlArguments &_args,
4383  const KParts::BrowserArguments &browserArgs)
4384 {
4385  // we always permit javascript: URLs here since they're basically just
4386  // empty pages (and checkLinkSecurity/KAuthorized doesn't know what to do with them)
4387  if (!d->isJavaScriptURL(url.toString()) && !checkLinkSecurity(url)) {
4388  // qCDebug(KHTML_LOG) << this << "checkLinkSecurity refused";
4389  return false;
4390  }
4391 
4392  if (d->m_bClearing) {
4393  return false;
4394  }
4395 
4396  if (child->m_bPreloaded) {
4397  if (child->m_partContainerElement && child->m_part) {
4398  child->m_partContainerElement.data()->setWidget(child->m_part.data()->widget());
4399  }
4400 
4401  child->m_bPreloaded = false;
4402  return true;
4403  }
4404 
4405  //qCDebug(KHTML_LOG) << "child=" << child << "child->m_part=" << child->m_part;
4406 
4407  KParts::OpenUrlArguments args(_args);
4408 
4409  if (child->m_run) {
4410  // qCDebug(KHTML_LOG) << "navigating ChildFrame while mimetype resolution was in progress...";
4411  child->m_run.data()->abort();
4412  }
4413 
4414  // ### Dubious -- the whole dir/ vs. img thing
4415  if (child->m_part && !args.reload() && areUrlsForSamePage(child->m_part.data()->url(), url)) {
4416  args.setMimeType(child->m_serviceType);
4417  }
4418 
4419  child->m_browserArgs = browserArgs;
4420  child->m_args = args;
4421 
4422  // reload/soft-reload arguments are always inherited from parent
4423  child->m_args.setReload(arguments().reload());
4424  child->m_browserArgs.softReload = d->m_extension->browserArguments().softReload;
4425 
4426  child->m_serviceName.clear();
4427  if (!d->m_referrer.isEmpty() && !child->m_args.metaData().contains("referrer")) {
4428  child->m_args.metaData()["referrer"] = d->m_referrer;
4429  }
4430 
4431  child->m_args.metaData().insert("PropagateHttpHeader", "true");
4432  child->m_args.metaData().insert("ssl_parent_ip", d->m_ssl_parent_ip);
4433  child->m_args.metaData().insert("ssl_parent_cert", d->m_ssl_parent_cert);
4434  child->m_args.metaData().insert("main_frame_request",
4435  parentPart() == nullptr ? "TRUE" : "FALSE");
4436  child->m_args.metaData().insert("ssl_was_in_use",
4437  d->m_ssl_in_use ? "TRUE" : "FALSE");
4438  child->m_args.metaData().insert("ssl_activate_warnings", "TRUE");
4439  child->m_args.metaData().insert("cross-domain", toplevelURL().toString());
4440 
4441  // We know the frame will be text/html if the HTML says <frame src=""> or <frame src="about:blank">,
4442  // no need to KHTMLRun to figure out the mimetype"
4443  // ### What if we're inside an XML document?
4444  if ((url.isEmpty() || url.toString() == "about:blank" || url.scheme() == "javascript") && args.mimeType().isEmpty()) {
4445  args.setMimeType(QLatin1String("text/html"));
4446  }
4447 
4448  if (args.mimeType().isEmpty()) {
4449  // qCDebug(KHTML_LOG) << "Running new KHTMLRun for" << this << "and child=" << child;
4450  child->m_run = new KHTMLRun(this, child, url, child->m_args, child->m_browserArgs, true);
4451  d->m_bComplete = false; // ensures we stop it in checkCompleted...
4452  return false;
4453  } else {
4454  return processObjectRequest(child, url, args.mimeType());
4455  }
4456 }
4457 
4458 void KHTMLPart::childLoadFailure(khtml::ChildFrame *child)
4459 {
4460  child->m_bCompleted = true;
4461  if (child->m_partContainerElement) {
4462  child->m_partContainerElement.data()->partLoadingErrorNotify();
4463  }
4464 
4465  checkCompleted();
4466 }
4467 
4468 bool KHTMLPart::processObjectRequest(khtml::ChildFrame *child, const QUrl &_url, const QString &mimetype)
4469 {
4470  // qCDebug(KHTML_LOG) << "trying to create part for" << mimetype << _url;
4471 
4472  // IMPORTANT: create a copy of the url here, because it is just a reference, which was likely to be given
4473  // by an emitting frame part (emit openUrlRequest( blahurl, ... ) . A few lines below we delete the part
4474  // though -> the reference becomes invalid -> crash is likely
4475  QUrl url(_url);
4476 
4477  // khtmlrun called us with empty url + mimetype to indicate a loading error,
4478  // we obviosuly failed; but we can return true here since we don't want it
4479  // doing anything more, while childLoadFailure is enough to notify our kid.
4480  if (d->m_onlyLocalReferences || (url.isEmpty() && mimetype.isEmpty())) {
4481  childLoadFailure(child);
4482  return true;
4483  }
4484 
4485  // we also want to ignore any spurious requests due to closing when parser is being cleared. These should be
4486  // ignored entirely --- the tail end of ::clear will clean things up.
4487  if (d->m_bClearing) {
4488  return false;
4489  }
4490 
4491  if (child->m_bNotify) {
4492  child->m_bNotify = false;
4493  if (!child->m_browserArgs.lockHistory()) {
4494  emit d->m_extension->openUrlNotify();
4495  }
4496  }
4497 
4498  QMimeDatabase db;
4499 
4500  // Now, depending on mimetype and current state of the world, we may have
4501  // to create a new part or ask the user to save things, etc.
4502  //
4503  // We need a new part if there isn't one at all (doh) or the one that's there
4504  // is not for the mimetype we're loading.
4505  //
4506  // For these new types, we may have to ask the user to save it or not
4507  // (we don't if it's navigating the same type).
4508  // Further, we will want to ask if content-disposition suggests we ask for
4509  // saving, even if we're re-navigating.
4510  if (!child->m_part || child->m_serviceType != mimetype ||
4511  (child->m_run && child->m_run.data()->serverSuggestsSave())) {
4512  // We often get here if we didn't know the mimetype in advance, and had to rely
4513  // on KRun to figure it out. In this case, we let the element check if it wants to
4514  // handle this mimetype itself, for e.g. objects containing images.
4515  if (child->m_partContainerElement &&
4516  child->m_partContainerElement.data()->mimetypeHandledInternally(mimetype)) {
4517  child->m_bCompleted = true;
4518  checkCompleted();
4519  return true;
4520  }
4521 
4522  // Before attempting to load a part, check if the user wants that.
4523  // Many don't like getting ZIP files embedded.
4524  // However we don't want to ask for flash and other plugin things.
4525  //
4526  // Note: this is fine for frames, since we will merely effectively ignore
4527  // the navigation if this happens
4528  if (child->m_type != khtml::ChildFrame::Object && child->m_type != khtml::ChildFrame::IFrame) {
4529  QString suggestedFileName;
4530  int disposition = 0;
4531  if (KHTMLRun *run = child->m_run.data()) {
4532  suggestedFileName = run->suggestedFileName();
4533  disposition = run->serverSuggestsSave() ?
4534  KParts::BrowserRun::AttachmentDisposition :
4535  KParts::BrowserRun::InlineDisposition;
4536  }
4537 
4538  KParts::BrowserOpenOrSaveQuestion dlg(widget(), url, mimetype);
4539  dlg.setSuggestedFileName(suggestedFileName);
4540  const KParts::BrowserOpenOrSaveQuestion::Result res = dlg.askEmbedOrSave(disposition);
4541 
4542  switch (res) {
4543  case KParts::BrowserOpenOrSaveQuestion::Save:
4544  KHTMLPopupGUIClient::saveURL(widget(), i18n("Save As"), url, child->m_args.metaData(), QString(), 0, suggestedFileName);
4545  // fall-through
4546  case KParts::BrowserOpenOrSaveQuestion::Cancel:
4547  child->m_bCompleted = true;
4548  checkCompleted();
4549  return true; // done
4550  default: // Embed
4551  break;
4552  }
4553  }
4554 
4555  // Now, for frames and iframes, we always create a KHTMLPart anyway,
4556  // doing it in advance when registering the frame. So we want the
4557  // actual creation only for objects here.
4558  if (child->m_type == khtml::ChildFrame::Object) {
4559  QMimeType mime = db.mimeTypeForName(mimetype);
4560  if (mime.isValid()) {
4561  // Even for objects, however, we want to force a KHTMLPart for
4562  // html & xml, even if the normally preferred part is another one,
4563  // so that we can script the target natively via contentDocument method.
4564  if (mime.inherits("text/html")
4565  || mime.inherits("application/xml")) { // this includes xhtml and svg
4566  child->m_serviceName = "khtml";
4567  } else {
4568  if (!pluginsEnabled()) {
4569  childLoadFailure(child);
4570  return false;
4571  }
4572  }
4573  }
4574 
4575  QStringList dummy; // the list of servicetypes handled by the part is now unused.
4576  KParts::ReadOnlyPart *part = createPart(d->m_view->viewport(), this, mimetype, child->m_serviceName, dummy, child->m_params);
4577 
4578  if (!part) {
4579  childLoadFailure(child);
4580  return false;
4581  }
4582 
4583  connectToChildPart(child, part, mimetype);
4584  }
4585  }
4586 
4587  checkEmitLoadEvent();
4588 
4589  // Some JS code in the load event may have destroyed the part
4590  // In that case, abort
4591  if (!child->m_part) {
4592  return false;
4593  }
4594 
4595  if (child->m_bPreloaded) {
4596  if (child->m_partContainerElement && child->m_part) {
4597  child->m_partContainerElement.data()->setWidget(child->m_part.data()->widget());
4598  }
4599 
4600  child->m_bPreloaded = false;
4601  return true;
4602  }
4603 
4604  // reload/soft-reload arguments are always inherited from parent
4605  child->m_args.setReload(arguments().reload());
4606  child->m_browserArgs.softReload = d->m_extension->browserArguments().softReload;
4607 
4608  // make sure the part has a way to find out about the mimetype.
4609  // we actually set it in child->m_args in requestObject already,
4610  // but it's useless if we had to use a KHTMLRun instance, as the
4611  // point the run object is to find out exactly the mimetype.
4612  child->m_args.setMimeType(mimetype);
4613  child->m_part.data()->setArguments(child->m_args);
4614 
4615  // if not a frame set child as completed
4616  // ### dubious.
4617  child->m_bCompleted = child->m_type == khtml::ChildFrame::Object;
4618 
4619  if (child->m_extension) {
4620  child->m_extension.data()->setBrowserArguments(child->m_browserArgs);
4621  }
4622 
4623  return navigateChild(child, url);
4624 }
4625 
4626 bool KHTMLPart::navigateLocalProtocol(khtml::ChildFrame * /*child*/, KParts::ReadOnlyPart *inPart,
4627  const QUrl &url)
4628 {
4629  if (!qobject_cast<KHTMLPart *>(inPart)) {
4630  return false;
4631  }
4632 
4633  KHTMLPart *p = static_cast<KHTMLPart *>(static_cast<KParts::ReadOnlyPart *>(inPart));
4634 
4635  p->begin();
4636 
4637  // We may have to re-propagate the domain here if we go here due to navigation
4638  d->propagateInitialDomainAndBaseTo(p);
4639 
4640  // Support for javascript: sources
4641  if (d->isJavaScriptURL(url.toString())) {
4642  // See if we want to replace content with javascript: output..
4643  QVariant res = p->executeScript(DOM::Node(),
4644  d->codeForJavaScriptURL(url.toString()));
4645  if (res.type() == QVariant::String && p->d->m_redirectURL.isEmpty()) {
4646  p->begin();
4647  p->setAlwaysHonourDoctype(); // Disable public API compat; it messes with doctype
4648  // We recreated the document, so propagate domain again.
4649  d->propagateInitialDomainAndBaseTo(p);
4650  p->write(res.toString());
4651  p->end();
4652  }
4653  } else {
4654  p->setUrl(url);
4655  // we need a body element. testcase: <iframe id="a"></iframe><script>alert(a.document.body);</script>
4656  p->write("<HTML><TITLE></TITLE><BODY></BODY></HTML>");
4657  }
4658  p->end();
4659  // we don't need to worry about child completion explicitly for KHTMLPart...
4660  // or do we?
4661  return true;
4662 }
4663 
4664 bool KHTMLPart::navigateChild(khtml::ChildFrame *child, const QUrl &url)
4665 {
4666  if (url.scheme() == "javascript" || url.toString() == "about:blank") {
4667  return navigateLocalProtocol(child, child->m_part.data(), url);
4668  } else if (!url.isEmpty()) {
4669  // qCDebug(KHTML_LOG) << "opening" << url << "in frame" << child->m_part;
4670  bool b = child->m_part.data()->openUrl(url);
4671  if (child->m_bCompleted) {
4672  checkCompleted();
4673  }
4674  return b;
4675  } else {
4676  // empty URL -> no need to navigate
4677  child->m_bCompleted = true;
4678  checkCompleted();
4679  return true;
4680  }
4681 }
4682 
4683 void KHTMLPart::connectToChildPart(khtml::ChildFrame *child, KParts::ReadOnlyPart *part,
4684  const QString &mimetype)
4685 {
4686  // qCDebug(KHTML_LOG) << "we:" << this << "kid:" << child << part << mimetype;
4687 
4688  part->setObjectName(child->m_name);
4689 
4690  // Cleanup any previous part for this childframe and its connections
4691  if (KParts::ReadOnlyPart *p = child->m_part.data()) {
4692  if (!qobject_cast<KHTMLPart *>(p) && child->m_jscript) {
4693  child->m_jscript->clear();
4694  }
4695  partManager()->removePart(p);
4696  delete p;
4697  child->m_scriptable.clear();
4698  }
4699 
4700  child->m_part = part;
4701 
4702  child->m_serviceType = mimetype;
4703  if (child->m_partContainerElement && part->widget()) {
4704  child->m_partContainerElement.data()->setWidget(part->widget());
4705  }
4706 
4707  if (child->m_type != khtml::ChildFrame::Object) {
4708  partManager()->addPart(part, false);
4709  }
4710 // else
4711 // qCDebug(KHTML_LOG) << "AH! NO FRAME!!!!!";
4712 
4713  if (qobject_cast<KHTMLPart *>(part)) {
4714  static_cast<KHTMLPart *>(part)->d->m_frame = child;
4715  } else if (child->m_partContainerElement) {
4716  // See if this can be scripted..
4718  if (!scriptExt) {
4719  // Try to fall back to LiveConnectExtension compat
4720  KParts::LiveConnectExtension *lc = KParts::LiveConnectExtension::childObject(part);
4721  if (lc) {
4723  }
4724  }
4725 
4726  if (scriptExt) {
4727  scriptExt->setHost(d->m_scriptableExtension);
4728  }
4729  child->m_scriptable = scriptExt;
4730  }
4732  if (sb) {
4733  sb->setStatusBar(d->m_statusBarExtension->statusBar());
4734  }
4735 
4736  connect(part, SIGNAL(started(KIO::Job*)),
4737  this, SLOT(slotChildStarted(KIO::Job*)));
4738  connect(part, SIGNAL(completed()),
4739  this, SLOT(slotChildCompleted()));
4740  connect(part, SIGNAL(completed(bool)),
4741  this, SLOT(slotChildCompleted(bool)));
4742  connect(part, SIGNAL(setStatusBarText(QString)),
4743  this, SIGNAL(setStatusBarText(QString)));
4744  if (part->inherits("KHTMLPart")) {
4745  connect(this, SIGNAL(completed()),
4746  part, SLOT(slotParentCompleted()));
4747  connect(this, SIGNAL(completed(bool)),
4748  part, SLOT(slotParentCompleted()));
4749  // As soon as the child's document is created, we need to set its domain
4750  // (but we do so only once, so it can't be simply done in the child)
4751  connect(part, SIGNAL(docCreated()),
4752  this, SLOT(slotChildDocCreated()));
4753  }
4754 
4755  child->m_extension = KParts::BrowserExtension::childObject(part);
4756 
4757  if (KParts::BrowserExtension *kidBrowserExt = child->m_extension.data()) {
4758  connect(kidBrowserExt, SIGNAL(openUrlNotify()),
4759  d->m_extension, SIGNAL(openUrlNotify()));
4760 
4761  connect(kidBrowserExt, SIGNAL(openUrlRequestDelayed(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)),
4762  this, SLOT(slotChildURLRequest(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)));
4763 
4766 
4771 
4772  connect(kidBrowserExt, SIGNAL(infoMessage(QString)),
4773  d->m_extension, SIGNAL(infoMessage(QString)));
4774 
4775  connect(kidBrowserExt, SIGNAL(requestFocus(KParts::ReadOnlyPart*)),
4776  this, SLOT(slotRequestFocus(KParts::ReadOnlyPart*)));
4777 
4778  kidBrowserExt->setBrowserInterface(d->m_extension->browserInterface());
4779  }
4780 }
4781 
4783  QObject *parent, const QString &mimetype,
4784  QString &serviceName, QStringList &serviceTypes,
4785  const QStringList &params)
4786 {
4787  QString constr;
4788  if (!serviceName.isEmpty()) {
4789  constr.append(QString::fromLatin1("DesktopEntryName == '%1'").arg(serviceName));
4790  }
4791 
4792  KService::List offers = KMimeTypeTrader::self()->query(mimetype, "KParts/ReadOnlyPart", constr);
4793 
4794  if (offers.isEmpty()) {
4795  int pos = mimetype.indexOf("-plugin");
4796  if (pos < 0) {
4797  return nullptr;
4798  }
4799  QString stripped_mime = mimetype.left(pos);
4800  offers = KMimeTypeTrader::self()->query(stripped_mime, "KParts/ReadOnlyPart", constr);
4801  if (offers.isEmpty()) {
4802  return nullptr;
4803  }
4804  }
4805 
4807  const KService::List::ConstIterator itEnd = offers.constEnd();
4808  for (; it != itEnd; ++it) {
4809  KService::Ptr service = (*it);
4810 
4811  KPluginLoader loader(*service);
4812  KPluginFactory *const factory = loader.factory();
4813  if (factory) {
4814  // Turn params into a QVariantList as expected by KPluginFactory
4815  QVariantList variantlist;
4816  Q_FOREACH (const QString &str, params) {
4817  variantlist << QVariant(str);
4818  }
4819 
4820  if (service->serviceTypes().contains("Browser/View")) {
4821  variantlist << QString("Browser/View");
4822  }
4823 
4824  KParts::ReadOnlyPart *part = factory->create<KParts::ReadOnlyPart>(parentWidget, parent, QString(), variantlist);
4825  if (part) {
4826  serviceTypes = service->serviceTypes();
4827  serviceName = service->name();
4828  return part;
4829  }
4830  } else {
4831  // TODO KMessageBox::error and i18n, like in KonqFactory::createView?
4832  qCWarning(KHTML_LOG) << QString("There was an error loading the module %1.\nThe diagnostics is:\n%2")
4833  .arg(service->name()).arg(loader.errorString());
4834  }
4835  }
4836  return nullptr;
4837 }
4838 
4840 {
4841  if (!d->m_manager && d->m_view) {
4842  d->m_manager = new KParts::PartManager(d->m_view->topLevelWidget(), this);
4843  d->m_manager->setObjectName("khtml part manager");
4844  d->m_manager->setAllowNestedParts(true);
4845  connect(d->m_manager, SIGNAL(activePartChanged(KParts::Part*)),
4846  this, SLOT(slotActiveFrameChanged(KParts::Part*)));
4847  connect(d->m_manager, SIGNAL(partRemoved(KParts::Part*)),
4848  this, SLOT(slotPartRemoved(KParts::Part*)));
4849  }
4850 
4851  return d->m_manager;
4852 }
4853 
4854 void KHTMLPart::submitFormAgain()
4855 {
4856  disconnect(this, SIGNAL(completed()), this, SLOT(submitFormAgain()));
4857  if (d->m_doc && !d->m_doc->parsing() && d->m_submitForm) {
4858  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);
4859  }
4860 
4861  delete d->m_submitForm;
4862  d->m_submitForm = nullptr;
4863 }
4864 
4865 void KHTMLPart::submitFormProxy(const char *action, const QString &url, const QByteArray &formData, const QString &_target, const QString &contentType, const QString &boundary)
4866 {
4867  submitForm(action, url, formData, _target, contentType, boundary);
4868 }
4869 
4870 void KHTMLPart::submitForm(const char *action, const QString &url, const QByteArray &formData, const QString &_target, const QString &contentType, const QString &boundary)
4871 {
4872  // qCDebug(KHTML_LOG) << this << "target=" << _target << "url=" << url;
4873  if (d->m_formNotification == KHTMLPart::Only) {
4874  emit formSubmitNotification(action, url, formData, _target, contentType, boundary);
4875  return;
4876  } else if (d->m_formNotification == KHTMLPart::Before) {
4877  emit formSubmitNotification(action, url, formData, _target, contentType, boundary);
4878  }
4879 
4880  QUrl u = completeURL(url);
4881 
4882  if (!u.isValid()) {
4883  // ### ERROR HANDLING!
4884  return;
4885  }
4886 
4887  // Form security checks
4888  //
4889  /*
4890  * If these form security checks are still in this place in a month or two
4891  * I'm going to simply delete them.
4892  */
4893 
4894  /* This is separate for a reason. It has to be _before_ all script, etc,
4895  * AND I don't want to break anything that uses checkLinkSecurity() in
4896  * other places.
4897  */
4898 
4899  if (!d->m_submitForm) {
4900  if (u.scheme() != "https" && u.scheme() != "mailto") {
4901  if (d->m_ssl_in_use) { // Going from SSL -> nonSSL
4902  int rc = KMessageBox::warningContinueCancel(nullptr, i18n("Warning: This is a secure form but it is attempting to send your data back unencrypted."
4903  "\nA third party may be able to intercept and view this information."
4904  "\nAre you sure you wish to continue?"),
4905  i18n("Network Transmission"), KGuiItem(i18n("&Send Unencrypted")));
4906  if (rc == KMessageBox::Cancel) {
4907  return;
4908  }
4909  } else { // Going from nonSSL -> nonSSL
4910  KSSLSettings kss(true);
4911  if (kss.warnOnUnencrypted()) {
4912  int rc = KMessageBox::warningContinueCancel(nullptr,
4913  i18n("Warning: Your data is about to be transmitted across the network unencrypted."
4914  "\nAre you sure you wish to continue?"),
4915  i18n("Network Transmission"),
4916  KGuiItem(i18n("&Send Unencrypted")),
4918  "WarnOnUnencryptedForm");
4919  // Move this setting into KSSL instead
4920  QString grpNotifMsgs = QLatin1String("Notification Messages");
4921  KConfigGroup cg(KSharedConfig::openConfig(), grpNotifMsgs);
4922 
4923  if (!cg.readEntry("WarnOnUnencryptedForm", true)) {
4924  cg.deleteEntry("WarnOnUnencryptedForm");
4925  cg.sync();
4926  kss.setWarnOnUnencrypted(false);
4927  kss.save();
4928  }
4929  if (rc == KMessageBox::Cancel) {
4930  return;
4931  }
4932  }
4933  }
4934  }
4935 
4936  if (u.scheme() == "mailto") {
4937  int rc = KMessageBox::warningContinueCancel(nullptr,
4938  i18n("This site is attempting to submit form data via email.\n"
4939  "Do you want to continue?"),
4940  i18n("Network Transmission"),
4941  KGuiItem(i18n("&Send Email")),
4943  "WarnTriedEmailSubmit");
4944 
4945  if (rc == KMessageBox::Cancel) {
4946  return;
4947  }
4948  }
4949  }
4950 
4951  // End form security checks
4952  //
4953 
4954  QString urlstring = u.toString();
4955 
4956  if (d->isJavaScriptURL(urlstring)) {
4957  crossFrameExecuteScript(_target, d->codeForJavaScriptURL(urlstring));
4958  return;
4959  }
4960 
4961  if (!checkLinkSecurity(u,
4962  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>"),
4963  i18n("Submit"))) {
4964  return;
4965  }
4966 
4967  // OK. We're actually going to submit stuff. Clear any redirections,
4968  // we should win over them
4969  d->clearRedirection();
4970 
4972 
4973  if (!d->m_referrer.isEmpty()) {
4974  args.metaData()["referrer"] = d->m_referrer;
4975  }
4976 
4977  args.metaData().insert("PropagateHttpHeader", "true");
4978  args.metaData().insert("ssl_parent_ip", d->m_ssl_parent_ip);
4979  args.metaData().insert("ssl_parent_cert", d->m_ssl_parent_cert);
4980  args.metaData().insert("main_frame_request",
4981  parentPart() == nullptr ? "TRUE" : "FALSE");
4982  args.metaData().insert("ssl_was_in_use", d->m_ssl_in_use ? "TRUE" : "FALSE");
4983  args.metaData().insert("ssl_activate_warnings", "TRUE");
4984 //WABA: When we post a form we should treat it as the main url
4985 //the request should never be considered cross-domain
4986 //args.metaData().insert("cross-domain", toplevelURL().toString());
4987  KParts::BrowserArguments browserArgs;
4988  browserArgs.frameName = _target.isEmpty() ? d->m_doc->baseTarget() : _target;
4989 
4990  // Handle mailto: forms
4991  if (u.scheme() == "mailto") {
4992  // 1) Check for attach= and strip it
4993  QString q = u.query().mid(1);
4994  QStringList nvps = q.split("&");
4995  bool triedToAttach = false;
4996 
4997  QStringList::Iterator nvp = nvps.begin();
4998  const QStringList::Iterator nvpEnd = nvps.end();
4999 
5000 // cannot be a for loop as if something is removed we don't want to do ++nvp, as
5001 // remove returns an iterator pointing to the next item
5002 
5003  while (nvp != nvpEnd) {
5004  const QStringList pair = (*nvp).split("=");
5005  if (pair.count() >= 2) {
5006  if (pair.first().toLower() == "attach") {
5007  nvp = nvps.erase(nvp);
5008  triedToAttach = true;
5009  } else {
5010  ++nvp;
5011  }
5012  } else {
5013  ++nvp;
5014  }
5015  }
5016 
5017  if (triedToAttach) {
5018  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");
5019  }
5020 
5021  // 2) Append body=
5022  QString bodyEnc;
5023  if (contentType.toLower() == "multipart/form-data") {
5024  // FIXME: is this correct? I suspect not
5026  formData.size())));
5027  } else if (contentType.toLower() == "text/plain") {
5028  // Convention seems to be to decode, and s/&/\n/
5029  QString tmpbody = QString::fromLatin1(formData.data(),
5030  formData.size());
5031  tmpbody.replace(QRegExp("[&]"), "\n");
5032  tmpbody.replace(QRegExp("[+]"), " ");
5033  tmpbody = QUrl::fromPercentEncoding(tmpbody.toLatin1()); // Decode the rest of it
5034  bodyEnc = QLatin1String(QUrl::toPercentEncoding(tmpbody)); // Recode for the URL
5035  } else {
5037  formData.size())));
5038  }
5039 
5040  nvps.append(QString("body=%1").arg(bodyEnc));
5041  q = nvps.join("&");
5042  u.setQuery(q);
5043  }
5044 
5045  if (strcmp(action, "get") == 0) {
5046  if (u.scheme() != "mailto") {
5047  u.setQuery(QString::fromLatin1(formData.data(), formData.size()));
5048  }
5049  browserArgs.