]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blobdiff - src/widgets/TabWidget.cpp
Block all CSP requests. https://redmine.stoutner.com/issues/1193
[PrivacyBrowserPC.git] / src / widgets / TabWidget.cpp
index e98ac31c38e6c3da93d1dddd114e6d45685e37d4..ce8978b6ca46f11016090175d137f1b22af01667 100644 (file)
@@ -1,20 +1,20 @@
-/*
- * Copyright 2022-2024 Soren Stoutner <soren@stoutner.com>.
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * SPDX-FileCopyrightText: 2022-2025 Soren Stoutner <soren@stoutner.com>
  *
  * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc/>.
  *
- * Privacy Browser PC is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
  *
- * Privacy Browser PC is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
  *
- * You should have received a copy of the GNU General Public License
- * along with Privacy Browser PC.  If not, see <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 // Application headers.
@@ -43,7 +43,6 @@
 #include <QPrintDialog>
 #include <QPrintPreviewDialog>
 #include <QPrinter>
-#include <QSplitter>
 
 // Initialize the public static variables.
 QString TabWidget::webEngineDefaultUserAgent = QLatin1String("");
@@ -215,7 +214,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     QWebEngineCookieStore *webEngineCookieStorePointer = webEngineProfilePointer->cookieStore();
     QWebEngineSettings *webEngineSettingsPointer = webEnginePagePointer->settings();
 
-    // Set the development tools WebEngine.  This must be done here to preserve the bottom half of the window as the initial development tools size.
+    // Set the development tools WebEngine.
     webEnginePagePointer->setDevToolsPage(devToolsWebEngineViewPointer->page());
 
     // Initially hide the development tools WebEngine.
@@ -234,11 +233,11 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
         if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
         {
             // Update the URL line edit.
-            emit updateUrlLineEdit(newUrl);
+            Q_EMIT updateUrlLineEdit(newUrl);
 
             // Update the status of the forward and back buttons.
-            emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
-            emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
+            Q_EMIT updateBackAction(currentWebEngineHistoryPointer->canGoBack());
+            Q_EMIT updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
         }
     });
 
@@ -253,7 +252,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
 
         // Update the window title if this is the current tab.
         if (tabIndex == qTabWidgetPointer->currentIndex())
-            emit updateWindowTitle(title);
+            Q_EMIT updateWindowTitle(title);
     });
 
     // Connect the loading favorite icon movie to the tab icon.
@@ -297,7 +296,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
 
         // Show the progress bar if this is the current tab.
         if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
-            emit showProgressBar(0);
+            Q_EMIT showProgressBar(0);
 
         // Start the loading favorite icon movie.
         loadingFavoriteIconMoviePointer->start();
@@ -311,7 +310,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
 
         // Update the progress bar if this is the current tab.
         if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
-            emit showProgressBar(progress);
+            Q_EMIT showProgressBar(progress);
     });
 
     // Update the progress bar when a load finishes.
@@ -325,7 +324,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
 
         // Hide the progress bar if this is the current tab.
         if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
-            emit hideProgressBar();
+            Q_EMIT hideProgressBar();
 
         // Get the index for this tab.
         int tabIndex = qTabWidgetPointer->indexOf(splitterPointer);
@@ -381,12 +380,12 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
         }
     });
 
-    // Update the zoom actions when changed by CTRL-Scrolling.  This can be modified when <https://redmine.stoutner.com/issues/845> is fixed.
-    connect(webEnginePagePointer, &QWebEnginePage::contentsSizeChanged, [webEnginePagePointer, this] ()
+    // Update the zoom actions when they are changed.
+    connect(webEnginePagePointer, &QWebEnginePage::zoomFactorChanged, [webEnginePagePointer, this] (const qreal newZoomFactor)
     {
         // Only update the zoom actions if this is the current tab.
         if (webEnginePagePointer == currentWebEnginePagePointer)
-            emit updateZoomActions(webEnginePagePointer->zoomFactor());
+            Q_EMIT updateZoomActions(newZoomFactor);
     });
 
     // Display find text results.
@@ -399,13 +398,13 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     connect(webEnginePagePointer, SIGNAL(linkHovered(const QString)), this, SLOT(pageLinkHovered(const QString)));
 
     // Handle file downloads.
-    connect(webEngineProfilePointer, SIGNAL(downloadRequested(QWebEngineDownloadItem *)), this, SLOT(showSaveDialog(QWebEngineDownloadItem *)));
+    connect(webEngineProfilePointer, SIGNAL(downloadRequested(QWebEngineDownloadRequest *)), this, SLOT(showSaveDialog(QWebEngineDownloadRequest *)));
 
     // Set the local storage filter.
     webEngineCookieStorePointer->setCookieFilter([privacyWebEngineViewPointer](const QWebEngineCookieStore::FilterRequest &filterRequest)
     {
         // Block all third party local storage requests, including the sneaky ones that don't register a first party URL.
-        if (filterRequest.thirdParty || (filterRequest.firstPartyUrl == QStringLiteral("")))
+        if (filterRequest.thirdParty || (filterRequest.firstPartyUrl == QUrl(QLatin1String(""))))
         {
             //qDebug().noquote().nospace() << "Third-party request blocked:  " << filterRequest.origin;
 
@@ -452,12 +451,15 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     // Plugins must be enabled for the PDF viewer to work.  <https://doc.qt.io/qt-5/qtwebengine-features.html#pdf-file-viewing>
     webEngineSettingsPointer->setAttribute(QWebEngineSettings::PluginsEnabled, true);
 
+    // Allow JavaScript to paste to (but not copy from) the clipboard, but only with user interaction.
+    webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, true);
+
     // Update the blocked requests action.
-    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::requestBlocked, [this, privacyWebEngineViewPointer] (const int blockedRequests)
+    connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::requestBlocked, [this, privacyWebEngineViewPointer] (const QVector<int> blockedRequestsVector)
     {
         // Update the blocked requests action if the specified privacy WebEngine view is the current privacy WebEngine view.
         if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
-            emit blockedRequestsUpdated(blockedRequests);
+            Q_EMIT blockedRequestsUpdated(blockedRequestsVector);
     });
 
     // Update the cookies action.
@@ -465,7 +467,7 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
     {
         // Update the cookie action if the specified privacy WebEngine view is the current privacy WebEngine view.
         if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
-            emit cookiesChanged(numberOfCookies);
+            Q_EMIT cookiesChanged(numberOfCookies);
     });
 
     // Process cookie changes.
@@ -497,9 +499,10 @@ PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const
 
     // Clear the URL line edit focus so that it populates correctly when opening a new tab from the context menu.
     if (removeUrlLineEditFocus)
-        emit clearUrlLineEditFocus();
+        Q_EMIT clearUrlLineEditFocus();
 
-    if (urlString != nullptr)
+    // Load the URL if it isn't blank.
+    if (urlString != QLatin1String(""))
         privacyWebEngineViewPointer->load(QUrl::fromUserInput(urlString));
 
     // Return the privacy WebEngine view pointer.
@@ -530,7 +533,7 @@ void TabWidget::applyApplicationSettings()
     searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
 
     // Emit the update search engine actions signal.
-    emit updateSearchEngineActions(Settings::searchEngine(), true);
+    Q_EMIT updateSearchEngineActions(Settings::searchEngine(), true);
 }
 
 void TabWidget::applyDomainSettingsAndReload()
@@ -543,8 +546,8 @@ void TabWidget::applyDomainSettingsAndReload()
         // Get the WebEngine view pointer.
         PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild<PrivacyWebEngineView *>();
 
-        // Apply the spatial navigation settings to each page.
-        privacyWebEngineViewPointer->applyDomainSettings(privacyWebEngineViewPointer->url().host(), true);
+        // Apply the domain settings settings to each page.  `false` indicates that history is not being navigated.
+        privacyWebEngineViewPointer->applyDomainSettings(privacyWebEngineViewPointer->url(), false);
     }
 }
 
@@ -554,13 +557,13 @@ void TabWidget::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
     QString searchEngineName = searchEngineActionPointer->text();
 
     // Strip out any `&` characters.
-    searchEngineName.remove('&');
+    searchEngineName.remove(QLatin1Char('&'));
 
     // Store the search engine string.
     searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
 
     // Update the search engine actions.
-    emit updateSearchEngineActions(searchEngineName, false);
+    Q_EMIT updateSearchEngineActions(searchEngineName, false);
 }
 
 void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
@@ -569,13 +572,13 @@ void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
     QString userAgentName = userAgentActionPointer->text();
 
     // Strip out any `&` characters.
-    userAgentName.remove('&');
+    userAgentName.remove(QLatin1Char('&'));
 
     // Apply the user agent.
     currentWebEngineProfilePointer->setHttpUserAgent(userAgentHelperPointer->getUserAgentFromTranslatedName(userAgentName));
 
     // Update the user agent actions.
-    emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), false);
+    Q_EMIT updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), false);
 
     // Reload the website.
     currentPrivacyWebEngineViewPointer->reload();
@@ -702,7 +705,7 @@ void TabWidget::findTextFinished(const QWebEngineFindTextResult &findTextResult)
         currentPrivacyWebEngineViewPointer->findTextResult = findTextResult;
 
         // Update the UI.
-        emit updateFindTextResults(findTextResult);
+        Q_EMIT updateFindTextResults(findTextResult);
     }
 }
 
@@ -715,7 +718,7 @@ void TabWidget::forward() const
 void TabWidget::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) const
 {
     // Make it so.
-    emit fullScreenRequested(fullScreenRequest.toggleOn());
+    Q_EMIT fullScreenRequested(fullScreenRequest.toggleOn());
 
     // Accept the request.
     fullScreenRequest.accept();
@@ -796,18 +799,18 @@ void TabWidget::loadInitialWebsite()
 void TabWidget::loadUrlFromLineEdit(QString url) const
 {
     // Decide if the text is more likely to be a URL or a search.
-    if (url.startsWith("file://") || url.startsWith("view-source:"))  // The text is likely a file or view source URL.
+    if (url.startsWith(QLatin1String("file://")) || url.startsWith(QLatin1String("view-source:")))  // The text is likely a file or view source URL.
     {
         // Load the URL.
         currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
     }
-    else if (url.contains("."))  // The text is likely a URL.
+    else if (url.contains(QLatin1String(".")))  // The text is likely a URL.
     {
         // Check if the URL does not start with a valid protocol.
-        if (!url.startsWith("http"))
+        if (!url.startsWith(QLatin1String("http")))
         {
             // Add `https://` to the beginning of the URL.
-            url = "https://" + url;
+            url = QLatin1String("https://") + url;
         }
 
         // Load the URL.
@@ -826,7 +829,7 @@ void TabWidget::mouseBack() const
     if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoBack())
     {
         // Clear the URL line edit focus.
-        emit clearUrlLineEditFocus();
+        Q_EMIT clearUrlLineEditFocus();
 
         // Go back.
         currentPrivacyWebEngineViewPointer->back();
@@ -839,7 +842,7 @@ void TabWidget::mouseForward() const
     if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoForward())
     {
         // Clear the URL line edit focus.
-        emit clearUrlLineEditFocus();
+        Q_EMIT clearUrlLineEditFocus();
 
         // Go forward.
         currentPrivacyWebEngineViewPointer->forward();
@@ -849,7 +852,7 @@ void TabWidget::mouseForward() const
 void TabWidget::pageLinkHovered(const QString &linkUrl) const
 {
     // Emit a signal so that the browser window can update the status bar.
-    emit linkHovered(linkUrl);
+    Q_EMIT linkHovered(linkUrl);
 }
 
 void TabWidget::stopLoadingFavoriteIconMovie() const
@@ -861,55 +864,50 @@ void TabWidget::stopLoadingFavoriteIconMovie() const
 void TabWidget::print() const
 {
     // Create a printer.
-    QPrinter printer;
+    QPrinter *printerPointer = new QPrinter();
 
     // Set the resolution to be 300 dpi.
-    printer.setResolution(300);
+    printerPointer->setResolution(300);
 
     // Create a printer dialog.
-    QPrintDialog printDialog(&printer, currentPrivacyWebEngineViewPointer);
+    QPrintDialog printDialog(printerPointer, currentPrivacyWebEngineViewPointer);
 
     // Display the dialog and print the page if instructed.
     if (printDialog.exec() == QDialog::Accepted)
-        printWebpage(&printer);
+        currentPrivacyWebEngineViewPointer->print(printerPointer);
 }
 
 void TabWidget::printPreview() const
 {
     // Create a printer.
-    QPrinter printer;
+    QPrinter *printerPointer = new QPrinter();
 
     // Set the resolution to be 300 dpi.
-    printer.setResolution(300);
+    printerPointer->setResolution(300);
 
     // Create a print preview dialog.
-    QPrintPreviewDialog printPreviewDialog(&printer, currentPrivacyWebEngineViewPointer);
+    QPrintPreviewDialog printPreviewDialog(printerPointer, currentPrivacyWebEngineViewPointer);
 
     // Generate the print preview.
-    connect(&printPreviewDialog, SIGNAL(paintRequested(QPrinter *)), this, SLOT(printWebpage(QPrinter *)));
+    auto generatePrintPreview = [this, printerPointer](){
+        // Create an event loop.  The print preview must be generated in a loop or nothing is displayed.
+        QEventLoop eventLoop;
 
-    // Display the dialog.
-    printPreviewDialog.exec();
-}
+        // Quit the event loop once the print preview has been generated.
+        connect(currentPrivacyWebEngineViewPointer, &QWebEngineView::printFinished, &eventLoop, &QEventLoop::quit);
 
-void TabWidget::printWebpage(QPrinter *printerPointer) const
-{
-    // Create an event loop.  For some reason, the print preview doesn't produce any output unless it is run inside an event loop.
-    QEventLoop eventLoop;
+        // Generate the print preview for the current webpage.
+        currentPrivacyWebEngineViewPointer->print(printerPointer);
 
-    // Print the webpage, converting the callback above into a `QWebEngineCallback<bool>`.
-    // Printing requires that the printer be a pointer, not a reference, or it will crash with much cursing.
-    currentWebEnginePagePointer->print(printerPointer, [&eventLoop](bool printSuccess)
-    {
-        // Instruct the compiler to ignore the unused parameter.
-        (void) printSuccess;
+        // Execute the loop.
+        eventLoop.exec();
+    };
 
-        // Quit the loop.
-        eventLoop.quit();
-    });
+    // Generate the preview.
+    connect(&printPreviewDialog, &QPrintPreviewDialog::paintRequested, this, generatePrintPreview);
 
-    // Execute the loop.
-    eventLoop.exec();
+    // Display the dialog.
+    printPreviewDialog.exec();
 }
 
 void TabWidget::refresh() const
@@ -930,7 +928,7 @@ void TabWidget::reloadAndBypassCache() const
 void TabWidget::saveArchive()
 {
     // Get the suggested file name.
-    QString suggestedFileName = currentPrivacyWebEngineViewPointer->title() + ".mht";
+    QString suggestedFileName = currentPrivacyWebEngineViewPointer->title() + QLatin1String(".mht");
 
     // Get the download directory.
     QString downloadDirectory = Settings::downloadDirectory();
@@ -963,16 +961,16 @@ void TabWidget::setTabBarVisible(const bool visible) const
     qTabWidgetPointer->tabBar()->setVisible(visible);
 }
 
-void TabWidget::showSaveDialog(QWebEngineDownloadItem *webEngineDownloadItemPointer)
+void TabWidget::showSaveDialog(QWebEngineDownloadRequest *webEngineDownloadRequestPointer)
 {
     // Only show the save dialog if an archive is not currently being saved.  Otherwise, two save dialogs will be shown.
     if (!savingArchive)
     {
         // Get the download attributes.
-        QUrl downloadUrl = webEngineDownloadItemPointer->url();
-        QString mimeTypeString = webEngineDownloadItemPointer->mimeType();
-        QString suggestedFileName = webEngineDownloadItemPointer->suggestedFileName();
-        int totalBytes = webEngineDownloadItemPointer->totalBytes();
+        QUrl downloadUrl = webEngineDownloadRequestPointer->url();
+        QString mimeTypeString = webEngineDownloadRequestPointer->mimeType();
+        QString suggestedFileName = webEngineDownloadRequestPointer->suggestedFileName();
+        int totalBytes = webEngineDownloadRequestPointer->totalBytes();
 
         // Check to see if Privacy Browser is not running KDE or if local storage (cookies) is enabled.
         if (!isRunningKde || currentPrivacyWebEngineViewPointer->localStorageEnabled)  // KDE is not running or local storage (cookies) is enabled.  Use WebEngine's downloader.
@@ -1011,8 +1009,8 @@ void TabWidget::showSaveDialog(QWebEngineDownloadItem *webEngineDownloadItemPoin
                     QString saveFileName = saveFilePathFileInfo.fileName();
 
                     // Set the download directory and file name.
-                    webEngineDownloadItemPointer->setDownloadDirectory(absoluteSavePath);
-                    webEngineDownloadItemPointer->setDownloadFileName(saveFileName);
+                    webEngineDownloadRequestPointer->setDownloadDirectory(absoluteSavePath);
+                    webEngineDownloadRequestPointer->setDownloadFileName(saveFileName);
 
                     // Create a file download notification.
                     KNotification *fileDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
@@ -1029,17 +1027,17 @@ void TabWidget::showSaveDialog(QWebEngineDownloadItem *webEngineDownloadItemPoin
                     // Set the notification icon.
                     fileDownloadNotificationPointer->setIconName(downloadIcon.name());
 
-                    // Set the action list cancel button.
-                    fileDownloadNotificationPointer->setActions(QStringList({i18nc("Download notification action","Cancel")}));
+                    // Add the cancel action.
+                    KNotificationAction *cancelActionPointer = fileDownloadNotificationPointer->addDefaultAction(i18nc("Download notification action","Cancel"));
 
                     // Prevent the notification from being autodeleted if it is closed.  Otherwise, the updates to the notification below cause a crash.
                     fileDownloadNotificationPointer->setAutoDelete(false);
 
-                    // Handle clicks on the cancel button.
-                    connect(fileDownloadNotificationPointer, &KNotification::action1Activated, [webEngineDownloadItemPointer, saveFileName] ()
+                    // Handle clicks on the cancel action.
+                    connect(cancelActionPointer, &KNotificationAction::activated, [webEngineDownloadRequestPointer, saveFileName] ()
                     {
                         // Cancel the download.
-                        webEngineDownloadItemPointer->cancel();
+                        webEngineDownloadRequestPointer->cancel();
 
                         // Create a file download notification.
                         KNotification *canceledDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
@@ -1058,63 +1056,72 @@ void TabWidget::showSaveDialog(QWebEngineDownloadItem *webEngineDownloadItemPoin
                     });
 
                     // Update the notification when the download progresses.
-                    connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::downloadProgress, [fileDownloadNotificationPointer, saveFileName] (qint64 bytesReceived, qint64 totalBytes)
+                    connect(webEngineDownloadRequestPointer, &QWebEngineDownloadRequest::receivedBytesChanged, [webEngineDownloadRequestPointer, fileDownloadNotificationPointer, saveFileName] ()
                     {
+                        // Get the download request information.
+                        qint64 receivedBytes = webEngineDownloadRequestPointer->receivedBytes();
+                        qint64 totalBytes = webEngineDownloadRequestPointer->totalBytes();
+
                         // Set the new text.  Total bytes will be 0 if the download size is unknown.
                         if (totalBytes > 0)
                         {
                             // Calculate the download percentage.
-                            int downloadPercentage = 100 * bytesReceived / totalBytes;
+                            int downloadPercentage = 100 * receivedBytes / totalBytes;
 
                             // Set the file download notification text.
-                            fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1\% of %2 downloaded (%3 of %4 bytes)", downloadPercentage, saveFileName,
-                                                                        bytesReceived, totalBytes));
+                            fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1%% of %2 downloaded (%3 of %4 bytes)", downloadPercentage, saveFileName,
+                                                                           receivedBytes, totalBytes));
                         }
                         else
                         {
                             // Set the file download notification text.
-                            fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1:  %2 bytes downloaded", saveFileName, bytesReceived));
+                            fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1:  %2 bytes downloaded", saveFileName, receivedBytes));
                         }
 
                         // Display the updated notification.
-                        fileDownloadNotificationPointer->update();
+                        fileDownloadNotificationPointer->sendEvent();
                     });
 
                     // Update the notification when the download finishes.  The save file name must be copied into the lambda or a crash occurs.
-                    connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::finished, [fileDownloadNotificationPointer, saveFileName, saveFilePath] ()
+                    connect(webEngineDownloadRequestPointer, &QWebEngineDownloadRequest::isFinishedChanged, [webEngineDownloadRequestPointer, fileDownloadNotificationPointer, saveFileName,
+                            saveFilePath] ()
                     {
-                        // Set the new text.
-                        fileDownloadNotificationPointer->setText(i18nc("Download finished notification text", "%1 download finished", saveFileName));
+                        // Update the notification if the download is finished.
+                        if (webEngineDownloadRequestPointer->isFinished())
+                        {
+                            // Set the new text.
+                            fileDownloadNotificationPointer->setText(i18nc("Download finished notification text", "%1 download finished", saveFileName));
 
-                        // Set the URL so the file options will be displayed.
-                        fileDownloadNotificationPointer->setUrls(QList<QUrl> {QUrl(saveFilePath)});
+                            // Set the URL so the file options will be displayed.
+                            fileDownloadNotificationPointer->setUrls(QList<QUrl> {QUrl(saveFilePath)});
 
-                        // Remove the actions from the notification.
-                        fileDownloadNotificationPointer->setActions(QStringList());
+                            // Remove the actions from the notification.
+                            fileDownloadNotificationPointer->clearActions();
 
-                        // Set the notification to disappear after a timeout.
-                        fileDownloadNotificationPointer->setFlags(KNotification::CloseOnTimeout);
+                            // Set the notification to disappear after a timeout.
+                            fileDownloadNotificationPointer->setFlags(KNotification::CloseOnTimeout);
 
-                        // Display the updated notification.
-                        fileDownloadNotificationPointer->update();
+                            // Display the updated notification.
+                            fileDownloadNotificationPointer->sendEvent();
+                        }
                     });
 
                     // Display the notification.
                     fileDownloadNotificationPointer->sendEvent();
 
                     // Start the download.
-                    webEngineDownloadItemPointer->accept();
+                    webEngineDownloadRequestPointer->accept();
                 }
                 else  // The file save path is not populated.
                 {
                     // Cancel the download.
-                    webEngineDownloadItemPointer->cancel();
+                    webEngineDownloadRequestPointer->cancel();
                 }
             }
             else  // Cancel was selected.
             {
                 // Cancel the download.
-                webEngineDownloadItemPointer->cancel();
+                webEngineDownloadRequestPointer->cancel();
             }
         }
         else  // KDE is running and local storage (cookies) is disabled.  Use KDE's native downloader.
@@ -1141,10 +1148,17 @@ void TabWidget::stop() const
     currentPrivacyWebEngineViewPointer->stop();
 }
 
+void TabWidget::storeCurrentUrlText(const QString &urlText) const
+{
+    // Store the current URL text in the privacy WebEngine view.
+    currentPrivacyWebEngineViewPointer->currentUrlText = urlText;
+}
+
 void TabWidget::toggleDeveloperTools(const bool enabled) const
 {
-    // Get a handle for the current developer tools WebEngine.
-    DevToolsWebEngineView *devToolsWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild<DevToolsWebEngineView *>();
+    // Get handles for the current tab widgets.
+    QSplitter *splitterPointer = qobject_cast<QSplitter*>(qTabWidgetPointer->currentWidget());
+    DevToolsWebEngineView *devToolsWebEngineViewPointer = splitterPointer->findChild<DevToolsWebEngineView *>();
 
     if (enabled)
     {
@@ -1159,6 +1173,9 @@ void TabWidget::toggleDeveloperTools(const bool enabled) const
 
         // Display the developer tools.
         devToolsWebEngineViewPointer->setVisible(true);
+
+        // Split the visible space equally between the main WebEngine and the developer tools WebEngine.
+        splitterPointer->setSizes(QList<int>({1, 1}));
     }
     else
     {
@@ -1179,7 +1196,34 @@ void TabWidget::toggleDomStorage() const
     currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
 
     // Update the DOM storage action.
-    emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
+    Q_EMIT updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
+
+    // Reload the website.
+    currentPrivacyWebEngineViewPointer->reload();
+}
+
+void TabWidget::toggleEasyList() const
+{
+    // Toggle EasyList.
+    currentPrivacyWebEngineViewPointer->easyListEnabled = !currentPrivacyWebEngineViewPointer->easyListEnabled;
+
+    // Reload the website.
+    currentPrivacyWebEngineViewPointer->reload();
+}
+
+void TabWidget::toggleEasyPrivacy() const
+{
+    // Toggle EasyPrivacy.
+    currentPrivacyWebEngineViewPointer->easyPrivacyEnabled = !currentPrivacyWebEngineViewPointer->easyPrivacyEnabled;
+
+    // Reload the website.
+    currentPrivacyWebEngineViewPointer->reload();
+}
+
+void TabWidget::toggleFanboysAnnoyanceList() const
+{
+    // Toggle Fanboy's Annoyance List.
+    currentPrivacyWebEngineViewPointer->fanboysAnnoyanceListEnabled = !currentPrivacyWebEngineViewPointer->fanboysAnnoyanceListEnabled;
 
     // Reload the website.
     currentPrivacyWebEngineViewPointer->reload();
@@ -1206,7 +1250,7 @@ void TabWidget::toggleJavaScript() const
     currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
 
     // Update the JavaScript action.
-    emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
+    Q_EMIT updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
 
     // Reload the website.
     currentPrivacyWebEngineViewPointer->reload();
@@ -1218,7 +1262,25 @@ void TabWidget::toggleLocalStorage()
     currentPrivacyWebEngineViewPointer->localStorageEnabled = !currentPrivacyWebEngineViewPointer->localStorageEnabled;
 
     // Update the local storage action.
-    emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
+    Q_EMIT updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
+
+    // Reload the website.
+    currentPrivacyWebEngineViewPointer->reload();
+}
+
+void TabWidget::toggleUltraList() const
+{
+    // Toggle UltraList.
+    currentPrivacyWebEngineViewPointer->ultraListEnabled = !currentPrivacyWebEngineViewPointer->ultraListEnabled;
+
+    // Reload the website.
+    currentPrivacyWebEngineViewPointer->reload();
+}
+
+void TabWidget::toggleUltraPrivacy() const
+{
+    // Toggle UltraPrivacy.
+    currentPrivacyWebEngineViewPointer->ultraPrivacyEnabled = !currentPrivacyWebEngineViewPointer->ultraPrivacyEnabled;
 
     // Reload the website.
     currentPrivacyWebEngineViewPointer->reload();
@@ -1245,18 +1307,26 @@ void TabWidget::updateUiFromWebEngineView(const PrivacyWebEngineView *privacyWeb
     if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
     {
         // Update the UI.
-        emit updateDefaultZoomFactor(currentPrivacyWebEngineViewPointer->defaultZoomFactor);
-        emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QLatin1String(""));
-        emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
-        emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
-        emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
-        emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
-        emit updateZoomActions(currentPrivacyWebEngineViewPointer->zoomFactor());
+        Q_EMIT easyListStatusChanged(currentPrivacyWebEngineViewPointer->easyListEnabled);
+        Q_EMIT easyPrivacyStatusChanged(currentPrivacyWebEngineViewPointer->easyPrivacyEnabled);
+        Q_EMIT fanboysAnnoyanceListStatusChanged(currentPrivacyWebEngineViewPointer->fanboysAnnoyanceListEnabled);
+        Q_EMIT ultraListStatusChanged(currentPrivacyWebEngineViewPointer->ultraListEnabled);
+        Q_EMIT ultraPrivacyStatusChanged(currentPrivacyWebEngineViewPointer->ultraPrivacyEnabled);
+        Q_EMIT updateDefaultZoomFactor(currentPrivacyWebEngineViewPointer->defaultZoomFactor);
+        Q_EMIT updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QLatin1String(""));
+        Q_EMIT updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
+        Q_EMIT updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
+        Q_EMIT updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
+        Q_EMIT updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
+        Q_EMIT updateZoomActions(currentPrivacyWebEngineViewPointer->zoomFactor());
     }
 }
 
 void TabWidget::updateUiWithTabSettings()
 {
+    // Clear the URL line edit focus.
+    Q_EMIT clearUrlLineEditFocus();
+
     // Update the current WebEngine pointers.
     currentPrivacyWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild<PrivacyWebEngineView *>();
     currentWebEngineSettingsPointer = currentPrivacyWebEngineViewPointer->settings();
@@ -1265,39 +1335,41 @@ void TabWidget::updateUiWithTabSettings()
     currentWebEngineHistoryPointer = currentWebEnginePagePointer->history();
     currentWebEngineCookieStorePointer = currentWebEngineProfilePointer->cookieStore();
 
-    // Clear the URL line edit focus.
-    emit clearUrlLineEditFocus();
-
     // Get a handle for the development tools WebEngine view.
     DevToolsWebEngineView *devToolsWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild<DevToolsWebEngineView *>();
 
     // Update the actions.
-    emit blockedRequestsUpdated(currentPrivacyWebEngineViewPointer->blockedRequests);
-    emit cookiesChanged(currentPrivacyWebEngineViewPointer->cookieListPointer->size());
-    emit updateDefaultZoomFactor(currentPrivacyWebEngineViewPointer->defaultZoomFactor);
-    emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
-    emit updateDeveloperToolsAction(devToolsWebEngineViewPointer->isVisible());
-    emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
-    emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
-    emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
-    emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
-    emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
-    emit updateZoomActions(currentPrivacyWebEngineViewPointer->zoomFactor());
-
-    // Update the URL.
-    emit updateWindowTitle(currentPrivacyWebEngineViewPointer->title());
-    emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QLatin1String(""));
-    emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url());
+    Q_EMIT easyListStatusChanged(currentPrivacyWebEngineViewPointer->easyListEnabled);
+    Q_EMIT easyPrivacyStatusChanged(currentPrivacyWebEngineViewPointer->easyPrivacyEnabled);
+    Q_EMIT fanboysAnnoyanceListStatusChanged(currentPrivacyWebEngineViewPointer->fanboysAnnoyanceListEnabled);
+    Q_EMIT ultraListStatusChanged(currentPrivacyWebEngineViewPointer->ultraListEnabled);
+    Q_EMIT ultraPrivacyStatusChanged(currentPrivacyWebEngineViewPointer->ultraPrivacyEnabled);
+    Q_EMIT blockedRequestsUpdated(currentPrivacyWebEngineViewPointer->blockedRequestsVector);
+    Q_EMIT cookiesChanged(currentPrivacyWebEngineViewPointer->cookieListPointer->size());
+    Q_EMIT updateBackAction(currentWebEngineHistoryPointer->canGoBack());
+    Q_EMIT updateDefaultZoomFactor(currentPrivacyWebEngineViewPointer->defaultZoomFactor);
+    Q_EMIT updateDeveloperToolsAction(devToolsWebEngineViewPointer->isVisible());
+    Q_EMIT updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
+    Q_EMIT updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
+    Q_EMIT updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
+    Q_EMIT updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
+    Q_EMIT updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
+    Q_EMIT updateZoomActions(currentPrivacyWebEngineViewPointer->zoomFactor());
 
     // Update the find text.
-    emit updateFindText(currentPrivacyWebEngineViewPointer->findString, currentPrivacyWebEngineViewPointer->findCaseSensitive);
-    emit updateFindTextResults(currentPrivacyWebEngineViewPointer->findTextResult);
+    Q_EMIT updateFindText(currentPrivacyWebEngineViewPointer->findString, currentPrivacyWebEngineViewPointer->findCaseSensitive);
+    Q_EMIT updateFindTextResults(currentPrivacyWebEngineViewPointer->findTextResult);
 
     // Update the progress bar.
     if (currentPrivacyWebEngineViewPointer->loadProgressInt >= 0)
-        emit showProgressBar(currentPrivacyWebEngineViewPointer->loadProgressInt);
+        Q_EMIT showProgressBar(currentPrivacyWebEngineViewPointer->loadProgressInt);
     else
-        emit hideProgressBar();
+        Q_EMIT hideProgressBar();
+
+    // Update the URL.
+    Q_EMIT updateWindowTitle(currentPrivacyWebEngineViewPointer->title());
+    Q_EMIT updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QLatin1String(""));
+    Q_EMIT updateUrlLineEdit(QUrl(currentPrivacyWebEngineViewPointer->currentUrlText));
 }
 
 void TabWidget::useNativeKdeDownloader(QUrl &downloadUrl, QString &suggestedFileName)
@@ -1348,3 +1420,4 @@ void TabWidget::useNativeKdeDownloader(QUrl &downloadUrl, QString &suggestedFile
     // Show the dialog.
     saveFileDialogPointer->show();
 }
+