X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserPC.git;a=blobdiff_plain;f=src%2Fwidgets%2FTabWidget.cpp;h=ff8563c96a9b5d8b311aca160f51b3098e5ab41c;hp=3a1b54f33f2028f411b5eefadb14d346f794bb13;hb=refs%2Fheads%2Fmaster;hpb=aa9a6b20db5e2c808b390b62704f28bd649e283c diff --git a/src/widgets/TabWidget.cpp b/src/widgets/TabWidget.cpp index 3a1b54f..f65c79e 100644 --- a/src/widgets/TabWidget.cpp +++ b/src/widgets/TabWidget.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -18,38 +18,58 @@ */ // Application headers. +#include "DevToolsWebEngineView.h" #include "TabWidget.h" #include "Settings.h" #include "ui_AddTabWidget.h" +#include "ui_Tab.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 #include +#include // Qt toolkit headers. #include #include #include #include +#include #include #include #include // Initialize the public static variables. -QString TabWidget::webEngineDefaultUserAgent = QStringLiteral(""); +QString TabWidget::webEngineDefaultUserAgent = QLatin1String(""); // Construct the class. -TabWidget::TabWidget(QWidget *parent) : QWidget(parent) +TabWidget::TabWidget(QWidget *windowPointer) : QWidget(windowPointer) { + // Create a QProcess to check if KDE is running. + QProcess *checkIfRunningKdeQProcessPointer = new QProcess(); + + // Create an argument string list that contains `ksmserver` (KDE Session Manager). + QStringList argument = QStringList(QLatin1String("ksmserver")); + + // Run `pidof` to check for the presence of `ksmserver`. + checkIfRunningKdeQProcessPointer->start(QLatin1String("pidof"), argument); + + // Monitor any standard output. + connect(checkIfRunningKdeQProcessPointer, &QProcess::readyReadStandardOutput, [this] + { + // If there is any standard output, `ksmserver` is running. + isRunningKde = true; + }); + + // Instantiate the user agent helper. + userAgentHelperPointer = new UserAgentHelper(); + // Instantiate the UIs. Ui::TabWidget tabWidgetUi; Ui::AddTabWidget addTabWidgetUi; @@ -58,25 +78,34 @@ TabWidget::TabWidget(QWidget *parent) : QWidget(parent) 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); + + // Create the loading favorite icon movie. + loadingFavoriteIconMoviePointer = new QMovie(); + + // Set the loading favorite icon movie file name. + loadingFavoriteIconMoviePointer->setFileName(QStringLiteral(":/icons/loading.gif")); + + // Stop the loading favorite icon movie if the window is destroyed. Otherwise, the app will crash if there is more than one window open and a window is closed while at tab is loading. + connect(windowPointer, SIGNAL(destroyed()), this, SLOT(stopLoadingFavoriteIconMovie())); // 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(); @@ -94,32 +123,40 @@ TabWidget::TabWidget(QWidget *parent) : QWidget(parent) TabWidget::~TabWidget() { + // Get the number of tabs. + int numberOfTabs = qTabWidgetPointer->count(); + // Manually delete each WebEngine page. - for (int i = 0; i < tabWidgetPointer->count(); ++i) + for (int i = 0; i < numberOfTabs; ++i) { - // Get the privacy WebEngine view. - PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast(tabWidgetPointer->widget(i)); + // Get the tab splitter widget. + QWidget *tabSplitterWidgetPointer = qTabWidgetPointer->widget(i); + + // Get the WebEngine views. + PrivacyWebEngineView *privacyWebEngineViewPointer = tabSplitterWidgetPointer->findChild(); + DevToolsWebEngineView *devToolsWebEngineViewPointer = tabSplitterWidgetPointer->findChild(); - // Deletion the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !` + // Deletion the WebEngine pages to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !` delete privacyWebEngineViewPointer->page(); + delete devToolsWebEngineViewPointer->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. - 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. @@ -138,35 +175,60 @@ void TabWidget::addFirstTab() 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 adjacent, const bool backgroundTab, const QString urlString) { - // Create a privacy WebEngine view. + // Create a splitter widget. + QSplitter *splitterPointer = new QSplitter(); + + // Set the splitter to be vertical. + splitterPointer->setOrientation(Qt::Vertical); + + // Set the splitter handle size. + splitterPointer->setHandleWidth(5); + + // Create the WebEngines. PrivacyWebEngineView *privacyWebEngineViewPointer = new PrivacyWebEngineView(); + DevToolsWebEngineView *devToolsWebEngineViewPointer = new DevToolsWebEngineView(); + + // Add the WebEngines to the splitter. + splitterPointer->addWidget(privacyWebEngineViewPointer); + splitterPointer->addWidget(devToolsWebEngineViewPointer); + + // Initialize the new tab index. + int newTabIndex = 0; // Add a new tab. - int newTabIndex = tabWidgetPointer->addTab(privacyWebEngineViewPointer, i18nc("New tab label.", "New Tab")); + if (adjacent) // Add the new tab adjacent to the current tab. + newTabIndex = qTabWidgetPointer->insertTab((qTabWidgetPointer->currentIndex() + 1), splitterPointer, i18nc("New tab label.", "New Tab")); + else // Add the new tab at the end of the list. + newTabIndex = qTabWidgetPointer->addTab(splitterPointer, i18nc("New tab label.", "New Tab")); // Set the default tab icon. - tabWidgetPointer->setTabIcon(newTabIndex, defaultTabIcon); + qTabWidgetPointer->setTabIcon(newTabIndex, defaultFavoriteIcon); - // Create an off-the-record profile (the default when no profile name is specified). - QWebEngineProfile *webEngineProfilePointer = new QWebEngineProfile(QStringLiteral("")); + // Get handles for the WebEngine components. + QWebEnginePage *webEnginePagePointer = privacyWebEngineViewPointer->page(); + QWebEngineProfile *webEngineProfilePointer = webEnginePagePointer->profile(); + QWebEngineCookieStore *webEngineCookieStorePointer = webEngineProfilePointer->cookieStore(); + QWebEngineSettings *webEngineSettingsPointer = webEnginePagePointer->settings(); - // Create a WebEngine page. - QWebEnginePage *webEnginePagePointer = new QWebEnginePage(webEngineProfilePointer); + // Set the development tools WebEngine. This must be done here to preserve the bottom half of the window as the initial development tools size. + webEnginePagePointer->setDevToolsPage(devToolsWebEngineViewPointer->page()); - // Set the WebEngine page. - privacyWebEngineViewPointer->setPage(webEnginePagePointer); + // Initially hide the development tools WebEngine. + devToolsWebEngineViewPointer->setVisible(false); - // Get handles for the web engine elements. - QWebEngineCookieStore *webEngineCookieStorePointer = webEngineProfilePointer->cookieStore(); - QWebEngineSettings *webEngineSettingsPointer = webEnginePagePointer->settings(); + // Initially disable the development tools WebEngine. + webEnginePagePointer->setDevToolsPage(nullptr); + + // Disable JavaScript on the development tools WebEngine to prevent error messages from being written to the console. + devToolsWebEngineViewPointer->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false); // Update the URL line edit when the URL changes. - connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::urlChanged, [privacyWebEngineViewPointer, this] (const QUrl &newUrl) + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::urlChanged, [this, privacyWebEngineViewPointer] (const QUrl &newUrl) { // Only update the UI if this is the current tab. if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer) @@ -178,24 +240,71 @@ PrivacyWebEngineView* TabWidget::addTab(const bool focusNewWebEngineView) emit updateBackAction(currentWebEngineHistoryPointer->canGoBack()); emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward()); } + }); + + // Update the title when it changes. + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::titleChanged, [this, splitterPointer] (const QString &title) + { + // Get the index for this tab. + int tabIndex = qTabWidgetPointer->indexOf(splitterPointer); + + // Update the title for this tab. + qTabWidgetPointer->setTabText(tabIndex, title); + + // Update the window title if this is the current tab. + if (tabIndex == qTabWidgetPointer->currentIndex()) + emit updateWindowTitle(title); + }); + + // Connect the loading favorite icon movie to the tab icon. + connect(loadingFavoriteIconMoviePointer, &QMovie::frameChanged, [this, splitterPointer, privacyWebEngineViewPointer] + { + // Get the index for this tab. + int tabIndex = qTabWidgetPointer->indexOf(splitterPointer); + + // Display the loading favorite icon if this tab is loading. + if (privacyWebEngineViewPointer->isLoading) + qTabWidgetPointer->setTabIcon(tabIndex, loadingFavoriteIconMoviePointer->currentPixmap()); + }); + + // Update the icon when it changes. + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::iconChanged, [this, splitterPointer, privacyWebEngineViewPointer] (const QIcon &newFavoriteIcon) + { + // Store the favorite icon in the privacy web engine view. + if (newFavoriteIcon.isNull()) + privacyWebEngineViewPointer->favoriteIcon = defaultFavoriteIcon; + else + privacyWebEngineViewPointer->favoriteIcon = newFavoriteIcon; - // Reapply the zoom factor. This is a bug in QWebEngineView that resets the zoom with every load. It can be removed once is fixed. - privacyWebEngineViewPointer->setZoomFactor(currentZoomFactor); + // Get the index for this tab. + int tabIndex = qTabWidgetPointer->indexOf(splitterPointer); + + // Update the icon for this tab. + if (newFavoriteIcon.isNull()) + qTabWidgetPointer->setTabIcon(tabIndex, defaultFavoriteIcon); + else + qTabWidgetPointer->setTabIcon(tabIndex, newFavoriteIcon); }); - // Update the progress bar when a load is started. - connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadStarted, [privacyWebEngineViewPointer, this] () + // Update the progress bar and the favorite icon when a load is started. + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadStarted, [this, privacyWebEngineViewPointer] () { + // Set the privacy web engine view to be loading. + privacyWebEngineViewPointer->isLoading = true; + // Store the load progress. privacyWebEngineViewPointer->loadProgressInt = 0; // Show the progress bar if this is the current tab. if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer) emit showProgressBar(0); + + // Start the loading favorite icon movie. + loadingFavoriteIconMoviePointer->start(); }); // Update the progress bar when a load progresses. - connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadProgress, [privacyWebEngineViewPointer, this] (const int progress) + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadProgress, [this, privacyWebEngineViewPointer] (const int progress) { // Store the load progress. privacyWebEngineViewPointer->loadProgressInt = progress; @@ -206,14 +315,78 @@ PrivacyWebEngineView* TabWidget::addTab(const bool focusNewWebEngineView) }); // Update the progress bar when a load finishes. - connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadFinished, [privacyWebEngineViewPointer, this] () + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadFinished, [this, splitterPointer, privacyWebEngineViewPointer] () { + // Set the privacy web engine view to be not loading. + privacyWebEngineViewPointer->isLoading = false; + // Store the load progress. privacyWebEngineViewPointer->loadProgressInt = -1; // Hide the progress bar if this is the current tab. if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer) emit hideProgressBar(); + + // Get the index for this tab. + int tabIndex = qTabWidgetPointer->indexOf(splitterPointer); + + // Display the current favorite icon + qTabWidgetPointer->setTabIcon(tabIndex, privacyWebEngineViewPointer->favoriteIcon); + + // Create a no tabs loading variable. + bool noTabsLoading = true; + + // Get the number of tabs. + int numberOfTabs = qTabWidgetPointer->count(); + + // Check to see if any other tabs are loading. + for (int i = 0; i < numberOfTabs; i++) + { + // Get the privacy WebEngine view for the tab. + PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild(); + + // Check to see if it is currently loading. If at least one tab is loading, this flag will end up being marked `false` when the for loop has finished. + if (privacyWebEngineViewPointer->isLoading) + noTabsLoading = false; + } + + // Stop the loading favorite icon movie if there are no loading tabs. + if (noTabsLoading) + loadingFavoriteIconMoviePointer->stop(); + }); + + // Display HTTP Ping blocked dialogs. + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::displayHttpPingBlockedDialog, [this, privacyWebEngineViewPointer] (const QString &httpPingUrl) + { + // Only display the HTTP Ping blocked dialog if this is the current tab. + if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer) + { + // Instantiate an HTTP ping blocked message box. + QMessageBox httpPingBlockedMessageBox; + + // Set the icon. + httpPingBlockedMessageBox.setIcon(QMessageBox::Information); + + // Set the window title. + httpPingBlockedMessageBox.setWindowTitle(i18nc("HTTP Ping blocked dialog title", "HTTP Ping Blocked")); + + // Set the text. + httpPingBlockedMessageBox.setText(i18nc("HTTP Ping blocked dialog text", "This request has been blocked because it sends a naughty HTTP ping to %1.", httpPingUrl)); + + // Set the standard button. + httpPingBlockedMessageBox.setStandardButtons(QMessageBox::Ok); + + // Display the message box. + httpPingBlockedMessageBox.exec(); + } + }); + + // Update the zoom actions when changed by CTRL-Scrolling. This can be modified when is fixed. + connect(webEnginePagePointer, &QWebEnginePage::contentsSizeChanged, [webEnginePagePointer, this] () + { + // Only update the zoom actions if this is the current tab. + if (webEnginePagePointer == currentWebEnginePagePointer) + emit updateZoomActions(webEnginePagePointer->zoomFactor()); }); // Display find text results. @@ -228,15 +401,6 @@ PrivacyWebEngineView* TabWidget::addTab(const bool focusNewWebEngineView) // 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) { @@ -264,14 +428,14 @@ PrivacyWebEngineView* TabWidget::addTab(const bool focusNewWebEngineView) 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. webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false); - // Allow keyboard navigation. - webEngineSettingsPointer->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, true); + // Allow keyboard navigation between links and input fields. + webEngineSettingsPointer->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, Settings::spatialNavigation()); // Enable full screen support. webEngineSettingsPointer->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); @@ -282,8 +446,14 @@ PrivacyWebEngineView* TabWidget::addTab(const bool focusNewWebEngineView) // 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. + webEngineSettingsPointer->setAttribute(QWebEngineSettings::PluginsEnabled, true); + // Update the cookies action. - connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::updateCookiesAction, [privacyWebEngineViewPointer, this] (const int numberOfCookies) + connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::updateCookiesAction, [this, privacyWebEngineViewPointer] (const int numberOfCookies) { // Update the cookie action if the specified privacy WebEngine view is the current privacy WebEngine view. if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer) @@ -301,46 +471,29 @@ PrivacyWebEngineView* TabWidget::addTab(const bool focusNewWebEngineView) for (QNetworkCookie *cookiePointer : *durableCookiesListPointer) addCookieToStore(*cookiePointer, webEngineCookieStorePointer); - // Update the title when it changes. - connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::titleChanged, [this, privacyWebEngineViewPointer] (const QString &title) - { - // Get the index for this tab. - int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer); - - // Update the title for this tab. - tabWidgetPointer->setTabText(tabIndex, title); - - // Update the window title if this is the current tab. - if (tabIndex == tabWidgetPointer->currentIndex()) - emit updateWindowTitle(title); - }); - - // Update the icon when it changes. - connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::iconChanged, [privacyWebEngineViewPointer, this] (const QIcon &icon) - { - // Get the index for this tab. - int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer); - - // Update the icon for this tab. - if (icon.isNull()) - tabWidgetPointer->setTabIcon(tabIndex, defaultTabIcon); - else - tabWidgetPointer->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(); + if (urlString != nullptr) + privacyWebEngineViewPointer->load(QUrl::fromUserInput(urlString)); + // Return the privacy WebEngine view pointer. return privacyWebEngineViewPointer; } @@ -349,9 +502,21 @@ void TabWidget::applyApplicationSettings() { // Set the tab position. if (Settings::tabsOnTop()) - tabWidgetPointer->setTabPosition(QTabWidget::North); + qTabWidgetPointer->setTabPosition(QTabWidget::North); else - tabWidgetPointer->setTabPosition(QTabWidget::South); + qTabWidgetPointer->setTabPosition(QTabWidget::South); + + // Get the number of tabs. + int numberOfTabs = qTabWidgetPointer->count(); + + // Apply the spatial navigation settings to each WebEngine. + for (int i = 0; i < numberOfTabs; ++i) { + // Get the WebEngine view pointer. + PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild(); + + // Apply the spatial navigation settings to each page. + privacyWebEngineViewPointer->page()->settings()->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, Settings::spatialNavigation()); + } // Set the search engine URL. searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine()); @@ -360,174 +525,19 @@ void TabWidget::applyApplicationSettings() 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 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 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 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 has been resolved. - currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor); - } - else // The hostname does not have domain settings. - { - // Reset the domain settings name. - currentPrivacyWebEngineViewPointer->domainSettingsName = QStringLiteral(""); + // Get the number of tabs. + int numberOfTabs = qTabWidgetPointer->count(); - // Set the JavaScript status. - currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled()); + // Apply the domain settings to each WebEngine. + for (int i = 0; i < numberOfTabs; ++i) { + // Get the WebEngine view pointer. + PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild(); - // 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 has been resolved. - currentZoomFactor = Settings::zoomFactor(); - - // Set the zoom factor. - currentPrivacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor()); + // Apply the spatial navigation settings to each page. + privacyWebEngineViewPointer->applyDomainSettings(privacyWebEngineViewPointer->url().host(), true); } - - // 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(); } void TabWidget::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer) @@ -541,7 +551,7 @@ 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); } @@ -554,7 +564,7 @@ void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const 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); @@ -563,14 +573,32 @@ void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const currentPrivacyWebEngineViewPointer->reload(); } -// This can be const once has been resolved. -void TabWidget::applyOnTheFlyZoomFactor(const double &zoomFactor) +void TabWidget::applyOnTheFlyZoomFactor(const double zoomFactorDouble) const { - // Update the current zoom factor. This can be removed once has been resolved. - currentZoomFactor = zoomFactor; - // Set the zoom factor. - currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactor); + currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactorDouble); +} + +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 *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild(); + + // Get the WebEngine page pointer. + QWebEnginePage *webEnginePagePointer = privacyWebEngineViewPointer->page(); + + // Get the WebEngine profile pointer. + QWebEngineProfile *webEngineProfilePointer = webEnginePagePointer->profile(); + + // Set the spell check languages. + webEngineProfilePointer->setSpellCheckLanguages(Settings::spellCheckLanguages()); + } } void TabWidget::back() const @@ -593,20 +621,29 @@ void TabWidget::deleteCookieFromStore(const QNetworkCookie &cookie) const void TabWidget::deleteTab(const int tabIndex) { - // Get the privacy WebEngine view. - PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast(tabWidgetPointer->widget(tabIndex)); + // Get the tab splitter widget. + QWidget *tabSplitterWidgetPointer = qTabWidgetPointer->widget(tabIndex); - // Proccess the tab delete according to the number of tabs. - if (tabWidgetPointer->count() > 1) // There is more than one tab. + // Get the WebEngine views. + PrivacyWebEngineView *privacyWebEngineViewPointer = tabSplitterWidgetPointer->findChild(); + DevToolsWebEngineView *devToolsWebEngineViewPointer = tabSplitterWidgetPointer->findChild(); + + // 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); + // Remove the tab. + qTabWidgetPointer->removeTab(tabIndex); - // Delete the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !` + // Delete the WebEngine pages to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !` delete privacyWebEngineViewPointer->page(); + delete devToolsWebEngineViewPointer->page(); - // Delete the privacy WebEngine view. + // Delete the WebEngine views. delete privacyWebEngineViewPointer; + delete devToolsWebEngineViewPointer; + + // Delete the tab splitter widget. + delete tabSplitterWidgetPointer; } else // There is only one tab. { @@ -645,7 +682,7 @@ void TabWidget::findText(const QString &text) const 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`. + // Update the find text UI if it wasn't simply wiping the current find text selection. Otherwise the UI temporarily flashes `0/0`. if (wipingCurrentFindTextSelection) // The current selection is being wiped. { // Reset the flag. @@ -682,6 +719,30 @@ std::list* TabWidget::getCookieList() const return currentPrivacyWebEngineViewPointer->cookieListPointer; } +QIcon TabWidget::getCurrentTabFavoritIcon() const +{ + // Return the current Privacy WebEngine favorite icon. + return currentPrivacyWebEngineViewPointer->favoriteIcon; +} + +QString TabWidget::getCurrentTabTitle() const +{ + // Return the current Privacy WebEngine title. + return currentPrivacyWebEngineViewPointer->title(); +} + +QString TabWidget::getCurrentTabUrl() const +{ + // Return the current Privacy WebEngine URL as a string. + return currentPrivacyWebEngineViewPointer->url().toString(); +} + +QString TabWidget::getCurrentUserAgent() const +{ + // Return the current WebEngine user agent. + return currentWebEngineProfilePointer->httpUserAgent(); +} + QString& TabWidget::getDomainSettingsName() const { // Return the domain settings name. @@ -727,7 +788,7 @@ 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://")) // The text is likely a file URL. + if (url.startsWith("file://") || url.startsWith("view-source:")) // The text is likely a file or view source URL. { // Load the URL. currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url)); @@ -783,6 +844,12 @@ void TabWidget::pageLinkHovered(const QString &linkUrl) const emit linkHovered(linkUrl); } +void TabWidget::stopLoadingFavoriteIconMovie() const +{ + // Stop the loading favorite icon movie. Otherwise, the browser will crash if a second window is closed while a tab in it is loading. + loadingFavoriteIconMoviePointer->stop(); +} + void TabWidget::print() const { // Create a printer. @@ -839,69 +906,263 @@ void TabWidget::printWebpage(QPrinter *printerPointer) const void TabWidget::refresh() const { + // Reset the HTTP authentication dialog counter. + currentPrivacyWebEngineViewPointer->httpAuthenticationDialogsDisplayed = 0; + // Reload the website. currentPrivacyWebEngineViewPointer->reload(); } -void TabWidget::setTabBarVisible(const bool visible) const +void TabWidget::reloadAndBypassCache() const { - // Set the tab bar visibility. - tabWidgetPointer->tabBar()->setVisible(visible); + // Reload the website, bypassing the cache. + currentWebEnginePagePointer->triggerAction(QWebEnginePage::ReloadAndBypassCache); } -void TabWidget::showSaveDialog(QWebEngineDownloadItem *downloadItemPointer) const +void TabWidget::saveArchive() { - // Instantiate the save dialog. - SaveDialog *saveDialogPointer = new SaveDialog(downloadItemPointer); + // Get the suggested file name. + QString suggestedFileName = currentPrivacyWebEngineViewPointer->title() + ".mht"; - // Connect the save button. - connect(saveDialogPointer, SIGNAL(showSaveFilePickerDialog(QUrl &, QString &)), this, SLOT(showSaveFilePickerDialog(QUrl &, QString &))); + // Get the download directory. + QString downloadDirectory = Settings::downloadDirectory(); - // Show the dialog. - saveDialogPointer->show(); + // Resolve the system download directory if specified. + if (downloadDirectory == QLatin1String("System Download Directory")) + downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + + // Get a file path from the file picker. + QString saveFilePath = QFileDialog::getSaveFileName(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory + QLatin1Char('/') + suggestedFileName); + + // Save the webpage as an archive if the file save path is populated. + if (!saveFilePath.isEmpty()) + { + // Update the download directory if specified. + if (Settings::autoUpateDownloadDirectory()) + updateDownloadDirectory(saveFilePath); + + // Set the saving archive flag. Otherwise, a second download tries to run. + savingArchive = true; + + // Save the archive. + currentWebEnginePagePointer->save(saveFilePath); + } } -void TabWidget::showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName) +void TabWidget::setTabBarVisible(const bool visible) const { - // Get the download location. - QString downloadDirectory = Settings::downloadLocation(); + // Set the tab bar visibility. + qTabWidgetPointer->tabBar()->setVisible(visible); +} - // Resolve the system download directory if specified. - if (downloadDirectory == QStringLiteral("System Download Directory")) - downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); +void TabWidget::showSaveDialog(QWebEngineDownloadItem *webEngineDownloadItemPointer) +{ + // 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(); + + // 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. + { + // Instantiate the save dialog. + SaveDialog *saveDialogPointer = new SaveDialog(this, downloadUrl, mimeTypeString, totalBytes); - // Create a save file dialog. - QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory); + // Display the save dialog. + int saveDialogResult = saveDialogPointer->exec(); - // Tell the dialog to use a save button. - saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave); + // Process the save dialog results. + if (saveDialogResult == QDialog::Accepted) // Save was selected. + { + // Get the download directory. + QString downloadDirectory = Settings::downloadDirectory(); - // Populate the file name from the download item pointer. - saveFileDialogPointer->selectFile(suggestedFileName); + // Resolve the system download directory if specified. + if (downloadDirectory == QLatin1String("System Download Directory")) + downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); - // Prevent interaction with the parent window while the dialog is open. - saveFileDialogPointer->setWindowModality(Qt::WindowModal); + // Get a file path from the file picker. + QString saveFilePath = QFileDialog::getSaveFileName(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory + QLatin1Char('/') + suggestedFileName); - // 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); + // Process the save file path. + if (!saveFilePath.isEmpty()) // The file save path is populated. + { + // Update the download directory if specified. + if (Settings::autoUpateDownloadDirectory()) + updateDownloadDirectory(saveFilePath); - // Create a file copy job. `-1` creates the file with default permissions. - KIO::FileCopyJob *fileCopyJobPointer = KIO::file_copy(downloadUrl, saveLocation, -1, KIO::Overwrite); + // Create a save file path file info. + QFileInfo saveFilePathFileInfo = QFileInfo(saveFilePath); + + // Get the canonical save path and file name. + QString absoluteSavePath = saveFilePathFileInfo.absolutePath(); + QString saveFileName = saveFilePathFileInfo.fileName(); + + // Set the download directory and file name. + webEngineDownloadItemPointer->setDownloadDirectory(absoluteSavePath); + webEngineDownloadItemPointer->setDownloadFileName(saveFileName); + + // Create a file download notification. + KNotification *fileDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload")); + + // Set the notification title. + fileDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download")); + + // Set the notification text. + fileDownloadNotificationPointer->setText(i18nc("Downloading notification text", "Downloading %1", saveFileName)); + + // Get the download icon from the theme. + QIcon downloadIcon = QIcon::fromTheme(QLatin1String("download"), QIcon::fromTheme(QLatin1String("document-save"))); + + // Set the notification icon. + fileDownloadNotificationPointer->setIconName(downloadIcon.name()); + + // Set the action list cancel button. + fileDownloadNotificationPointer->setActions(QStringList({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] () + { + // 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(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(); + }); + + // Display the notification. + fileDownloadNotificationPointer->sendEvent(); + + // 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 // KDE is running and 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(this, downloadUrl, mimeTypeString, totalBytes, suggestedFileName, true); - // Set the download job to display any error messages. - fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true); + // Connect the save button. + connect(saveDialogPointer, SIGNAL(useNativeKdeDownloader(QUrl &, QString &)), this, SLOT(useNativeKdeDownloader(QUrl &, QString &))); - // Start the download. - fileCopyJobPointer->start(); - }; + // Show the dialog. + saveDialogPointer->show(); + } + } - // Handle clicks on the save button. - connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile); + // Reset the saving archive flag. + savingArchive = false; +} - // Show the dialog. - saveFileDialogPointer->show(); +void TabWidget::stop() const +{ + // Stop the loading of the current privacy WebEngine. + currentPrivacyWebEngineViewPointer->stop(); +} + +void TabWidget::toggleDeveloperTools(const bool enabled) const +{ + // Get a handle for the current developer tools WebEngine. + DevToolsWebEngineView *devToolsWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild(); + + if (enabled) + { + // Set the zoom factor on the development tools WebEngine. + devToolsWebEngineViewPointer->setZoomFactor(currentWebEnginePagePointer->zoomFactor()); + + // Enable the development tools. + currentWebEnginePagePointer->setDevToolsPage(devToolsWebEngineViewPointer->page()); + + // Enable JavaScript on the development tools WebEngine. + devToolsWebEngineViewPointer->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + + // Display the developer tools. + devToolsWebEngineViewPointer->setVisible(true); + } + else + { + // Disable JavaScript on the development tools WebEngine to prevent error messages from being written to the console. + devToolsWebEngineViewPointer->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false); + + // Disable the development tools. + currentWebEnginePagePointer->setDevToolsPage(nullptr); + + // Hide the developer tools. + devToolsWebEngineViewPointer->setVisible(false); + } } void TabWidget::toggleDomStorage() const @@ -925,7 +1186,7 @@ void TabWidget::toggleFindCaseSensitive(const QString &text) wipingCurrentFindTextSelection = true; // Wipe the previous search. Otherwise currently highlighted words will remain highlighted. - findText(QStringLiteral("")); + findText(QLatin1String("")); // Update the find text. findText(text); @@ -945,7 +1206,7 @@ void TabWidget::toggleJavaScript() const void TabWidget::toggleLocalStorage() { - // Toggle local storeage. + // Toggle local storage. currentPrivacyWebEngineViewPointer->localStorageEnabled = !currentPrivacyWebEngineViewPointer->localStorageEnabled; // Update the local storage action. @@ -955,10 +1216,41 @@ void TabWidget::toggleLocalStorage() currentPrivacyWebEngineViewPointer->reload(); } +void TabWidget::updateDownloadDirectory(QString newDownloadDirectory) const +{ + // Remove the file name from the save file path. + newDownloadDirectory.truncate(newDownloadDirectory.lastIndexOf(QLatin1Char('/'))); + + // Update the download location. + Settings::setDownloadDirectory(newDownloadDirectory); + + // Get a handle for the KConfig skeleton. + KConfigSkeleton *kConfigSkeletonPointer = Settings::self(); + + // Write the settings to disk. + kConfigSkeletonPointer->save(); +} + +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 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()); + } +} + void TabWidget::updateUiWithTabSettings() { // Update the current WebEngine pointers. - currentPrivacyWebEngineViewPointer = qobject_cast(tabWidgetPointer->currentWidget()); + currentPrivacyWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild(); currentWebEngineSettingsPointer = currentPrivacyWebEngineViewPointer->settings(); currentWebEnginePagePointer = currentPrivacyWebEngineViewPointer->page(); currentWebEngineProfilePointer = currentWebEnginePagePointer->profile(); @@ -968,19 +1260,24 @@ void TabWidget::updateUiWithTabSettings() // Clear the URL line edit focus. emit clearUrlLineEditFocus(); + // Get a handle for the development tools WebEngine view. + DevToolsWebEngineView *devToolsWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild(); + // Update the actions. + emit updateDefaultZoomFactor(currentPrivacyWebEngineViewPointer->defaultZoomFactor); emit updateBackAction(currentWebEngineHistoryPointer->canGoBack()); emit updateCookiesAction(currentPrivacyWebEngineViewPointer->cookieListPointer->size()); + 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 updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor()); + emit updateZoomActions(currentPrivacyWebEngineViewPointer->zoomFactor()); // Update the URL. emit updateWindowTitle(currentPrivacyWebEngineViewPointer->title()); - emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral("")); + emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QLatin1String("")); emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url()); // Update the find text. @@ -993,3 +1290,52 @@ void TabWidget::updateUiWithTabSettings() else emit hideProgressBar(); } + +void TabWidget::useNativeKdeDownloader(QUrl &downloadUrl, QString &suggestedFileName) +{ + // Get the download directory. + QString downloadDirectory = Settings::downloadDirectory(); + + // 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, this] () + { + // Get the save location. The dialog box should only allow the selecting of one file location. + QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0); + + // Update the download directory if specified. + if (Settings::autoUpateDownloadDirectory()) + updateDownloadDirectory(saveLocation.toLocalFile()); + + // 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 warning and error messages. + fileCopyJobPointer->uiDelegate()->setAutoWarningHandlingEnabled(true); + 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(); +}