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 removeUrlLineEditFocus, const bool backgroundTab)
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 if it is not a background tab.
342 tabWidgetPointer->setCurrentIndex(newTabIndex);
344 // Clear the URL line edit focus so that it populates correctly when opening a new tab from the context menu.
345 if (removeUrlLineEditFocus)
346 emit clearUrlLineEditFocus();
348 // Return the privacy WebEngine view pointer.
349 return privacyWebEngineViewPointer;
352 void TabWidget::applyApplicationSettings()
354 // Set the tab position.
355 if (Settings::tabsOnTop())
356 tabWidgetPointer->setTabPosition(QTabWidget::North);
358 tabWidgetPointer->setTabPosition(QTabWidget::South);
360 // Set the search engine URL.
361 searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
363 // Emit the update search engine actions signal.
364 emit updateSearchEngineActions(Settings::searchEngine(), true);
367 // 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.
368 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
369 void TabWidget::applyDomainSettingsAndReload()
371 // Apply the domain settings. `true` reloads the website.
372 applyDomainSettings(currentPrivacyWebEngineViewPointer->url().host(), true);
375 // 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.
376 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
377 void TabWidget::applyDomainSettingsWithoutReloading(const QString &hostname)
379 // Apply the domain settings `false` does not reload the website.
380 applyDomainSettings(hostname, false);
383 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
384 void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
386 // Get the record for the hostname.
387 QSqlQuery domainQuery = DomainsDatabase::getDomainQuery(hostname);
389 // Check if the hostname has domain settings.
390 if (domainQuery.isValid()) // The hostname has domain settings.
392 // Get the domain record.
393 QSqlRecord domainRecord = domainQuery.record();
395 // Store the domain settings name.
396 currentPrivacyWebEngineViewPointer->domainSettingsName = domainRecord.field(DomainsDatabase::DOMAIN_NAME).value().toString();
398 // Set the JavaScript status.
399 switch (domainRecord.field(DomainsDatabase::JAVASCRIPT).value().toInt())
401 // Set the default JavaScript status.
402 case (DomainsDatabase::SYSTEM_DEFAULT):
404 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
409 // Disable JavaScript.
410 case (DomainsDatabase::DISABLED):
412 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
417 // Enable JavaScript.
418 case (DomainsDatabase::ENABLED):
420 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
426 // Set the local storage status.
427 switch (domainRecord.field(DomainsDatabase::LOCAL_STORAGE).value().toInt())
429 // Set the default local storage status.
430 case (DomainsDatabase::SYSTEM_DEFAULT):
432 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
437 // Disable local storage.
438 case (DomainsDatabase::DISABLED):
440 currentPrivacyWebEngineViewPointer->localStorageEnabled = false;
445 // Enable local storage.
446 case (DomainsDatabase::ENABLED):
448 currentPrivacyWebEngineViewPointer->localStorageEnabled = true;
454 // Set the DOM storage status.
455 switch (domainRecord.field(DomainsDatabase::DOM_STORAGE).value().toInt())
457 // Set the default DOM storage status.
458 case (DomainsDatabase::SYSTEM_DEFAULT):
460 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
465 // Disable DOM storage.
466 case (DomainsDatabase::DISABLED):
468 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
473 // Enable DOM storage.
474 case (DomainsDatabase::ENABLED):
476 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
482 // Set the user agent.
483 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainRecord.field(DomainsDatabase::USER_AGENT).value().toString()));
485 // Check if a custom zoom factor is set.
486 if (domainRecord.field(DomainsDatabase::ZOOM_FACTOR).value().toInt())
488 // Store the current zoom factor.
489 currentZoomFactor = domainRecord.field(DomainsDatabase::CUSTOM_ZOOM_FACTOR).value().toDouble();
493 // Reset the current zoom factor.
494 currentZoomFactor = Settings::zoomFactor();
497 // Set the zoom factor. The use of `currentZoomFactor` can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
498 currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);
500 else // The hostname does not have domain settings.
502 // Reset the domain settings name.
503 currentPrivacyWebEngineViewPointer->domainSettingsName = QStringLiteral("");
505 // Set the JavaScript status.
506 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
508 // Set the local storage status.
509 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
511 // Set DOM storage. In QWebEngineSettings it is called Local Storage.
512 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
514 // Set the user agent.
515 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
517 // Store the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
518 currentZoomFactor = Settings::zoomFactor();
520 // Set the zoom factor.
521 currentPrivacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor());
525 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
526 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
527 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
528 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
529 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
530 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
532 // Reload the website if requested.
534 currentPrivacyWebEngineViewPointer->reload();
537 void TabWidget::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
539 // Store the search engine name.
540 QString searchEngineName = searchEngineActionPointer->text();
542 // Strip out any `&` characters.
543 searchEngineName.remove('&');
545 // Store the search engine string.
546 searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
548 // Update the search engine actionas.
549 emit updateSearchEngineActions(searchEngineName, false);
552 void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
554 // Get the user agent name.
555 QString userAgentName = userAgentActionPointer->text();
557 // Strip out any `&` characters.
558 userAgentName.remove('&');
560 // Apply the user agent.
561 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromTranslatedName(userAgentName));
563 // Update the user agent actions.
564 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), false);
566 // Reload the website.
567 currentPrivacyWebEngineViewPointer->reload();
570 // This can be const once <https://redmine.stoutner.com/issues/799> has been resolved.
571 void TabWidget::applyOnTheFlyZoomFactor(const double &zoomFactor)
573 // Update the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
574 currentZoomFactor = zoomFactor;
576 // Set the zoom factor.
577 currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactor);
580 void TabWidget::back() const
583 currentPrivacyWebEngineViewPointer->back();
586 void TabWidget::deleteAllCookies() const
588 // Delete all the cookies.
589 currentWebEngineCookieStorePointer->deleteAllCookies();
592 void TabWidget::deleteCookieFromStore(const QNetworkCookie &cookie) const
594 // Delete the cookie.
595 currentWebEngineCookieStorePointer->deleteCookie(cookie);
598 void TabWidget::deleteTab(const int tabIndex)
600 // Get the privacy WebEngine view.
601 PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->widget(tabIndex));
603 // Proccess the tab delete according to the number of tabs.
604 if (tabWidgetPointer->count() > 1) // There is more than one tab.
607 tabWidgetPointer->removeTab(tabIndex);
609 // Delete the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
610 delete privacyWebEngineViewPointer->page();
612 // Delete the privacy WebEngine view.
613 delete privacyWebEngineViewPointer;
615 else // There is only one tab.
617 // Close Privacy Browser.
622 void TabWidget::findPrevious(const QString &text) const
624 // Store the current text.
625 currentPrivacyWebEngineViewPointer->findString = text;
627 // Find the previous text in the current privacy WebEngine.
628 if (currentPrivacyWebEngineViewPointer->findCaseSensitive)
629 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively|QWebEnginePage::FindBackward);
631 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindBackward);
634 void TabWidget::findText(const QString &text) const
636 // Store the current text.
637 currentPrivacyWebEngineViewPointer->findString = text;
639 // Find the text in the current privacy WebEngine.
640 if (currentPrivacyWebEngineViewPointer->findCaseSensitive)
641 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively);
643 currentPrivacyWebEngineViewPointer->findText(text);
645 // Clear the currently selected text in the WebEngine page if the find text is empty.
647 currentWebEnginePagePointer->action(QWebEnginePage::Unselect)->activate(QAction::Trigger);
650 void TabWidget::findTextFinished(const QWebEngineFindTextResult &findTextResult)
652 // Update the find text UI if it wasn't simply wiping the current find text selection. Otherwise the UI temporarially flashes `0/0`.
653 if (wipingCurrentFindTextSelection) // The current selection is being wiped.
656 wipingCurrentFindTextSelection = false;
658 else // A new search has been performed.
661 currentPrivacyWebEngineViewPointer->findTextResult = findTextResult;
664 emit updateFindTextResults(findTextResult);
668 void TabWidget::forward() const
671 currentPrivacyWebEngineViewPointer->forward();
674 void TabWidget::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) const
677 emit fullScreenRequested(fullScreenRequest.toggleOn());
679 // Accept the request.
680 fullScreenRequest.accept();
683 std::list<QNetworkCookie>* TabWidget::getCookieList() const
685 // Return the current cookie list.
686 return currentPrivacyWebEngineViewPointer->cookieListPointer;
689 QString& TabWidget::getDomainSettingsName() const
691 // Return the domain settings name.
692 return currentPrivacyWebEngineViewPointer->domainSettingsName;
695 void TabWidget::home() const
697 // Load the homepage.
698 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(Settings::homepage()));
701 PrivacyWebEngineView* TabWidget::loadBlankInitialWebsite()
703 // Apply the application settings.
704 applyApplicationSettings();
706 // Return the current privacy WebEngine view pointer.
707 return currentPrivacyWebEngineViewPointer;
710 void TabWidget::loadInitialWebsite()
712 // Apply the application settings.
713 applyApplicationSettings();
715 // Get the arguments.
716 QStringList argumentsStringList = qApp->arguments();
718 // Check to see if the arguments lists contains a URL.
719 if (argumentsStringList.size() > 1)
721 // Load the URL from the arguments list.
722 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(argumentsStringList.at(1)));
726 // Load the homepage.
731 void TabWidget::loadUrlFromLineEdit(QString url) const
733 // Decide if the text is more likely to be a URL or a search.
734 if (url.startsWith("file://")) // The text is likely a file URL.
737 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
739 else if (url.contains(".")) // The text is likely a URL.
741 // Check if the URL does not start with a valid protocol.
742 if (!url.startsWith("http"))
744 // Add `https://` to the beginning of the URL.
745 url = "https://" + url;
749 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
751 else // The text is likely a search.
754 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(searchEngineUrl + url));
758 void TabWidget::mouseBack() const
760 // Go back if possible.
761 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoBack())
763 // Clear the URL line edit focus.
764 emit clearUrlLineEditFocus();
767 currentPrivacyWebEngineViewPointer->back();
771 void TabWidget::mouseForward() const
773 // Go forward if possible.
774 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoForward())
776 // Clear the URL line edit focus.
777 emit clearUrlLineEditFocus();
780 currentPrivacyWebEngineViewPointer->forward();
784 void TabWidget::pageLinkHovered(const QString &linkUrl) const
786 // Emit a signal so that the browser window can update the status bar.
787 emit linkHovered(linkUrl);
790 void TabWidget::print() const
795 // Set the resolution to be 300 dpi.
796 printer.setResolution(300);
798 // Create a printer dialog.
799 QPrintDialog printDialog(&printer, currentPrivacyWebEngineViewPointer);
801 // Display the dialog and print the page if instructed.
802 if (printDialog.exec() == QDialog::Accepted)
803 printWebpage(&printer);
806 void TabWidget::printPreview() const
811 // Set the resolution to be 300 dpi.
812 printer.setResolution(300);
814 // Create a print preview dialog.
815 QPrintPreviewDialog printPreviewDialog(&printer, currentPrivacyWebEngineViewPointer);
817 // Generate the print preview.
818 connect(&printPreviewDialog, SIGNAL(paintRequested(QPrinter *)), this, SLOT(printWebpage(QPrinter *)));
820 // Display the dialog.
821 printPreviewDialog.exec();
824 void TabWidget::printWebpage(QPrinter *printerPointer) const
826 // Create an event loop. For some reason, the print preview doesn't produce any output unless it is run inside an event loop.
827 QEventLoop eventLoop;
829 // Print the webpage, converting the callback above into a `QWebEngineCallback<bool>`.
830 // Printing requires that the printer be a pointer, not a reference, or it will crash with much cursing.
831 currentWebEnginePagePointer->print(printerPointer, [&eventLoop](bool printSuccess)
833 // Instruct the compiler to ignore the unused parameter.
844 void TabWidget::refresh() const
846 // Reload the website.
847 currentPrivacyWebEngineViewPointer->reload();
850 void TabWidget::setTabBarVisible(const bool visible) const
852 // Set the tab bar visibility.
853 tabWidgetPointer->tabBar()->setVisible(visible);
856 void TabWidget::showSaveDialog(QWebEngineDownloadItem *downloadItemPointer) const
858 // Instantiate the save dialog.
859 SaveDialog *saveDialogPointer = new SaveDialog(downloadItemPointer);
861 // Connect the save button.
862 connect(saveDialogPointer, SIGNAL(showSaveFilePickerDialog(QUrl &, QString &)), this, SLOT(showSaveFilePickerDialog(QUrl &, QString &)));
865 saveDialogPointer->show();
868 void TabWidget::showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName)
870 // Get the download location.
871 QString downloadDirectory = Settings::downloadLocation();
873 // Resolve the system download directory if specified.
874 if (downloadDirectory == QStringLiteral("System Download Directory"))
875 downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
877 // Create a save file dialog.
878 QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory);
880 // Tell the dialog to use a save button.
881 saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave);
883 // Populate the file name from the download item pointer.
884 saveFileDialogPointer->selectFile(suggestedFileName);
886 // Prevent interaction with the parent window while the dialog is open.
887 saveFileDialogPointer->setWindowModality(Qt::WindowModal);
889 // Process the saving of the file. The save file dialog pointer must be captured directly instead of by reference or nasty crashes occur.
890 auto saveFile = [saveFileDialogPointer, &downloadUrl] () {
891 // Get the save location. The dialog box should only allow the selecting of one file location.
892 QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0);
894 // Create a file copy job. `-1` creates the file with default permissions.
895 KIO::FileCopyJob *fileCopyJobPointer = KIO::file_copy(downloadUrl, saveLocation, -1, KIO::Overwrite);
897 // Set the download job to display any error messages.
898 fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true);
900 // Start the download.
901 fileCopyJobPointer->start();
904 // Handle clicks on the save button.
905 connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile);
908 saveFileDialogPointer->show();
911 void TabWidget::toggleDomStorage() const
913 // Toggle DOM storage.
914 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
916 // Update the DOM storage action.
917 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
919 // Reload the website.
920 currentPrivacyWebEngineViewPointer->reload();
923 void TabWidget::toggleFindCaseSensitive(const QString &text)
925 // Toggle find case sensitive.
926 currentPrivacyWebEngineViewPointer->findCaseSensitive = !currentPrivacyWebEngineViewPointer->findCaseSensitive;
928 // Set the wiping current find text selection flag.
929 wipingCurrentFindTextSelection = true;
931 // Wipe the previous search. Otherwise currently highlighted words will remain highlighted.
932 findText(QStringLiteral(""));
934 // Update the find text.
938 void TabWidget::toggleJavaScript() const
940 // Toggle JavaScript.
941 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
943 // Update the JavaScript action.
944 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
946 // Reload the website.
947 currentPrivacyWebEngineViewPointer->reload();
950 void TabWidget::toggleLocalStorage()
952 // Toggle local storeage.
953 currentPrivacyWebEngineViewPointer->localStorageEnabled = !currentPrivacyWebEngineViewPointer->localStorageEnabled;
955 // Update the local storage action.
956 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
958 // Reload the website.
959 currentPrivacyWebEngineViewPointer->reload();
962 void TabWidget::updateUiWithTabSettings()
964 // Update the current WebEngine pointers.
965 currentPrivacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->currentWidget());
966 currentWebEngineSettingsPointer = currentPrivacyWebEngineViewPointer->settings();
967 currentWebEnginePagePointer = currentPrivacyWebEngineViewPointer->page();
968 currentWebEngineProfilePointer = currentWebEnginePagePointer->profile();
969 currentWebEngineHistoryPointer = currentWebEnginePagePointer->history();
970 currentWebEngineCookieStorePointer = currentWebEngineProfilePointer->cookieStore();
972 // Clear the URL line edit focus.
973 emit clearUrlLineEditFocus();
975 // Update the actions.
976 emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
977 emit updateCookiesAction(currentPrivacyWebEngineViewPointer->cookieListPointer->size());
978 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
979 emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
980 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
981 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
982 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
983 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
986 emit updateWindowTitle(currentPrivacyWebEngineViewPointer->title());
987 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
988 emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url());
990 // Update the find text.
991 emit updateFindText(currentPrivacyWebEngineViewPointer->findString, currentPrivacyWebEngineViewPointer->findCaseSensitive);
992 emit updateFindTextResults(currentPrivacyWebEngineViewPointer->findTextResult);
994 // Update the progress bar.
995 if (currentPrivacyWebEngineViewPointer->loadProgressInt >= 0)
996 emit showProgressBar(currentPrivacyWebEngineViewPointer->loadProgressInt);
998 emit hideProgressBar();