File indexing completed on 2024-04-21 04:57:51
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2008 David Faure <faure@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <konqmainwindowfactory.h> 0008 #include "../src/konqsettingsxt.h" 0009 #include <KLocalizedString> 0010 0011 #include <konqmainwindow.h> 0012 #include <konqviewmanager.h> 0013 #include <konqview.h> 0014 #include <konqsessionmanager.h> 0015 0016 #include <webenginepart.h> 0017 #include <webengineview.h> 0018 #include <webenginepage.h> 0019 #include <QWebEngineProfile> 0020 #include <QWebEngineSettings> 0021 0022 #include <KSharedConfig> 0023 #include <ktoolbar.h> 0024 #include <ksycoca.h> 0025 0026 #include <QTemporaryFile> 0027 #include <QScrollArea> 0028 #include <QProcess> 0029 #include <QTest> 0030 #include <QSignalSpy> 0031 #include <QObject> 0032 #include <QStandardPaths> 0033 0034 class KonqHtmlTest : public QObject 0035 { 0036 Q_OBJECT 0037 private Q_SLOTS: 0038 void initTestCase() 0039 { 0040 KLocalizedString::setApplicationDomain("konqhtmltest"); 0041 QStandardPaths::setTestModeEnabled(true); 0042 0043 KonqSessionManager::self()->disableAutosave(); 0044 //qRegisterMetaType<KonqView *>("KonqView*"); 0045 0046 // Ensure the tests use webenginepart, not KHTML or kwebkitpart 0047 // This code is inspired by settings/konqhtml/generalopts.cpp 0048 bool needsUpdate = false; 0049 KSharedConfig::Ptr profile = KSharedConfig::openConfig(QStringLiteral("mimeapps.list"), KConfig::NoGlobals, QStandardPaths::ApplicationsLocation); 0050 KConfigGroup addedServices(profile, "Added KDE Service Associations"); 0051 for (const QString &mimeType: {"text/html", "application/xhtml+xml", "application/xml"}) { 0052 QStringList services = addedServices.readXdgListEntry(mimeType); 0053 const QString wanted = QStringLiteral("webenginepart.desktop"); 0054 if (services.isEmpty() || services.at(0) != wanted) { 0055 services.removeAll(wanted); 0056 services.prepend(wanted); // make it the preferred one 0057 addedServices.writeXdgListEntry(mimeType, services); 0058 needsUpdate = true; 0059 } 0060 } 0061 if (needsUpdate) { 0062 profile->sync(); 0063 // kbuildsycoca is the one reading mimeapps.list, so we need to run it now 0064 QProcess::execute(QStandardPaths::findExecutable(KBUILDSYCOCA_EXENAME), {}); 0065 } 0066 } 0067 void cleanupTestCase() 0068 { 0069 // in case some test broke, don't assert in khtmlglobal... 0070 deleteAllMainWindows(); 0071 } 0072 void loadSimpleHtml() 0073 { 0074 KonqMainWindow mainWindow; 0075 // we specify the mimetype so that we don't have to wait for a KonqRun 0076 mainWindow.openUrl(nullptr, QUrl(QStringLiteral("data:text/html, <p>Hello World</p>")), QStringLiteral("text/html")); 0077 KonqView *view = mainWindow.currentView(); 0078 QVERIFY(view); 0079 QVERIFY(view->part()); 0080 QSignalSpy spyCompleted(view, &KonqView::viewCompleted); 0081 QVERIFY(spyCompleted.wait(20000)); 0082 QCOMPARE(view->serviceType(), QString("text/html")); 0083 WebEnginePart* part = qobject_cast<WebEnginePart *>(view->part()); 0084 QVERIFY(part); 0085 } 0086 0087 void loadDirectory() // #164495, konqueror gets in a loop when setting a directory as homepage 0088 { 0089 KonqMainWindow mainWindow; 0090 const QUrl url = QUrl::fromLocalFile(QDir::homePath()); 0091 mainWindow.openUrl(nullptr, url, QStringLiteral("text/html")); 0092 KonqView *view = mainWindow.currentView(); 0093 qDebug() << "Waiting for first completed signal"; 0094 QSignalSpy spyCompleted(view, &KonqView::viewCompleted); 0095 QVERIFY(spyCompleted.wait(20000)); // error calls openUrlRequest 0096 if (view->aborted()) { 0097 qDebug() << "Waiting for second completed signal"; 0098 QVERIFY(spyCompleted.wait(20000)); // which then opens the right part 0099 QCOMPARE(view->serviceType(), QString("inode/directory")); 0100 } else { 0101 // WebEngine can actually list directories, no error. 0102 // To test this: konqueror --mimetype text/html $HOME 0103 QCOMPARE(view->url().adjusted(QUrl::StripTrailingSlash), url); 0104 } 0105 } 0106 0107 void rightClickClose() // #149736 0108 { 0109 QPointer<KonqMainWindow> mainWindow = new KonqMainWindow; 0110 // we specify the mimetype so that we don't have to wait for a KonqRun 0111 mainWindow->openUrl(nullptr, QUrl( 0112 "data:text/html, <script type=\"text/javascript\">" 0113 "function closeMe() { window.close(); } " 0114 "document.onmousedown = closeMe; " 0115 "</script>"), QStringLiteral("text/html")); 0116 QPointer<KonqView> view = mainWindow->currentView(); 0117 QVERIFY(view); 0118 QSignalSpy spyCompleted(view, SIGNAL(viewCompleted(KonqView*))); 0119 QVERIFY(spyCompleted.wait(20000)); 0120 QWidget *widget = partWidget(view); 0121 qDebug() << "Clicking on" << widget; 0122 QTest::mousePress(widget, Qt::RightButton); 0123 QTRY_VERIFY(!view); // deleted 0124 QTRY_VERIFY(!mainWindow); // the whole window gets deleted, in fact 0125 } 0126 0127 void windowOpen() 0128 { 0129 // Simple test for window.open in a onmousedown handler. 0130 0131 // Want a window, not a tab (historical test) 0132 KonqSettings::setMmbOpensTab(false); 0133 KonqSettings::setAlwaysHavePreloaded(false); 0134 0135 // We have to use the same protocol for both the orig and dest urls. 0136 // KAuthorized would forbid a data: URL to redirect to a file: URL for instance. 0137 QTemporaryFile tempFile; 0138 QVERIFY(tempFile.open()); 0139 tempFile.write("<title>Popup</title><script>document.title=\"Opener=\" + window.opener;</script>"); 0140 0141 QTemporaryFile origTempFile; 0142 QVERIFY(origTempFile.open()); 0143 origTempFile.write( 0144 "<html><script>" 0145 "function openWindow() { window.open('" + QUrl::fromLocalFile(tempFile.fileName()).url().toUtf8() + "'); } " 0146 "document.onmousedown = openWindow; " 0147 "</script></html>" 0148 ); 0149 tempFile.close(); 0150 const QString origFile = origTempFile.fileName(); 0151 origTempFile.close(); 0152 0153 KonqMainWindow *mainWindow = KonqMainWindowFactory::createNewWindow(QUrl::fromLocalFile(origFile)); 0154 QCOMPARE(KMainWindow::memberList().count(), 1); 0155 KonqView *view = mainWindow->currentView(); 0156 QVERIFY(view); 0157 QSignalSpy spyCompleted(view, SIGNAL(viewCompleted(KonqView*))); 0158 QVERIFY(spyCompleted.wait(20000)); 0159 qApp->processEvents(); 0160 WebEnginePart *htmlPart = qobject_cast<WebEnginePart *>(view->part()); 0161 htmlPart->view()->page()->profile()->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); 0162 htmlPart->view()->page()->profile()->settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, true); 0163 htmlPart->view()->page()->profile()->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls , true); 0164 htmlPart->view()->page()->profile()->settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); 0165 htmlPart->view()->page()->profile()->settings()->setAttribute(QWebEngineSettings::AllowWindowActivationFromJavaScript, true); 0166 QWidget *widget = partWidget(view); 0167 QVERIFY(widget); 0168 qDebug() << "Clicking on the khtmlview"; 0169 QTest::mousePress(widget, Qt::LeftButton); 0170 qApp->processEvents(); // openurlrequestdelayed 0171 qApp->processEvents(); // browserrun 0172 hideAllMainWindows(); // TODO: why does it appear nonetheless? hiding too early? hiding too late 0173 // Did it open a window? 0174 QTRY_COMPARE(KMainWindow::memberList().count(), 2); 0175 KonqMainWindow *newWindow = qobject_cast<KonqMainWindow *>(KMainWindow::memberList().last()); 0176 QVERIFY(newWindow); 0177 QVERIFY(newWindow != mainWindow); 0178 compareToolbarSettings(mainWindow, newWindow); 0179 // Does the window contain exactly one view? 0180 QCOMPARE(newWindow->viewCount(), 1); 0181 KonqFrame *frame = newWindow->currentView()->frame(); 0182 QVERIFY(frame); 0183 QVERIFY(!frame->childView()->isLoading()); 0184 WebEnginePart *part = qobject_cast<WebEnginePart *>(frame->part()); 0185 QVERIFY(part); 0186 QTRY_VERIFY(!part->view()->url().isEmpty() && part->view()->url().scheme() != QStringLiteral("konq")); // hack to wait for webengine to load the page 0187 QTRY_COMPARE(part->view()->title(), QString("Opener=[object Window]")); 0188 deleteAllMainWindows(); 0189 } 0190 0191 void testJSError() 0192 { 0193 // JS errors appear in a statusbar label, and deleting the frame first 0194 // would lead to double deletion (#228255) 0195 QPointer<KonqMainWindow> mainWindow = new KonqMainWindow; 0196 // we specify the mimetype so that we don't have to wait for a KonqRun 0197 mainWindow->openUrl(nullptr, QUrl(QStringLiteral("data:text/html, <script>window.foo=bar</script><p>Hello World</p>")), QStringLiteral("text/html")); 0198 KonqView *view = mainWindow->currentView(); 0199 QVERIFY(view); 0200 QVERIFY(view->part()); 0201 QSignalSpy spyCompleted(view, &KonqView::viewCompleted); 0202 QVERIFY(spyCompleted.wait(20000)); 0203 QCOMPARE(view->serviceType(), QString("text/html")); 0204 delete view->part(); 0205 QTRY_VERIFY(!mainWindow); // the window gets deleted 0206 } 0207 0208 private: 0209 // Return the main widget for the given KonqView; used for clicking onto it 0210 static QWidget *partWidget(KonqView *view) 0211 { 0212 QWidget *widget = view->part()->widget(); 0213 WebEnginePart *htmlPart = qobject_cast<WebEnginePart *>(view->part()); 0214 if (htmlPart) { 0215 widget = htmlPart->view(); // khtmlview != widget() nowadays, due to find bar 0216 } 0217 if (QScrollArea *scrollArea = qobject_cast<QScrollArea *>(widget)) { 0218 widget = scrollArea->widget(); 0219 } 0220 if (widget && widget->focusProxy()) { // for WebEngine's RenderWidgetHostViewQtDelegateWidget 0221 return widget->focusProxy(); 0222 } 0223 return widget; 0224 } 0225 0226 // Delete all KonqMainWindows 0227 static void deleteAllMainWindows() 0228 { 0229 const QList<KMainWindow *> windows = KMainWindow::memberList(); 0230 qDeleteAll(windows); 0231 } 0232 0233 void compareToolbarSettings(KMainWindow *mainWindow, KMainWindow *newWindow) 0234 { 0235 QVERIFY(mainWindow != newWindow); 0236 KToolBar *firstToolBar = mainWindow->toolBars().first(); 0237 QVERIFY(firstToolBar); 0238 KToolBar *newFirstToolBar = newWindow->toolBars().first(); 0239 QVERIFY(newFirstToolBar); 0240 QCOMPARE(firstToolBar->toolButtonStyle(), newFirstToolBar->toolButtonStyle()); 0241 } 0242 0243 static void hideAllMainWindows() 0244 { 0245 const QList<KMainWindow *> windows = KMainWindow::memberList(); 0246 qDebug() << "hiding" << windows.count() << "windows"; 0247 for (KMainWindow *window: windows) { 0248 window->hide(); 0249 } 0250 } 0251 }; 0252 0253 QTEST_MAIN(KonqHtmlTest) 0254 0255 #include "konqhtmltest.moc"