2 * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
6 * Privacy Browser PC is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Browser PC is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser PC. If not, see <http://www.gnu.org/licenses/>.
20 // Application headers.
21 #include "TabWidget.h"
23 #include "ui_AddTabWidget.h"
24 #include "ui_TabWidget.h"
25 #include "databases/CookiesDatabase.h"
26 #include "databases/DomainsDatabase.h"
27 #include "dialogs/SaveDialog.h"
28 #include "filters/MouseEventFilter.h"
29 #include "helpers/SearchEngineHelper.h"
30 #include "helpers/UserAgentHelper.h"
31 #include "interceptors/UrlRequestInterceptor.h"
32 #include "windows/BrowserWindow.h"
34 // KDE Framework headers.
35 #include <KIO/FileCopyJob>
36 #include <KIO/JobUiDelegate>
38 // Qt toolkit headers.
40 #include <QFileDialog>
41 #include <QGraphicsScene>
42 #include <QGraphicsView>
43 #include <QPrintDialog>
44 #include <QPrintPreviewDialog>
47 // Initialize the public static variables.
48 QString TabWidget::webEngineDefaultUserAgent = QStringLiteral("");
50 // Construct the class.
51 TabWidget::TabWidget(QWidget *parent) : QWidget(parent)
53 // Instantiate the UIs.
54 Ui::TabWidget tabWidgetUi;
55 Ui::AddTabWidget addTabWidgetUi;
58 tabWidgetUi.setupUi(this);
60 // Get a handle for the tab widget.
61 tabWidgetPointer = tabWidgetUi.tabWidget;
63 // Setup the add tab UI.
64 addTabWidgetUi.setupUi(tabWidgetPointer);
66 // Get handles for the add tab widgets.
67 QWidget *addTabWidgetPointer = addTabWidgetUi.addTabQWidget;
68 QPushButton *addTabButtonPointer = addTabWidgetUi.addTabButton;
70 // Display the add tab widget.
71 tabWidgetPointer->setCornerWidget(addTabWidgetPointer);
76 // Process tab events.
77 connect(tabWidgetPointer, SIGNAL(currentChanged(int)), this, SLOT(updateUiWithTabSettings()));
78 connect(addTabButtonPointer, SIGNAL(clicked()), this, SLOT(addTab()));
79 connect(tabWidgetPointer, SIGNAL(tabCloseRequested(int)), this, SLOT(deleteTab(int)));
81 // Store a copy of the WebEngine default user agent.
82 webEngineDefaultUserAgent = currentWebEngineProfilePointer->httpUserAgent();
84 // Instantiate the mouse event filter pointer.
85 MouseEventFilter *mouseEventFilterPointer = new MouseEventFilter();
87 // Install the mouse event filter.
88 qApp->installEventFilter(mouseEventFilterPointer);
90 // Process mouse forward and back commands.
91 connect(mouseEventFilterPointer, SIGNAL(mouseBack()), this, SLOT(mouseBack()));
92 connect(mouseEventFilterPointer, SIGNAL(mouseForward()), this, SLOT(mouseForward()));
95 TabWidget::~TabWidget()
97 // Manually delete each WebEngine page.
98 for (int i = 0; i < tabWidgetPointer->count(); ++i)
100 // Get the privacy WebEngine view.
101 PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->widget(i));
103 // Deletion the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
104 delete privacyWebEngineViewPointer->page();
108 // 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.
109 void TabWidget::addCookieToStore(QNetworkCookie cookie, QWebEngineCookieStore *webEngineCookieStorePointer) const
114 // 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>
115 if (!cookie.domain().startsWith(QStringLiteral(".")))
118 url.setHost(cookie.domain());
119 url.setScheme(QStringLiteral("https"));
121 // Clear the domain from the cookie.
122 cookie.setDomain(QStringLiteral(""));
125 // Add the cookie to the store.
126 if (webEngineCookieStorePointer == nullptr)
127 currentWebEngineCookieStorePointer->setCookie(cookie, url);
129 webEngineCookieStorePointer->setCookie(cookie, url);
132 void TabWidget::addFirstTab()
134 // Create the first tab.
137 // Update the UI with the tab settings.
138 updateUiWithTabSettings();
140 // 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.
141 tabWidgetPointer->currentWidget()->setFocus();
144 PrivacyWebEngineView* TabWidget::addTab(const bool focusNewWebEngineView)
146 // Create a privacy WebEngine view.
147 PrivacyWebEngineView *privacyWebEngineViewPointer = new PrivacyWebEngineView();
150 int newTabIndex = tabWidgetPointer->addTab(privacyWebEngineViewPointer, i18nc("New tab label.", "New Tab"));
152 // Set the default tab icon.
153 tabWidgetPointer->setTabIcon(newTabIndex, defaultTabIcon);
155 // Create an off-the-record profile (the default when no profile name is specified).
156 QWebEngineProfile *webEngineProfilePointer = new QWebEngineProfile(QStringLiteral(""));
158 // Create a WebEngine page.
159 QWebEnginePage *webEnginePagePointer = new QWebEnginePage(webEngineProfilePointer);
161 // Set the WebEngine page.
162 privacyWebEngineViewPointer->setPage(webEnginePagePointer);
164 // Get handles for the web engine elements.
165 QWebEngineCookieStore *webEngineCookieStorePointer = webEngineProfilePointer->cookieStore();
166 QWebEngineSettings *webEngineSettingsPointer = webEnginePagePointer->settings();
168 // Update the URL line edit when the URL changes.
169 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::urlChanged, [privacyWebEngineViewPointer, this] (const QUrl &newUrl)
171 // Only update the UI if this is the current tab.
172 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
174 // Update the URL line edit.
175 emit updateUrlLineEdit(newUrl);
177 // Update the status of the forward and back buttons.
178 emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
179 emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
182 // 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.
183 privacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);
186 // Update the progress bar when a load is started.
187 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadStarted, [privacyWebEngineViewPointer, this] ()
189 // Store the load progress.
190 privacyWebEngineViewPointer->loadProgressInt = 0;
192 // Show the progress bar if this is the current tab.
193 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
194 emit showProgressBar(0);
197 // Update the progress bar when a load progresses.
198 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadProgress, [privacyWebEngineViewPointer, this] (const int progress)
200 // Store the load progress.
201 privacyWebEngineViewPointer->loadProgressInt = progress;
203 // Update the progress bar if this is the current tab.
204 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
205 emit showProgressBar(progress);
208 // Update the progress bar when a load finishes.
209 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadFinished, [privacyWebEngineViewPointer, this] ()
211 // Store the load progress.
212 privacyWebEngineViewPointer->loadProgressInt = -1;
214 // Hide the progress bar if this is the current tab.
215 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
216 emit hideProgressBar();
219 // Display find text results.
220 connect(webEnginePagePointer, SIGNAL(findTextFinished(const QWebEngineFindTextResult &)), this, SLOT(findTextFinished(const QWebEngineFindTextResult &)));
222 // Handle full screen requests.
223 connect(webEnginePagePointer, SIGNAL(fullScreenRequested(QWebEngineFullScreenRequest)), this, SLOT(fullScreenRequested(QWebEngineFullScreenRequest)));
225 // Listen for hovered link URLs.
226 connect(webEnginePagePointer, SIGNAL(linkHovered(const QString)), this, SLOT(pageLinkHovered(const QString)));
228 // Handle file downloads.
229 connect(webEngineProfilePointer, SIGNAL(downloadRequested(QWebEngineDownloadItem *)), this, SLOT(showSaveDialog(QWebEngineDownloadItem *)));
231 // Instantiate the URL request interceptor.
232 UrlRequestInterceptor *urlRequestInterceptorPointer = new UrlRequestInterceptor();
234 // Set the URL request interceptor.
235 webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer);
237 // Reapply the domain settings when the host changes.
238 connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings(QString)), this, SLOT(applyDomainSettingsWithoutReloading(QString)));
240 // Set the local storage filter.
241 webEngineCookieStorePointer->setCookieFilter([privacyWebEngineViewPointer](const QWebEngineCookieStore::FilterRequest &filterRequest)
243 // Block all third party local storage requests, including the sneaky ones that don't register a first party URL.
244 if (filterRequest.thirdParty || (filterRequest.firstPartyUrl == QStringLiteral("")))
246 //qDebug().noquote().nospace() << "Third-party request blocked: " << filterRequest.origin;
252 // Allow the request if local storage is enabled.
253 if (privacyWebEngineViewPointer->localStorageEnabled)
255 //qDebug().noquote().nospace() << "Request allowed by local storage: " << filterRequest.origin;
261 //qDebug().noquote().nospace() << "Request blocked by default: " << filterRequest.origin;
263 // Block any remaining local storage requests.
267 // Disable JavaScript by default (this prevetns JavaScript from being enabled on a new tab before domain settings are loaded).
268 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
270 // Don't allow JavaScript to open windows.
271 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
273 // Allow keyboard navigation.
274 webEngineSettingsPointer->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, true);
276 // Enable full screen support.
277 webEngineSettingsPointer->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
279 // Require user interaction to play media.
280 webEngineSettingsPointer->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, true);
282 // Limit WebRTC to public IP addresses.
283 webEngineSettingsPointer->setAttribute(QWebEngineSettings::WebRTCPublicInterfacesOnly, true);
285 // Update the cookies action.
286 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::updateCookiesAction, [privacyWebEngineViewPointer, this] (const int numberOfCookies)
288 // Update the cookie action if the specified privacy WebEngine view is the current privacy WebEngine view.
289 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
290 emit updateCookiesAction(numberOfCookies);
293 // Process cookie changes.
294 connect(webEngineCookieStorePointer, SIGNAL(cookieAdded(QNetworkCookie)), privacyWebEngineViewPointer, SLOT(addCookieToList(QNetworkCookie)));
295 connect(webEngineCookieStorePointer, SIGNAL(cookieRemoved(QNetworkCookie)), privacyWebEngineViewPointer, SLOT(removeCookieFromList(QNetworkCookie)));
297 // Get a list of durable cookies.
298 QList<QNetworkCookie*> *durableCookiesListPointer = CookiesDatabase::getCookies();
300 // Add the durable cookies to the store.
301 for (QNetworkCookie *cookiePointer : *durableCookiesListPointer)
302 addCookieToStore(*cookiePointer, webEngineCookieStorePointer);
304 // Update the title when it changes.
305 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::titleChanged, [this, privacyWebEngineViewPointer] (const QString &title)
307 // Get the index for this tab.
308 int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer);
310 // Update the title for this tab.
311 tabWidgetPointer->setTabText(tabIndex, title);
313 // Update the window title if this is the current tab.
314 if (tabIndex == tabWidgetPointer->currentIndex())
315 emit updateWindowTitle(title);
318 // Update the icon when it changes.
319 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::iconChanged, [privacyWebEngineViewPointer, this] (const QIcon &icon)
321 // Get the index for this tab.
322 int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer);
324 // Update the icon for this tab.
326 tabWidgetPointer->setTabIcon(tabIndex, defaultTabIcon);
328 tabWidgetPointer->setTabIcon(tabIndex, icon);
331 // Enable spell checking.
332 webEngineProfilePointer->setSpellCheckEnabled(true);
334 // Set the spell check language.
335 webEngineProfilePointer->setSpellCheckLanguages({QStringLiteral("en_US")});
337 // Populate the zoom factor. This is necessary if a URL is being loaded, like a local URL, that does not trigger `applyDomainSettings()`.
338 privacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor());
340 // Move to the new tab.
341 tabWidgetPointer->setCurrentIndex(newTabIndex);
343 // Clear the URL line edit focus so that it populates correctly when opening a new tab from the context menu.
344 if (focusNewWebEngineView)
345 emit clearUrlLineEditFocus();
347 // Return the privacy WebEngine view pointer.
348 return privacyWebEngineViewPointer;
351 void TabWidget::applyApplicationSettings()
353 // Set the tab position.
354 if (Settings::tabsOnTop())
355 tabWidgetPointer->setTabPosition(QTabWidget::North);
357 tabWidgetPointer->setTabPosition(QTabWidget::South);
359 // Set the search engine URL.
360 searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
362 // Emit the update search engine actions signal.
363 emit updateSearchEngineActions(Settings::searchEngine(), true);
366 // 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.
367 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
368 void TabWidget::applyDomainSettingsAndReload()
370 // Apply the domain settings. `true` reloads the website.
371 applyDomainSettings(currentPrivacyWebEngineViewPointer->url().host(), true);
374 // 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.
375 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
376 void TabWidget::applyDomainSettingsWithoutReloading(const QString &hostname)
378 // Apply the domain settings `false` does not reload the website.
379 applyDomainSettings(hostname, false);
382 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
383 void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
385 // Get the record for the hostname.
386 QSqlQuery domainQuery = DomainsDatabase::getDomainQuery(hostname);
388 // Check if the hostname has domain settings.
389 if (domainQuery.isValid()) // The hostname has domain settings.
391 // Get the domain record.
392 QSqlRecord domainRecord = domainQuery.record();
394 // Store the domain settings name.
395 currentPrivacyWebEngineViewPointer->domainSettingsName = domainRecord.field(DomainsDatabase::DOMAIN_NAME).value().toString();
397 // Set the JavaScript status.
398 switch (domainRecord.field(DomainsDatabase::JAVASCRIPT).value().toInt())
400 // Set the default JavaScript status.
401 case (DomainsDatabase::SYSTEM_DEFAULT):
403 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
408 // Disable JavaScript.
409 case (DomainsDatabase::DISABLED):
411 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
416 // Enable JavaScript.
417 case (DomainsDatabase::ENABLED):
419 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
425 // Set the local storage status.
426 switch (domainRecord.field(DomainsDatabase::LOCAL_STORAGE).value().toInt())
428 // Set the default local storage status.
429 case (DomainsDatabase::SYSTEM_DEFAULT):
431 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
436 // Disable local storage.
437 case (DomainsDatabase::DISABLED):
439 currentPrivacyWebEngineViewPointer->localStorageEnabled = false;
444 // Enable local storage.
445 case (DomainsDatabase::ENABLED):
447 currentPrivacyWebEngineViewPointer->localStorageEnabled = true;
453 // Set the DOM storage status.
454 switch (domainRecord.field(DomainsDatabase::DOM_STORAGE).value().toInt())
456 // Set the default DOM storage status.
457 case (DomainsDatabase::SYSTEM_DEFAULT):
459 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
464 // Disable DOM storage.
465 case (DomainsDatabase::DISABLED):
467 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
472 // Enable DOM storage.
473 case (DomainsDatabase::ENABLED):
475 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
481 // Set the user agent.
482 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainRecord.field(DomainsDatabase::USER_AGENT).value().toString()));
484 // Check if a custom zoom factor is set.
485 if (domainRecord.field(DomainsDatabase::ZOOM_FACTOR).value().toInt())
487 // Store the current zoom factor.
488 currentZoomFactor = domainRecord.field(DomainsDatabase::CUSTOM_ZOOM_FACTOR).value().toDouble();
492 // Reset the current zoom factor.
493 currentZoomFactor = Settings::zoomFactor();
496 // Set the zoom factor. The use of `currentZoomFactor` can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
497 currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);
499 else // The hostname does not have domain settings.
501 // Reset the domain settings name.
502 currentPrivacyWebEngineViewPointer->domainSettingsName = QStringLiteral("");
504 // Set the JavaScript status.
505 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
507 // Set the local storage status.
508 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
510 // Set DOM storage. In QWebEngineSettings it is called Local Storage.
511 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
513 // Set the user agent.
514 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
516 // Store the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
517 currentZoomFactor = Settings::zoomFactor();
519 // Set the zoom factor.
520 currentPrivacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor());
524 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
525 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
526 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
527 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
528 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
529 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
531 // Reload the website if requested.
533 currentPrivacyWebEngineViewPointer->reload();
536 void TabWidget::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
538 // Store the search engine name.
539 QString searchEngineName = searchEngineActionPointer->text();
541 // Strip out any `&` characters.
542 searchEngineName.remove('&');
544 // Store the search engine string.
545 searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
547 // Update the search engine actionas.
548 emit updateSearchEngineActions(searchEngineName, false);
551 void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
553 // Get the user agent name.
554 QString userAgentName = userAgentActionPointer->text();
556 // Strip out any `&` characters.
557 userAgentName.remove('&');
559 // Apply the user agent.
560 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromTranslatedName(userAgentName));
562 // Update the user agent actions.
563 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), false);
565 // Reload the website.
566 currentPrivacyWebEngineViewPointer->reload();
569 // This can be const once <https://redmine.stoutner.com/issues/799> has been resolved.
570 void TabWidget::applyOnTheFlyZoomFactor(const double &zoomFactor)
572 // Update the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
573 currentZoomFactor = zoomFactor;
575 // Set the zoom factor.
576 currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactor);
579 void TabWidget::back() const
582 currentPrivacyWebEngineViewPointer->back();
585 void TabWidget::deleteAllCookies() const
587 // Delete all the cookies.
588 currentWebEngineCookieStorePointer->deleteAllCookies();
591 void TabWidget::deleteCookieFromStore(const QNetworkCookie &cookie) const
593 // Delete the cookie.
594 currentWebEngineCookieStorePointer->deleteCookie(cookie);
597 void TabWidget::deleteTab(const int tabIndex)
599 // Get the privacy WebEngine view.
600 PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->widget(tabIndex));
602 // Proccess the tab delete according to the number of tabs.
603 if (tabWidgetPointer->count() > 1) // There is more than one tab.
606 tabWidgetPointer->removeTab(tabIndex);
608 // Delete the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
609 delete privacyWebEngineViewPointer->page();
611 // Delete the privacy WebEngine view.
612 delete privacyWebEngineViewPointer;
614 else // There is only one tab.
616 // Close Privacy Browser.
621 void TabWidget::findPrevious(const QString &text) const
623 // Store the current text.
624 currentPrivacyWebEngineViewPointer->findString = text;
626 // Find the previous text in the current privacy WebEngine.
627 if (currentPrivacyWebEngineViewPointer->findCaseSensitive)
628 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively|QWebEnginePage::FindBackward);
630 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindBackward);
633 void TabWidget::findText(const QString &text) const
635 // Store the current text.
636 currentPrivacyWebEngineViewPointer->findString = text;
638 // Find the text in the current privacy WebEngine.
639 if (currentPrivacyWebEngineViewPointer->findCaseSensitive)
640 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively);
642 currentPrivacyWebEngineViewPointer->findText(text);
644 // Clear the currently selected text in the WebEngine page if the find text is empty.
646 currentWebEnginePagePointer->action(QWebEnginePage::Unselect)->activate(QAction::Trigger);
649 void TabWidget::findTextFinished(const QWebEngineFindTextResult &findTextResult)
651 // Update the find text UI if it wasn't simply wiping the current find text selection. Otherwise the UI temporarially flashes `0/0`.
652 if (wipingCurrentFindTextSelection) // The current selection is being wiped.
655 wipingCurrentFindTextSelection = false;
657 else // A new search has been performed.
660 currentPrivacyWebEngineViewPointer->findTextResult = findTextResult;
663 emit updateFindTextResults(findTextResult);
667 void TabWidget::forward() const
670 currentPrivacyWebEngineViewPointer->forward();
673 void TabWidget::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) const
676 emit fullScreenRequested(fullScreenRequest.toggleOn());
678 // Accept the request.
679 fullScreenRequest.accept();
682 std::list<QNetworkCookie>* TabWidget::getCookieList() const
684 // Return the current cookie list.
685 return currentPrivacyWebEngineViewPointer->cookieListPointer;
688 QString& TabWidget::getDomainSettingsName() const
690 // Return the domain settings name.
691 return currentPrivacyWebEngineViewPointer->domainSettingsName;
694 void TabWidget::home() const
696 // Load the homepage.
697 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(Settings::homepage()));
700 PrivacyWebEngineView* TabWidget::loadBlankInitialWebsite()
702 // Apply the application settings.
703 applyApplicationSettings();
705 // Return the current privacy WebEngine view pointer.
706 return currentPrivacyWebEngineViewPointer;
709 void TabWidget::loadInitialWebsite()
711 // Apply the application settings.
712 applyApplicationSettings();
714 // Get the arguments.
715 QStringList argumentsStringList = qApp->arguments();
717 // Check to see if the arguments lists contains a URL.
718 if (argumentsStringList.size() > 1)
720 // Load the URL from the arguments list.
721 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(argumentsStringList.at(1)));
725 // Load the homepage.
730 void TabWidget::loadUrlFromLineEdit(QString url) const
732 // Decide if the text is more likely to be a URL or a search.
733 if (url.startsWith("file://")) // The text is likely a file URL.
736 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
738 else if (url.contains(".")) // The text is likely a URL.
740 // Check if the URL does not start with a valid protocol.
741 if (!url.startsWith("http"))
743 // Add `https://` to the beginning of the URL.
744 url = "https://" + url;
748 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
750 else // The text is likely a search.
753 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(searchEngineUrl + url));
757 void TabWidget::mouseBack() const
759 // Go back if possible.
760 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoBack())
762 // Clear the URL line edit focus.
763 emit clearUrlLineEditFocus();
766 currentPrivacyWebEngineViewPointer->back();
770 void TabWidget::mouseForward() const
772 // Go forward if possible.
773 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoForward())
775 // Clear the URL line edit focus.
776 emit clearUrlLineEditFocus();
779 currentPrivacyWebEngineViewPointer->forward();
783 void TabWidget::pageLinkHovered(const QString &linkUrl) const
785 // Emit a signal so that the browser window can update the status bar.
786 emit linkHovered(linkUrl);
789 void TabWidget::print() const
794 // Set the resolution to be 300 dpi.
795 printer.setResolution(300);
797 // Create a printer dialog.
798 QPrintDialog printDialog(&printer, currentPrivacyWebEngineViewPointer);
800 // Display the dialog and print the page if instructed.
801 if (printDialog.exec() == QDialog::Accepted)
802 printWebpage(&printer);
805 void TabWidget::printPreview() const
810 // Set the resolution to be 300 dpi.
811 printer.setResolution(300);
813 // Create a print preview dialog.
814 QPrintPreviewDialog printPreviewDialog(&printer, currentPrivacyWebEngineViewPointer);
816 // Generate the print preview.
817 connect(&printPreviewDialog, SIGNAL(paintRequested(QPrinter *)), this, SLOT(printWebpage(QPrinter *)));
819 // Display the dialog.
820 printPreviewDialog.exec();
823 void TabWidget::printWebpage(QPrinter *printerPointer) const
825 // Create an event loop. For some reason, the print preview doesn't produce any output unless it is run inside an event loop.
826 QEventLoop eventLoop;
828 // Print the webpage, converting the callback above into a `QWebEngineCallback<bool>`.
829 // Printing requires that the printer be a pointer, not a reference, or it will crash with much cursing.
830 currentWebEnginePagePointer->print(printerPointer, [&eventLoop](bool printSuccess)
832 // Instruct the compiler to ignore the unused parameter.
843 void TabWidget::refresh() const
845 // Reload the website.
846 currentPrivacyWebEngineViewPointer->reload();
849 void TabWidget::setTabBarVisible(const bool visible) const
851 // Set the tab bar visibility.
852 tabWidgetPointer->tabBar()->setVisible(visible);
855 void TabWidget::showSaveDialog(QWebEngineDownloadItem *downloadItemPointer) const
857 // Instantiate the save dialog.
858 SaveDialog *saveDialogPointer = new SaveDialog(downloadItemPointer);
860 // Connect the save button.
861 connect(saveDialogPointer, SIGNAL(showSaveFilePickerDialog(QUrl &, QString &)), this, SLOT(showSaveFilePickerDialog(QUrl &, QString &)));
864 saveDialogPointer->show();
867 void TabWidget::showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName)
869 // Get the download location.
870 QString downloadDirectory = Settings::downloadLocation();
872 // Resolve the system download directory if specified.
873 if (downloadDirectory == QStringLiteral("System Download Directory"))
874 downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
876 // Create a save file dialog.
877 QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory);
879 // Tell the dialog to use a save button.
880 saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave);
882 // Populate the file name from the download item pointer.
883 saveFileDialogPointer->selectFile(suggestedFileName);
885 // Prevent interaction with the parent window while the dialog is open.
886 saveFileDialogPointer->setWindowModality(Qt::WindowModal);
888 // Process the saving of the file. The save file dialog pointer must be captured directly instead of by reference or nasty crashes occur.
889 auto saveFile = [saveFileDialogPointer, &downloadUrl] () {
890 // Get the save location. The dialog box should only allow the selecting of one file location.
891 QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0);
893 // Create a file copy job. `-1` creates the file with default permissions.
894 KIO::FileCopyJob *fileCopyJobPointer = KIO::file_copy(downloadUrl, saveLocation, -1, KIO::Overwrite);
896 // Set the download job to display any error messages.
897 fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true);
899 // Start the download.
900 fileCopyJobPointer->start();
903 // Handle clicks on the save button.
904 connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile);
907 saveFileDialogPointer->show();
910 void TabWidget::toggleDomStorage() const
912 // Toggle DOM storage.
913 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
915 // Update the DOM storage action.
916 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
918 // Reload the website.
919 currentPrivacyWebEngineViewPointer->reload();
922 void TabWidget::toggleFindCaseSensitive(const QString &text)
924 // Toggle find case sensitive.
925 currentPrivacyWebEngineViewPointer->findCaseSensitive = !currentPrivacyWebEngineViewPointer->findCaseSensitive;
927 // Set the wiping current find text selection flag.
928 wipingCurrentFindTextSelection = true;
930 // Wipe the previous search. Otherwise currently highlighted words will remain highlighted.
931 findText(QStringLiteral(""));
933 // Update the find text.
937 void TabWidget::toggleJavaScript() const
939 // Toggle JavaScript.
940 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
942 // Update the JavaScript action.
943 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
945 // Reload the website.
946 currentPrivacyWebEngineViewPointer->reload();
949 void TabWidget::toggleLocalStorage()
951 // Toggle local storeage.
952 currentPrivacyWebEngineViewPointer->localStorageEnabled = !currentPrivacyWebEngineViewPointer->localStorageEnabled;
954 // Update the local storage action.
955 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
957 // Reload the website.
958 currentPrivacyWebEngineViewPointer->reload();
961 void TabWidget::updateUiWithTabSettings()
963 // Update the current WebEngine pointers.
964 currentPrivacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->currentWidget());
965 currentWebEngineSettingsPointer = currentPrivacyWebEngineViewPointer->settings();
966 currentWebEnginePagePointer = currentPrivacyWebEngineViewPointer->page();
967 currentWebEngineProfilePointer = currentWebEnginePagePointer->profile();
968 currentWebEngineHistoryPointer = currentWebEnginePagePointer->history();
969 currentWebEngineCookieStorePointer = currentWebEngineProfilePointer->cookieStore();
971 // Clear the URL line edit focus.
972 emit clearUrlLineEditFocus();
974 // Update the actions.
975 emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
976 emit updateCookiesAction(currentPrivacyWebEngineViewPointer->cookieListPointer->size());
977 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
978 emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
979 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
980 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
981 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
982 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
985 emit updateWindowTitle(currentPrivacyWebEngineViewPointer->title());
986 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
987 emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url());
989 // Update the find text.
990 emit updateFindText(currentPrivacyWebEngineViewPointer->findString, currentPrivacyWebEngineViewPointer->findCaseSensitive);
991 emit updateFindTextResults(currentPrivacyWebEngineViewPointer->findTextResult);
993 // Update the progress bar.
994 if (currentPrivacyWebEngineViewPointer->loadProgressInt >= 0)
995 emit showProgressBar(currentPrivacyWebEngineViewPointer->loadProgressInt);
997 emit hideProgressBar();