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