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 // Move to the new tab.
338 tabWidgetPointer->setCurrentIndex(newTabIndex);
340 // Clear the URL line edit focus so that it populates correctly when opening a new tab from the context menu.
341 if (focusNewWebEngineView)
342 emit clearUrlLineEditFocus();
344 // Return the privacy WebEngine view pointer.
345 return privacyWebEngineViewPointer;
348 void TabWidget::applyApplicationSettings()
350 // Set the tab position.
351 if (Settings::tabsOnTop())
352 tabWidgetPointer->setTabPosition(QTabWidget::North);
354 tabWidgetPointer->setTabPosition(QTabWidget::South);
356 // Set the search engine URL.
357 searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
359 // Emit the update search engine actions signal.
360 emit updateSearchEngineActions(Settings::searchEngine(), true);
363 // 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.
364 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
365 void TabWidget::applyDomainSettingsAndReload()
367 // Apply the domain settings. `true` reloads the website.
368 applyDomainSettings(currentPrivacyWebEngineViewPointer->url().host(), true);
371 // 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.
372 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
373 void TabWidget::applyDomainSettingsWithoutReloading(const QString &hostname)
375 // Apply the domain settings `false` does not reload the website.
376 applyDomainSettings(hostname, false);
379 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
380 void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
382 // Get the record for the hostname.
383 QSqlQuery domainQuery = DomainsDatabase::getDomainQuery(hostname);
385 // Check if the hostname has domain settings.
386 if (domainQuery.isValid()) // The hostname has domain settings.
388 // Get the domain record.
389 QSqlRecord domainRecord = domainQuery.record();
391 // Store the domain settings name.
392 currentPrivacyWebEngineViewPointer->domainSettingsName = domainRecord.field(DomainsDatabase::DOMAIN_NAME).value().toString();
394 // Set the JavaScript status.
395 switch (domainRecord.field(DomainsDatabase::JAVASCRIPT).value().toInt())
397 // Set the default JavaScript status.
398 case (DomainsDatabase::SYSTEM_DEFAULT):
400 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
405 // Disable JavaScript.
406 case (DomainsDatabase::DISABLED):
408 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
413 // Enable JavaScript.
414 case (DomainsDatabase::ENABLED):
416 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
422 // Set the local storage status.
423 switch (domainRecord.field(DomainsDatabase::LOCAL_STORAGE).value().toInt())
425 // Set the default local storage status.
426 case (DomainsDatabase::SYSTEM_DEFAULT):
428 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
433 // Disable local storage.
434 case (DomainsDatabase::DISABLED):
436 currentPrivacyWebEngineViewPointer->localStorageEnabled = false;
441 // Enable local storage.
442 case (DomainsDatabase::ENABLED):
444 currentPrivacyWebEngineViewPointer->localStorageEnabled = true;
450 // Set the DOM storage status.
451 switch (domainRecord.field(DomainsDatabase::DOM_STORAGE).value().toInt())
453 // Set the default DOM storage status.
454 case (DomainsDatabase::SYSTEM_DEFAULT):
456 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
461 // Disable DOM storage.
462 case (DomainsDatabase::DISABLED):
464 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
469 // Enable DOM storage.
470 case (DomainsDatabase::ENABLED):
472 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
478 // Set the user agent.
479 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainRecord.field(DomainsDatabase::USER_AGENT).value().toString()));
481 // Check if a custom zoom factor is set.
482 if (domainRecord.field(DomainsDatabase::ZOOM_FACTOR).value().toInt())
484 // Store the current zoom factor.
485 currentZoomFactor = domainRecord.field(DomainsDatabase::CUSTOM_ZOOM_FACTOR).value().toDouble();
489 // Reset the current zoom factor.
490 currentZoomFactor = Settings::zoomFactor();
493 // Set the zoom factor. The use of `currentZoomFactor` can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
494 currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);
496 else // The hostname does not have domain settings.
498 // Reset the domain settings name.
499 currentPrivacyWebEngineViewPointer->domainSettingsName = QStringLiteral("");
501 // Set the JavaScript status.
502 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
504 // Set the local storage status.
505 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
507 // Set DOM storage. In QWebEngineSettings it is called Local Storage.
508 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
510 // Set the user agent.
511 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
513 // Store the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
514 currentZoomFactor = Settings::zoomFactor();
516 // Set the zoom factor.
517 currentPrivacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor());
521 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
522 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
523 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
524 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
525 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
526 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
528 // Reload the website if requested.
530 currentPrivacyWebEngineViewPointer->reload();
533 void TabWidget::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
535 // Store the search engine name.
536 QString searchEngineName = searchEngineActionPointer->text();
538 // Strip out any `&` characters.
539 searchEngineName.remove('&');
541 // Store the search engine string.
542 searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
544 // Update the search engine actionas.
545 emit updateSearchEngineActions(searchEngineName, false);
548 void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
550 // Get the user agent name.
551 QString userAgentName = userAgentActionPointer->text();
553 // Strip out any `&` characters.
554 userAgentName.remove('&');
556 // Apply the user agent.
557 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromTranslatedName(userAgentName));
559 // Update the user agent actions.
560 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), false);
562 // Reload the website.
563 currentPrivacyWebEngineViewPointer->reload();
566 // This can be const once <https://redmine.stoutner.com/issues/799> has been resolved.
567 void TabWidget::applyOnTheFlyZoomFactor(const double &zoomFactor)
569 // Update the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
570 currentZoomFactor = zoomFactor;
572 // Set the zoom factor.
573 currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactor);
576 void TabWidget::back() const
579 currentPrivacyWebEngineViewPointer->back();
582 void TabWidget::deleteAllCookies() const
584 // Delete all the cookies.
585 currentWebEngineCookieStorePointer->deleteAllCookies();
588 void TabWidget::deleteCookieFromStore(const QNetworkCookie &cookie) const
590 // Delete the cookie.
591 currentWebEngineCookieStorePointer->deleteCookie(cookie);
594 void TabWidget::deleteTab(const int tabIndex)
596 // Get the privacy WebEngine view.
597 PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->widget(tabIndex));
599 // Proccess the tab delete according to the number of tabs.
600 if (tabWidgetPointer->count() > 1) // There is more than one tab.
603 tabWidgetPointer->removeTab(tabIndex);
605 // Delete the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
606 delete privacyWebEngineViewPointer->page();
608 // Delete the privacy WebEngine view.
609 delete privacyWebEngineViewPointer;
611 else // There is only one tab.
613 // Close Privacy Browser.
618 void TabWidget::findPrevious(const QString &text) const
620 // Store the current text.
621 currentPrivacyWebEngineViewPointer->findString = text;
623 // Find the previous text in the current privacy WebEngine.
624 if (currentPrivacyWebEngineViewPointer->findCaseSensitive)
625 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively|QWebEnginePage::FindBackward);
627 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindBackward);
630 void TabWidget::findText(const QString &text) const
632 // Store the current text.
633 currentPrivacyWebEngineViewPointer->findString = text;
635 // Find the text in the current privacy WebEngine.
636 if (currentPrivacyWebEngineViewPointer->findCaseSensitive)
637 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively);
639 currentPrivacyWebEngineViewPointer->findText(text);
641 // Clear the currently selected text in the WebEngine page if the find text is empty.
643 currentWebEnginePagePointer->action(QWebEnginePage::Unselect)->activate(QAction::Trigger);
646 void TabWidget::findTextFinished(const QWebEngineFindTextResult &findTextResult)
648 // Update the find text UI if it wasn't simply wiping the current find text selection. Otherwise the UI temporarially flashes `0/0`.
649 if (wipingCurrentFindTextSelection) // The current selection is being wiped.
652 wipingCurrentFindTextSelection = false;
654 else // A new search has been performed.
657 currentPrivacyWebEngineViewPointer->findTextResult = findTextResult;
660 emit updateFindTextResults(findTextResult);
664 void TabWidget::forward() const
667 currentPrivacyWebEngineViewPointer->forward();
670 void TabWidget::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) const
673 emit fullScreenRequested(fullScreenRequest.toggleOn());
675 // Accept the request.
676 fullScreenRequest.accept();
679 std::list<QNetworkCookie>* TabWidget::getCookieList() const
681 // Return the current cookie list.
682 return currentPrivacyWebEngineViewPointer->cookieListPointer;
685 QString& TabWidget::getDomainSettingsName() const
687 // Return the domain settings name.
688 return currentPrivacyWebEngineViewPointer->domainSettingsName;
691 void TabWidget::home() const
693 // Load the homepage.
694 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(Settings::homepage()));
697 PrivacyWebEngineView* TabWidget::loadBlankInitialWebsite()
699 // Apply the application settings.
700 applyApplicationSettings();
702 // Return the current privacy WebEngine view pointer.
703 return currentPrivacyWebEngineViewPointer;
706 void TabWidget::loadInitialWebsite()
708 // Apply the application settings.
709 applyApplicationSettings();
711 // Get the arguments.
712 QStringList argumentsStringList = qApp->arguments();
714 // Check to see if the arguments lists contains a URL.
715 if (argumentsStringList.size() > 1)
717 // Load the URL from the arguments list.
718 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(argumentsStringList.at(1)));
722 // Load the homepage.
727 void TabWidget::loadUrlFromLineEdit(QString url) const
729 // Decide if the text is more likely to be a URL or a search.
730 if (url.startsWith("file://")) // The text is likely a file URL.
733 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
735 else if (url.contains(".")) // The text is likely a URL.
737 // Check if the URL does not start with a valid protocol.
738 if (!url.startsWith("http"))
740 // Add `https://` to the beginning of the URL.
741 url = "https://" + url;
745 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
747 else // The text is likely a search.
750 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(searchEngineUrl + url));
754 void TabWidget::mouseBack() const
756 // Go back if possible.
757 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoBack())
759 // Clear the URL line edit focus.
760 emit clearUrlLineEditFocus();
763 currentPrivacyWebEngineViewPointer->back();
767 void TabWidget::mouseForward() const
769 // Go forward if possible.
770 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoForward())
772 // Clear the URL line edit focus.
773 emit clearUrlLineEditFocus();
776 currentPrivacyWebEngineViewPointer->forward();
780 void TabWidget::pageLinkHovered(const QString &linkUrl) const
782 // Emit a signal so that the browser window can update the status bar.
783 emit linkHovered(linkUrl);
786 void TabWidget::print() const
791 // Set the resolution to be 300 dpi.
792 printer.setResolution(300);
794 // Create a printer dialog.
795 QPrintDialog printDialog(&printer, currentPrivacyWebEngineViewPointer);
797 // Display the dialog and print the page if instructed.
798 if (printDialog.exec() == QDialog::Accepted)
799 printWebpage(&printer);
802 void TabWidget::printPreview() const
807 // Set the resolution to be 300 dpi.
808 printer.setResolution(300);
810 // Create a print preview dialog.
811 QPrintPreviewDialog printPreviewDialog(&printer, currentPrivacyWebEngineViewPointer);
813 // Generate the print preview.
814 connect(&printPreviewDialog, SIGNAL(paintRequested(QPrinter *)), this, SLOT(printWebpage(QPrinter *)));
816 // Display the dialog.
817 printPreviewDialog.exec();
820 void TabWidget::printWebpage(QPrinter *printerPointer) const
822 // Create an event loop. For some reason, the print preview doesn't produce any output unless it is run inside an event loop.
823 QEventLoop eventLoop;
825 // Print the webpage, converting the callback above into a `QWebEngineCallback<bool>`.
826 // Printing requires that the printer be a pointer, not a reference, or it will crash with much cursing.
827 currentWebEnginePagePointer->print(printerPointer, [&eventLoop](bool printSuccess)
829 // Instruct the compiler to ignore the unused parameter.
840 void TabWidget::refresh() const
842 // Reload the website.
843 currentPrivacyWebEngineViewPointer->reload();
846 void TabWidget::setTabBarVisible(const bool visible) const
848 // Set the tab bar visibility.
849 tabWidgetPointer->tabBar()->setVisible(visible);
852 void TabWidget::showSaveDialog(QWebEngineDownloadItem *downloadItemPointer) const
854 // Instantiate the save dialog.
855 SaveDialog *saveDialogPointer = new SaveDialog(downloadItemPointer);
857 // Connect the save button.
858 connect(saveDialogPointer, SIGNAL(showSaveFilePickerDialog(QUrl &, QString &)), this, SLOT(showSaveFilePickerDialog(QUrl &, QString &)));
861 saveDialogPointer->show();
864 void TabWidget::showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName)
866 // Get the download location.
867 QString downloadDirectory = Settings::downloadLocation();
869 // Resolve the system download directory if specified.
870 if (downloadDirectory == QStringLiteral("System Download Directory"))
871 downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
873 // Create a save file dialog.
874 QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory);
876 // Tell the dialog to use a save button.
877 saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave);
879 // Populate the file name from the download item pointer.
880 saveFileDialogPointer->selectFile(suggestedFileName);
882 // Prevent interaction with the parent window while the dialog is open.
883 saveFileDialogPointer->setWindowModality(Qt::WindowModal);
885 // Process the saving of the file. The save file dialog pointer must be captured directly instead of by reference or nasty crashes occur.
886 auto saveFile = [saveFileDialogPointer, &downloadUrl] () {
887 // Get the save location. The dialog box should only allow the selecting of one file location.
888 QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0);
890 // Create a file copy job. `-1` creates the file with default permissions.
891 KIO::FileCopyJob *fileCopyJobPointer = KIO::file_copy(downloadUrl, saveLocation, -1, KIO::Overwrite);
893 // Set the download job to display any error messages.
894 fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true);
896 // Start the download.
897 fileCopyJobPointer->start();
900 // Handle clicks on the save button.
901 connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile);
904 saveFileDialogPointer->show();
907 void TabWidget::toggleDomStorage() const
909 // Toggle DOM storage.
910 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
912 // Update the DOM storage action.
913 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
915 // Reload the website.
916 currentPrivacyWebEngineViewPointer->reload();
919 void TabWidget::toggleFindCaseSensitive(const QString &text)
921 // Toggle find case sensitive.
922 currentPrivacyWebEngineViewPointer->findCaseSensitive = !currentPrivacyWebEngineViewPointer->findCaseSensitive;
924 // Set the wiping current find text selection flag.
925 wipingCurrentFindTextSelection = true;
927 // Wipe the previous search. Otherwise currently highlighted words will remain highlighted.
928 findText(QStringLiteral(""));
930 // Update the find text.
934 void TabWidget::toggleJavaScript() const
936 // Toggle JavaScript.
937 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
939 // Update the JavaScript action.
940 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
942 // Reload the website.
943 currentPrivacyWebEngineViewPointer->reload();
946 void TabWidget::toggleLocalStorage()
948 // Toggle local storeage.
949 currentPrivacyWebEngineViewPointer->localStorageEnabled = !currentPrivacyWebEngineViewPointer->localStorageEnabled;
951 // Update the local storage action.
952 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
954 // Reload the website.
955 currentPrivacyWebEngineViewPointer->reload();
958 void TabWidget::updateUiWithTabSettings()
960 // Update the current WebEngine pointers.
961 currentPrivacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->currentWidget());
962 currentWebEngineSettingsPointer = currentPrivacyWebEngineViewPointer->settings();
963 currentWebEnginePagePointer = currentPrivacyWebEngineViewPointer->page();
964 currentWebEngineProfilePointer = currentWebEnginePagePointer->profile();
965 currentWebEngineHistoryPointer = currentWebEnginePagePointer->history();
966 currentWebEngineCookieStorePointer = currentWebEngineProfilePointer->cookieStore();
968 // Clear the URL line edit focus.
969 emit clearUrlLineEditFocus();
971 // Update the actions.
972 emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
973 emit updateCookiesAction(currentPrivacyWebEngineViewPointer->cookieListPointer->size());
974 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
975 emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
976 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
977 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
978 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
979 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
982 emit updateWindowTitle(currentPrivacyWebEngineViewPointer->title());
983 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
984 emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url());
986 // Update the find text.
987 emit updateFindText(currentPrivacyWebEngineViewPointer->findString, currentPrivacyWebEngineViewPointer->findCaseSensitive);
988 emit updateFindTextResults(currentPrivacyWebEngineViewPointer->findTextResult);
990 // Update the progress bar.
991 if (currentPrivacyWebEngineViewPointer->loadProgressInt >= 0)
992 emit showProgressBar(currentPrivacyWebEngineViewPointer->loadProgressInt);
994 emit hideProgressBar();