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"