/*
- * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2022-2023 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
*
#include "ui_AddTabWidget.h"
#include "ui_TabWidget.h"
#include "databases/CookiesDatabase.h"
-#include "databases/DomainsDatabase.h"
#include "dialogs/SaveDialog.h"
#include "filters/MouseEventFilter.h"
#include "helpers/SearchEngineHelper.h"
-#include "helpers/UserAgentHelper.h"
-#include "interceptors/UrlRequestInterceptor.h"
#include "windows/BrowserWindow.h"
// KDE Framework headers.
#include <KIO/FileCopyJob>
#include <KIO/JobUiDelegate>
+#include <KNotification>
// Qt toolkit headers.
#include <QAction>
#include <QPrinter>
// Initialize the public static variables.
-QString TabWidget::webEngineDefaultUserAgent = QStringLiteral("");
+QString TabWidget::webEngineDefaultUserAgent = QLatin1String("");
// Construct the class.
TabWidget::TabWidget(QWidget *parent) : QWidget(parent)
{
+ // Instantiate the user agent helper.
+ userAgentHelperPointer = new UserAgentHelper();
+
// Instantiate the UIs.
Ui::TabWidget tabWidgetUi;
Ui::AddTabWidget addTabWidgetUi;
tabWidgetUi.setupUi(this);
// Get a handle for the tab widget.
- tabWidgetPointer = tabWidgetUi.tabWidget;
+ qTabWidgetPointer = tabWidgetUi.tabWidget;
// Setup the add tab UI.
- addTabWidgetUi.setupUi(tabWidgetPointer);
+ addTabWidgetUi.setupUi(qTabWidgetPointer);
// Get handles for the add tab widgets.
QWidget *addTabWidgetPointer = addTabWidgetUi.addTabQWidget;
QPushButton *addTabButtonPointer = addTabWidgetUi.addTabButton;
// Display the add tab widget.
- tabWidgetPointer->setCornerWidget(addTabWidgetPointer);
+ qTabWidgetPointer->setCornerWidget(addTabWidgetPointer);
// Add the first tab.
addFirstTab();
// Process tab events.
- connect(tabWidgetPointer, SIGNAL(currentChanged(int)), this, SLOT(updateUiWithTabSettings()));
+ connect(qTabWidgetPointer, SIGNAL(currentChanged(int)), this, SLOT(updateUiWithTabSettings()));
connect(addTabButtonPointer, SIGNAL(clicked()), this, SLOT(addTab()));
- connect(tabWidgetPointer, SIGNAL(tabCloseRequested(int)), this, SLOT(deleteTab(int)));
+ connect(qTabWidgetPointer, SIGNAL(tabCloseRequested(int)), this, SLOT(deleteTab(int)));
// Store a copy of the WebEngine default user agent.
webEngineDefaultUserAgent = currentWebEngineProfilePointer->httpUserAgent();
TabWidget::~TabWidget()
{
// Manually delete each WebEngine page.
- for (int i = 0; i < tabWidgetPointer->count(); ++i)
+ for (int i = 0; i < qTabWidgetPointer->count(); ++i)
{
// Get the privacy WebEngine view.
- PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->widget(i));
+ PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(qTabWidgetPointer->widget(i));
// Deletion the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
delete privacyWebEngineViewPointer->page();
// The cookie is copied instead of referenced so that changes made to the cookie do not create a race condition with the display of the cookie in the dialog.
void TabWidget::addCookieToStore(QNetworkCookie cookie, QWebEngineCookieStore *webEngineCookieStorePointer) const
{
- // Create a url.
+ // Create a URL.
QUrl url;
// Check to see if the domain does not start with a `.` because Qt makes this harder than it should be. <https://doc.qt.io/qt-5/qwebenginecookiestore.html#setCookie>
- if (!cookie.domain().startsWith(QStringLiteral(".")))
+ if (!cookie.domain().startsWith(QLatin1String(".")))
{
// Populate the URL.
url.setHost(cookie.domain());
- url.setScheme(QStringLiteral("https"));
+ url.setScheme(QLatin1String("https"));
// Clear the domain from the cookie.
- cookie.setDomain(QStringLiteral(""));
+ cookie.setDomain(QLatin1String(""));
}
// Add the cookie to the store.
updateUiWithTabSettings();
// Set the focus on the current tab widget. This prevents the tab bar from showing a blue bar under the label of the first tab.
- tabWidgetPointer->currentWidget()->setFocus();
+ qTabWidgetPointer->currentWidget()->setFocus();
}
-PrivacyWebEngineView* TabWidget::addTab(const bool focusNewWebEngineView)
+PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const bool backgroundTab)
{
// Create a privacy WebEngine view.
PrivacyWebEngineView *privacyWebEngineViewPointer = new PrivacyWebEngineView();
// Add a new tab.
- int newTabIndex = tabWidgetPointer->addTab(privacyWebEngineViewPointer, i18nc("New tab label.", "New Tab"));
+ int newTabIndex = qTabWidgetPointer->addTab(privacyWebEngineViewPointer, i18nc("New tab label.", "New Tab"));
// Set the default tab icon.
- tabWidgetPointer->setTabIcon(newTabIndex, defaultTabIcon);
-
- // Create an off-the-record profile (the default when no profile name is specified).
- QWebEngineProfile *webEngineProfilePointer = new QWebEngineProfile(QStringLiteral(""));
+ qTabWidgetPointer->setTabIcon(newTabIndex, defaultTabIcon);
- // Create a WebEngine page.
- QWebEnginePage *webEnginePagePointer = new QWebEnginePage(webEngineProfilePointer);
-
- // Set the WebEngine page.
- privacyWebEngineViewPointer->setPage(webEnginePagePointer);
+ // Get handles for the WebEngine page and profile.
+ QWebEnginePage *webEnginePagePointer = privacyWebEngineViewPointer->page();
+ QWebEngineProfile *webEngineProfilePointer = webEnginePagePointer->profile();
// Get handles for the web engine elements.
QWebEngineCookieStore *webEngineCookieStorePointer = webEngineProfilePointer->cookieStore();
emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
}
-
- // Reapply the zoom factor. This is a bug in QWebEngineView that resets the zoom with every load. It can be removed once <https://redmine.stoutner.com/issues/799> is fixed.
- privacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);
});
// Update the progress bar when a load is started.
emit hideProgressBar();
});
+ // Update the zoom factor when changed by CTRL-Scrolling. This can be modified when <https://redmine.stoutner.com/issues/845> is fixed.
+ connect(webEnginePagePointer, &QWebEnginePage::contentsSizeChanged, [webEnginePagePointer, this] ()
+ {
+ // Only update the zoom factor action text if this is the current tab.
+ if (webEnginePagePointer == currentWebEnginePagePointer)
+ emit updateZoomFactorAction(webEnginePagePointer->zoomFactor());
+ });
+
+ // Display find text results.
+ connect(webEnginePagePointer, SIGNAL(findTextFinished(const QWebEngineFindTextResult &)), this, SLOT(findTextFinished(const QWebEngineFindTextResult &)));
+
// Handle full screen requests.
connect(webEnginePagePointer, SIGNAL(fullScreenRequested(QWebEngineFullScreenRequest)), this, SLOT(fullScreenRequested(QWebEngineFullScreenRequest)));
// Handle file downloads.
connect(webEngineProfilePointer, SIGNAL(downloadRequested(QWebEngineDownloadItem *)), this, SLOT(showSaveDialog(QWebEngineDownloadItem *)));
- // Instantiate the URL request interceptor.
- UrlRequestInterceptor *urlRequestInterceptorPointer = new UrlRequestInterceptor();
-
- // Set the URL request interceptor.
- webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer);
-
- // Reapply the domain settings when the host changes.
- connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings(QString)), this, SLOT(applyDomainSettingsWithoutReloading(QString)));
-
// Set the local storage filter.
webEngineCookieStorePointer->setCookieFilter([privacyWebEngineViewPointer](const QWebEngineCookieStore::FilterRequest &filterRequest)
{
return false;
});
- // Disable JavaScript by default (this prevetns JavaScript from being enabled on a new tab before domain settings are loaded).
+ // Disable JavaScript by default (this prevents JavaScript from being enabled on a new tab before domain settings are loaded).
webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
// Don't allow JavaScript to open windows.
// Limit WebRTC to public IP addresses.
webEngineSettingsPointer->setAttribute(QWebEngineSettings::WebRTCPublicInterfacesOnly, true);
+ // Enable the PDF viewer (it should be enabled by default, but it is nice to be explicit in case the defaults change).
+ webEngineSettingsPointer->setAttribute(QWebEngineSettings::PdfViewerEnabled, true);
+
+ // 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);
+
// Update the cookies action.
connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::updateCookiesAction, [privacyWebEngineViewPointer, this] (const int numberOfCookies)
{
connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::titleChanged, [this, privacyWebEngineViewPointer] (const QString &title)
{
// Get the index for this tab.
- int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer);
+ int tabIndex = qTabWidgetPointer->indexOf(privacyWebEngineViewPointer);
// Update the title for this tab.
- tabWidgetPointer->setTabText(tabIndex, title);
+ qTabWidgetPointer->setTabText(tabIndex, title);
// Update the window title if this is the current tab.
- if (tabIndex == tabWidgetPointer->currentIndex())
+ if (tabIndex == qTabWidgetPointer->currentIndex())
emit updateWindowTitle(title);
});
connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::iconChanged, [privacyWebEngineViewPointer, this] (const QIcon &icon)
{
// Get the index for this tab.
- int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer);
+ int tabIndex = qTabWidgetPointer->indexOf(privacyWebEngineViewPointer);
// Update the icon for this tab.
if (icon.isNull())
- tabWidgetPointer->setTabIcon(tabIndex, defaultTabIcon);
+ qTabWidgetPointer->setTabIcon(tabIndex, defaultTabIcon);
else
- tabWidgetPointer->setTabIcon(tabIndex, icon);
+ qTabWidgetPointer->setTabIcon(tabIndex, icon);
});
// Enable spell checking.
webEngineProfilePointer->setSpellCheckEnabled(true);
// Set the spell check language.
- webEngineProfilePointer->setSpellCheckLanguages({QStringLiteral("en_US")});
+ webEngineProfilePointer->setSpellCheckLanguages(Settings::spellCheckLanguages());
+
+ // Populate the zoom factor. This is necessary if a URL is being loaded, like a local URL, that does not trigger `applyDomainSettings()`.
+ privacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor());
+
+ // Update the UI when domain settings are applied.
+ connect(privacyWebEngineViewPointer, SIGNAL(updateUi(const PrivacyWebEngineView*)), this, SLOT(updateUiFromWebEngineView(const PrivacyWebEngineView*)));
- // Move to the new tab.
- tabWidgetPointer->setCurrentIndex(newTabIndex);
+ // Move to the new tab if it is not a background tab.
+ if (!backgroundTab)
+ qTabWidgetPointer->setCurrentIndex(newTabIndex);
// Clear the URL line edit focus so that it populates correctly when opening a new tab from the context menu.
- if (focusNewWebEngineView)
+ if (removeUrlLineEditFocus)
emit clearUrlLineEditFocus();
// Return the privacy WebEngine view pointer.
{
// Set the tab position.
if (Settings::tabsOnTop())
- tabWidgetPointer->setTabPosition(QTabWidget::North);
+ qTabWidgetPointer->setTabPosition(QTabWidget::North);
else
- tabWidgetPointer->setTabPosition(QTabWidget::South);
+ qTabWidgetPointer->setTabPosition(QTabWidget::South);
// Set the search engine URL.
searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
emit updateSearchEngineActions(Settings::searchEngine(), true);
}
-// This exists as a separate function from `applyDomainSettings()` so it can be listed as a slot and function without the need for a boolean argument.
-// Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
void TabWidget::applyDomainSettingsAndReload()
{
// Apply the domain settings. `true` reloads the website.
- applyDomainSettings(currentPrivacyWebEngineViewPointer->url().host(), true);
-}
-
-// This exists as a separate function from `applyDomainSettings()` so it can be listed as a slot and function without the need for a boolean argument.
-// Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
-void TabWidget::applyDomainSettingsWithoutReloading(const QString &hostname)
-{
- // Apply the domain settings `false` does not reload the website.
- applyDomainSettings(hostname, false);
-}
-
-// Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
-void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
-{
- // Get the record for the hostname.
- QSqlQuery domainQuery = DomainsDatabase::getDomainQuery(hostname);
-
- // Check if the hostname has domain settings.
- if (domainQuery.isValid()) // The hostname has domain settings.
- {
- // Get the domain record.
- QSqlRecord domainRecord = domainQuery.record();
-
- // Store the domain settings name.
- currentPrivacyWebEngineViewPointer->domainSettingsName = domainRecord.field(DomainsDatabase::DOMAIN_NAME).value().toString();
-
- // Set the JavaScript status.
- switch (domainRecord.field(DomainsDatabase::JAVASCRIPT).value().toInt())
- {
- // Set the default JavaScript status.
- case (DomainsDatabase::SYSTEM_DEFAULT):
- {
- currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
-
- break;
- }
-
- // Disable JavaScript.
- case (DomainsDatabase::DISABLED):
- {
- currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
-
- break;
- }
-
- // Enable JavaScript.
- case (DomainsDatabase::ENABLED):
- {
- currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
-
- break;
- }
- }
-
- // Set the local storage status.
- switch (domainRecord.field(DomainsDatabase::LOCAL_STORAGE).value().toInt())
- {
- // Set the default local storage status.
- case (DomainsDatabase::SYSTEM_DEFAULT):
- {
- currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
-
- break;
- }
-
- // Disable local storage.
- case (DomainsDatabase::DISABLED):
- {
- currentPrivacyWebEngineViewPointer->localStorageEnabled = false;
-
- break;
- }
-
- // Enable local storage.
- case (DomainsDatabase::ENABLED):
- {
- currentPrivacyWebEngineViewPointer->localStorageEnabled = true;
-
- break;
- }
- }
-
- // Set the DOM storage status.
- switch (domainRecord.field(DomainsDatabase::DOM_STORAGE).value().toInt())
- {
- // Set the default DOM storage status.
- case (DomainsDatabase::SYSTEM_DEFAULT):
- {
- currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
-
- break;
- }
-
- // Disable DOM storage.
- case (DomainsDatabase::DISABLED):
- {
- currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
-
- break;
- }
-
- // Enable DOM storage.
- case (DomainsDatabase::ENABLED):
- {
- currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
-
- break;
- }
- }
-
- // Set the user agent.
- currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainRecord.field(DomainsDatabase::USER_AGENT).value().toString()));
-
- // Check if a custom zoom factor is set.
- if (domainRecord.field(DomainsDatabase::ZOOM_FACTOR).value().toInt())
- {
- // Store the current zoom factor.
- currentZoomFactor = domainRecord.field(DomainsDatabase::CUSTOM_ZOOM_FACTOR).value().toDouble();
- }
- else
- {
- // Reset the current zoom factor.
- currentZoomFactor = Settings::zoomFactor();
- }
-
- // Set the zoom factor. The use of `currentZoomFactor` can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
- currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);
- }
- else // The hostname does not have domain settings.
- {
- // Reset the domain settings name.
- currentPrivacyWebEngineViewPointer->domainSettingsName = QStringLiteral("");
-
- // Set the JavaScript status.
- currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
-
- // Set the local storage status.
- currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
-
- // Set DOM storage. In QWebEngineSettings it is called Local Storage.
- currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
-
- // Set the user agent.
- currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
-
- // Store the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
- currentZoomFactor = Settings::zoomFactor();
-
- // Set the zoom factor.
- currentPrivacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor());
- }
-
- // Update the UI.
- emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
- emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
- emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
- emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
- emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
- emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
-
- // Reload the website if requested.
- if (reloadWebsite)
- currentPrivacyWebEngineViewPointer->reload();
+ currentPrivacyWebEngineViewPointer->applyDomainSettings(currentPrivacyWebEngineViewPointer->url().host(), true);
}
void TabWidget::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
// Store the search engine string.
searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
- // Update the search engine actionas.
+ // Update the search engine actions.
emit updateSearchEngineActions(searchEngineName, false);
}
userAgentName.remove('&');
// Apply the user agent.
- currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromTranslatedName(userAgentName));
+ currentWebEngineProfilePointer->setHttpUserAgent(userAgentHelperPointer->getUserAgentFromTranslatedName(userAgentName));
// Update the user agent actions.
emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), false);
currentPrivacyWebEngineViewPointer->reload();
}
-// This can be const once <https://redmine.stoutner.com/issues/799> has been resolved.
-void TabWidget::applyOnTheFlyZoomFactor(const double &zoomFactor)
+void TabWidget::applyOnTheFlyZoomFactor(const double &zoomFactor) const
{
- // Update the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
- currentZoomFactor = zoomFactor;
-
// Set the zoom factor.
currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactor);
}
+void TabWidget::applySpellCheckLanguages() const
+{
+ // Get the number of tab.
+ int numberOfTabs = qTabWidgetPointer->count();
+
+ // Set the spell check languages for each tab.
+ for (int i = 0; i < numberOfTabs; ++i)
+ {
+ // Get the WebEngine view pointer.
+ PrivacyWebEngineView *webEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(qTabWidgetPointer->currentWidget());
+
+ // Get the WebEngine page pointer.
+ QWebEnginePage *webEnginePagePointer = webEngineViewPointer->page();
+
+ // Get the WebEngine profile pointer.
+ QWebEngineProfile *webEngineProfilePointer = webEnginePagePointer->profile();
+
+ // Set the spell check languages.
+ webEngineProfilePointer->setSpellCheckLanguages(Settings::spellCheckLanguages());
+ }
+}
+
void TabWidget::back() const
{
// Go back.
void TabWidget::deleteTab(const int tabIndex)
{
// Get the privacy WebEngine view.
- PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->widget(tabIndex));
+ PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(qTabWidgetPointer->widget(tabIndex));
- // Proccess the tab delete according to the number of tabs.
- if (tabWidgetPointer->count() > 1) // There is more than one tab.
+ // Process the tab delete according to the number of tabs.
+ if (qTabWidgetPointer->count() > 1) // There is more than one tab.
{
// Delete the tab.
- tabWidgetPointer->removeTab(tabIndex);
+ qTabWidgetPointer->removeTab(tabIndex);
// Delete the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
delete privacyWebEngineViewPointer->page();
}
}
+void TabWidget::findPrevious(const QString &text) const
+{
+ // Store the current text.
+ currentPrivacyWebEngineViewPointer->findString = text;
+
+ // Find the previous text in the current privacy WebEngine.
+ if (currentPrivacyWebEngineViewPointer->findCaseSensitive)
+ currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively|QWebEnginePage::FindBackward);
+ else
+ currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindBackward);
+}
+
+void TabWidget::findText(const QString &text) const
+{
+ // Store the current text.
+ currentPrivacyWebEngineViewPointer->findString = text;
+
+ // Find the text in the current privacy WebEngine.
+ if (currentPrivacyWebEngineViewPointer->findCaseSensitive)
+ currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively);
+ else
+ currentPrivacyWebEngineViewPointer->findText(text);
+
+ // Clear the currently selected text in the WebEngine page if the find text is empty.
+ if (text.isEmpty())
+ currentWebEnginePagePointer->action(QWebEnginePage::Unselect)->activate(QAction::Trigger);
+}
+
+void TabWidget::findTextFinished(const QWebEngineFindTextResult &findTextResult)
+{
+ // Update the find text UI if it wasn't simply wiping the current find text selection. Otherwise the UI temporarially flashes `0/0`.
+ if (wipingCurrentFindTextSelection) // The current selection is being wiped.
+ {
+ // Reset the flag.
+ wipingCurrentFindTextSelection = false;
+ }
+ else // A new search has been performed.
+ {
+ // Store the result.
+ currentPrivacyWebEngineViewPointer->findTextResult = findTextResult;
+
+ // Update the UI.
+ emit updateFindTextResults(findTextResult);
+ }
+}
+
void TabWidget::forward() const
{
// Go forward.
void TabWidget::setTabBarVisible(const bool visible) const
{
// Set the tab bar visibility.
- tabWidgetPointer->tabBar()->setVisible(visible);
+ qTabWidgetPointer->tabBar()->setVisible(visible);
}
-void TabWidget::showSaveDialog(QWebEngineDownloadItem *downloadItemPointer) const
+void TabWidget::showSaveDialog(QWebEngineDownloadItem *webEngineDownloadItemPointer)
{
- // Instantiate the save dialog.
- SaveDialog *saveDialogPointer = new SaveDialog(downloadItemPointer);
+ // Get the download attributes.
+ QUrl downloadUrl = webEngineDownloadItemPointer->url();
+ QString mimeTypeString = webEngineDownloadItemPointer->mimeType();
+ QString suggestedFileName = webEngineDownloadItemPointer->suggestedFileName();
+ int totalBytes = webEngineDownloadItemPointer->totalBytes();
- // Connect the save button.
- connect(saveDialogPointer, SIGNAL(showSaveFilePickerDialog(QUrl &, QString &)), this, SLOT(showSaveFilePickerDialog(QUrl &, QString &)));
+ // Check to see if local storage (cookies) is enabled.
+ if (currentPrivacyWebEngineViewPointer->localStorageEnabled) // Local storage (cookies) is enabled. Use WebEngine's downloader.
+ {
+ // Instantiate the save dialog.
+ SaveDialog *saveDialogPointer = new SaveDialog(downloadUrl, mimeTypeString, totalBytes);
- // Show the dialog.
- saveDialogPointer->show();
-}
+ // Display the save dialog.
+ int saveDialogResult = saveDialogPointer->exec();
-void TabWidget::showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName)
-{
- // Get the download location.
- QString downloadDirectory = Settings::downloadLocation();
+ // Process the save dialog results.
+ if (saveDialogResult == QDialog::Accepted) // Save was selected.
+ {
+ // Get the download directory.
+ QString downloadDirectory = Settings::downloadLocation();
- // Resolve the system download directory if specified.
- if (downloadDirectory == QStringLiteral("System Download Directory"))
- downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+ // Resolve the system download directory if specified.
+ if (downloadDirectory == QLatin1String("System Download Directory"))
+ downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
- // Create a save file dialog.
- QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory);
+ // Display a save file dialog.
+ QString saveFilePath = QFileDialog::getSaveFileName(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory + QLatin1Char('/') + suggestedFileName);
- // Tell the dialog to use a save button.
- saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave);
+ // Process the save file path.
+ if (!saveFilePath.isEmpty()) // The file save path is populated.
+ {
+ // Create a save file path file info.
+ QFileInfo saveFilePathFileInfo = QFileInfo(saveFilePath);
- // Populate the file name from the download item pointer.
- saveFileDialogPointer->selectFile(suggestedFileName);
+ // Get the canonical save path and file name.
+ QString absoluteSavePath = saveFilePathFileInfo.absolutePath();
+ QString saveFileName = saveFilePathFileInfo.fileName();
- // Prevent interaction with the parent window while the dialog is open.
- saveFileDialogPointer->setWindowModality(Qt::WindowModal);
+ // Set the download directory and file name.
+ webEngineDownloadItemPointer->setDownloadDirectory(absoluteSavePath);
+ webEngineDownloadItemPointer->setDownloadFileName(saveFileName);
- // Process the saving of the file. The save file dialog pointer must be captured directly instead of by reference or nasty crashes occur.
- auto saveFile = [saveFileDialogPointer, &downloadUrl] () {
- // Get the save location. The dialog box should only allow the selecting of one file location.
- QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0);
+ // Create a file download notification.
+ KNotification *fileDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
- // Create a file copy job. `-1` creates the file with default permissions.
- KIO::FileCopyJob *fileCopyJobPointer = KIO::file_copy(downloadUrl, saveLocation, -1, KIO::Overwrite);
+ // Set the notification title.
+ fileDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
- // Set the download job to display any error messages.
- fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true);
+ // Set the notification text.
+ fileDownloadNotificationPointer->setText(i18nc("Downloading notification text", "Downloading %1", saveFileName));
- // Start the download.
- fileCopyJobPointer->start();
- };
+ // Set the notification icon.
+ fileDownloadNotificationPointer->setIconName(QLatin1String("download"));
- // Handle clicks on the save button.
- connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile);
+ // Set the action list cancel button.
+ fileDownloadNotificationPointer->setActions(QStringList({i18nc("Download notification action","Cancel")}));
- // Show the dialog.
- saveFileDialogPointer->show();
+ // Set the notification to display indefinitely.
+ fileDownloadNotificationPointer->setFlags(KNotification::Persistent);
+
+ // Prevent the notification from being autodeleted if it is closed. Otherwise, the updates to the notification below cause a crash.
+ fileDownloadNotificationPointer->setAutoDelete(false);
+
+ // Display the notification.
+ fileDownloadNotificationPointer->sendEvent();
+
+ // Handle clicks on the cancel button.
+ connect(fileDownloadNotificationPointer, &KNotification::action1Activated, [webEngineDownloadItemPointer, saveFileName] ()
+ {
+ // Cancel the download.
+ webEngineDownloadItemPointer->cancel();
+
+ // Create a file download notification.
+ KNotification *canceledDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
+
+ // Set the notification title.
+ canceledDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
+
+ // Set the new text.
+ canceledDownloadNotificationPointer->setText(i18nc("Download canceled notification", "%1 download canceled", saveFileName));
+
+ // Set the notification icon.
+ canceledDownloadNotificationPointer->setIconName(QLatin1String("download"));
+
+ // Display the notification.
+ canceledDownloadNotificationPointer->sendEvent();
+ });
+
+ // Update the notification when the download progresses.
+ connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::downloadProgress, [fileDownloadNotificationPointer, saveFileName] (qint64 bytesReceived, qint64 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;
+
+ // 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));
+ }
+ else
+ {
+ // Set the file download notification text.
+ fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1: %2 bytes downloaded", saveFileName, bytesReceived));
+ }
+
+ // Display the updated notification.
+ fileDownloadNotificationPointer->update();
+ });
+
+ // 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] ()
+ {
+ // 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)});
+
+ // Remove the actions from the notification.
+ fileDownloadNotificationPointer->setActions(QStringList());
+
+ // Set the notification to disappear after a timeout.
+ fileDownloadNotificationPointer->setFlags(KNotification::CloseOnTimeout);
+
+ // Display the updated notification.
+ fileDownloadNotificationPointer->update();
+ });
+
+ // Start the download.
+ webEngineDownloadItemPointer->accept();
+ }
+ else // The file save path is not populated.
+ {
+ // Cancel the download.
+ webEngineDownloadItemPointer->cancel();
+ }
+ }
+ else // Cancel was selected.
+ {
+ // Cancel the download.
+ webEngineDownloadItemPointer->cancel();
+ }
+ }
+ else // Local storage (cookies) is disabled. Use KDE's native downloader.
+ // This must use the show command to launch a separate dialog which cancels WebEngine's automatic background download of the file to a temporary location.
+ {
+ // Instantiate the save dialog. `true` instructs it to use the native downloader
+ SaveDialog *saveDialogPointer = new SaveDialog(downloadUrl, mimeTypeString, totalBytes, suggestedFileName, true);
+
+ // Connect the save button.
+ connect(saveDialogPointer, SIGNAL(useNativeDownloader(QUrl &, QString &)), this, SLOT(useNativeDownloader(QUrl &, QString &)));
+
+ // Show the dialog.
+ saveDialogPointer->show();
+ }
}
void TabWidget::toggleDomStorage() const
currentPrivacyWebEngineViewPointer->reload();
}
+void TabWidget::toggleFindCaseSensitive(const QString &text)
+{
+ // Toggle find case sensitive.
+ currentPrivacyWebEngineViewPointer->findCaseSensitive = !currentPrivacyWebEngineViewPointer->findCaseSensitive;
+
+ // Set the wiping current find text selection flag.
+ wipingCurrentFindTextSelection = true;
+
+ // Wipe the previous search. Otherwise currently highlighted words will remain highlighted.
+ findText(QLatin1String(""));
+
+ // Update the find text.
+ findText(text);
+}
+
void TabWidget::toggleJavaScript() const
{
// Toggle JavaScript.
void TabWidget::toggleLocalStorage()
{
- // Toggle local storeage.
+ // Toggle local storage.
currentPrivacyWebEngineViewPointer->localStorageEnabled = !currentPrivacyWebEngineViewPointer->localStorageEnabled;
// Update the local storage action.
currentPrivacyWebEngineViewPointer->reload();
}
+void TabWidget::updateUiFromWebEngineView(const PrivacyWebEngineView *privacyWebEngineViewPointer) const
+{
+ // Only update the UI if the signal was emitted from the current privacy WebEngine.
+ if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
+ {
+ // Update the UI.
+ 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 updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
+ }
+}
+
void TabWidget::updateUiWithTabSettings()
{
// Update the current WebEngine pointers.
- currentPrivacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->currentWidget());
+ currentPrivacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(qTabWidgetPointer->currentWidget());
currentWebEngineSettingsPointer = currentPrivacyWebEngineViewPointer->settings();
currentWebEnginePagePointer = currentPrivacyWebEngineViewPointer->page();
currentWebEngineProfilePointer = currentWebEnginePagePointer->profile();
// Clear the URL line edit focus.
emit clearUrlLineEditFocus();
- // Update the UI.
+ // Update the actions.
emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
emit updateCookiesAction(currentPrivacyWebEngineViewPointer->cookieListPointer->size());
- emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
- emit updateWindowTitle(currentPrivacyWebEngineViewPointer->title());
- emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url());
emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
+ // Update the URL.
+ emit updateWindowTitle(currentPrivacyWebEngineViewPointer->title());
+ emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QLatin1String(""));
+ emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url());
+
+ // Update the find text.
+ emit updateFindText(currentPrivacyWebEngineViewPointer->findString, currentPrivacyWebEngineViewPointer->findCaseSensitive);
+ emit updateFindTextResults(currentPrivacyWebEngineViewPointer->findTextResult);
+
// Update the progress bar.
if (currentPrivacyWebEngineViewPointer->loadProgressInt >= 0)
emit showProgressBar(currentPrivacyWebEngineViewPointer->loadProgressInt);
else
emit hideProgressBar();
}
+
+void TabWidget::useNativeDownloader(QUrl &downloadUrl, QString &suggestedFileName)
+{
+ // Get the download directory.
+ QString downloadDirectory = Settings::downloadLocation();
+
+ // Resolve the system download directory if specified.
+ if (downloadDirectory == QLatin1String("System Download Directory"))
+ downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+
+ // Create a save file dialog.
+ QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory);
+
+ // Tell the dialog to use a save button.
+ saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave);
+
+ // Populate the file name from the download item pointer.
+ saveFileDialogPointer->selectFile(suggestedFileName);
+
+ // Prevent interaction with the parent window while the dialog is open.
+ saveFileDialogPointer->setWindowModality(Qt::WindowModal);
+
+ // Process the saving of the file. The save file dialog pointer must be captured directly instead of by reference or nasty crashes occur.
+ auto saveFile = [saveFileDialogPointer, downloadUrl] ()
+ {
+ // Get the save location. The dialog box should only allow the selecting of one file location.
+ QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0);
+
+ // Create a file copy job. `-1` creates the file with default permissions.
+ KIO::FileCopyJob *fileCopyJobPointer = KIO::file_copy(downloadUrl, saveLocation, -1, KIO::Overwrite);
+
+ // Set the download job to display any error messages.
+ fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true);
+
+ // Start the download.
+ fileCopyJobPointer->start();
+ };
+
+ // Handle clicks on the save button.
+ connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile);
+
+ // Show the dialog.
+ saveFileDialogPointer->show();
+}