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 void TabWidget::addTab()
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, SIGNAL(urlChanged(const QUrl)), this, SLOT(updateUrl(const QUrl)));
172 // Update the progress bar.
173 connect(privacyWebEngineViewPointer, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
174 connect(privacyWebEngineViewPointer, SIGNAL(loadProgress(const int)), this, SLOT(loadProgress(const int)));
175 connect(privacyWebEngineViewPointer, SIGNAL(loadFinished(const bool)), this, SLOT(loadFinished()));
177 // Handle full screen requests.
178 connect(webEnginePagePointer, SIGNAL(fullScreenRequested(QWebEngineFullScreenRequest)), this, SLOT(fullScreenRequested(QWebEngineFullScreenRequest)));
180 // Listen for hovered link URLs.
181 connect(webEnginePagePointer, SIGNAL(linkHovered(const QString)), this, SLOT(pageLinkHovered(const QString)));
183 // Handle file downloads.
184 connect(webEngineProfilePointer, SIGNAL(downloadRequested(QWebEngineDownloadItem *)), this, SLOT(showSaveDialog(QWebEngineDownloadItem *)));
186 // Instantiate the URL request interceptor.
187 UrlRequestInterceptor *urlRequestInterceptorPointer = new UrlRequestInterceptor();
189 // Set the URL request interceptor.
190 webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer);
192 // Reapply the domain settings when the host changes.
193 connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings(QString)), this, SLOT(applyDomainSettingsWithoutReloading(QString)));
195 // Set the local storage filter.
196 webEngineCookieStorePointer->setCookieFilter([privacyWebEngineViewPointer](const QWebEngineCookieStore::FilterRequest &filterRequest)
198 // Block all third party local storage requests, including the sneaky ones that don't register a first party URL.
199 if (filterRequest.thirdParty || (filterRequest.firstPartyUrl == QStringLiteral("")))
201 //qDebug().noquote().nospace() << "Third-party request blocked: " << filterRequest.origin;
207 // Allow the request if local storage is enabled.
208 if (privacyWebEngineViewPointer->localStorageEnabled)
210 //qDebug().noquote().nospace() << "Request allowed by local storage: " << filterRequest.origin;
216 //qDebug().noquote().nospace() << "Request blocked by default: " << filterRequest.origin;
218 // Block any remaining local storage requests.
222 // Disable JavaScript by default (this prevetns JavaScript from being enabled on a new tab before domain settings are loaded).
223 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
225 // Don't allow JavaScript to open windows.
226 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
228 // Allow keyboard navigation.
229 webEngineSettingsPointer->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, true);
231 // Enable full screen support.
232 webEngineSettingsPointer->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
234 // Require user interaction to play media.
235 webEngineSettingsPointer->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, true);
237 // Limit WebRTC to public IP addresses.
238 webEngineSettingsPointer->setAttribute(QWebEngineSettings::WebRTCPublicInterfacesOnly, true);
240 // Define an update cookie count lambda.
241 auto updateCookieCount = [privacyWebEngineViewPointer, this] (const int numberOfCookies)
243 // Update the cookie action if the specified privacy WebEngine view is the current privacy WebEngine view.
244 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
245 emit updateCookiesAction(numberOfCookies);
248 // Update the cookies action.
249 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::updateCookiesAction, this, updateCookieCount);
251 // Process cookie changes.
252 connect(webEngineCookieStorePointer, SIGNAL(cookieAdded(QNetworkCookie)), privacyWebEngineViewPointer, SLOT(addCookieToList(QNetworkCookie)));
253 connect(webEngineCookieStorePointer, SIGNAL(cookieRemoved(QNetworkCookie)), privacyWebEngineViewPointer, SLOT(removeCookieFromList(QNetworkCookie)));
255 // Get a list of durable cookies.
256 QList<QNetworkCookie*> *durableCookiesListPointer = CookiesDatabase::getCookies();
258 // Add the durable cookies to the store.
259 for (QNetworkCookie *cookiePointer : *durableCookiesListPointer)
260 addCookieToStore(*cookiePointer, webEngineCookieStorePointer);
262 // Define an update tab title lambda.
263 auto updateTabTitle = [privacyWebEngineViewPointer, this] (const QString &title)
265 // Get the index for this tab.
266 int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer);
268 // Update the title for this tab.
269 tabWidgetPointer->setTabText(tabIndex, title);
272 // Update the title when it changes.
273 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::titleChanged, this, updateTabTitle);
275 // Define an update tab icon lambda.
276 auto updateTabIcon = [privacyWebEngineViewPointer, this] (const QIcon &icon)
278 // Get the index for this tab.
279 int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer);
281 // Update the icon for this tab.
283 tabWidgetPointer->setTabIcon(tabIndex, defaultTabIcon);
285 tabWidgetPointer->setTabIcon(tabIndex, icon);
288 // Update the icon when it changes.
289 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::iconChanged, this, updateTabIcon);
291 // Move to the new tab.
292 tabWidgetPointer->setCurrentIndex(newTabIndex);
295 void TabWidget::applyApplicationSettings()
297 // Set the tab position.
298 if (Settings::tabsOnTop())
299 tabWidgetPointer->setTabPosition(QTabWidget::North);
301 tabWidgetPointer->setTabPosition(QTabWidget::South);
303 // Set the search engine URL.
304 searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
306 // Emit the update search engine actions signal.
307 emit updateSearchEngineActions(Settings::searchEngine(), true);
310 // 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.
311 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
312 void TabWidget::applyDomainSettingsAndReload()
314 // Apply the domain settings. `true` reloads the website.
315 applyDomainSettings(currentPrivacyWebEngineViewPointer->url().host(), true);
318 // 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.
319 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
320 void TabWidget::applyDomainSettingsWithoutReloading(const QString &hostname)
322 // Apply the domain settings `false` does not reload the website.
323 applyDomainSettings(hostname, false);
326 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
327 void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
329 // Get the record for the hostname.
330 QSqlQuery domainQuery = DomainsDatabase::getDomainQuery(hostname);
332 // Check if the hostname has domain settings.
333 if (domainQuery.isValid()) // The hostname has domain settings.
335 // Get the domain record.
336 QSqlRecord domainRecord = domainQuery.record();
338 // Store the domain settings name.
339 currentPrivacyWebEngineViewPointer->domainSettingsName = domainRecord.field(DomainsDatabase::DOMAIN_NAME).value().toString();
341 // Set the JavaScript status.
342 switch (domainRecord.field(DomainsDatabase::JAVASCRIPT).value().toInt())
344 // Set the default JavaScript status.
345 case (DomainsDatabase::SYSTEM_DEFAULT):
347 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
352 // Disable JavaScript.
353 case (DomainsDatabase::DISABLED):
355 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
360 // Enable JavaScript.
361 case (DomainsDatabase::ENABLED):
363 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
369 // Set the local storage status.
370 switch (domainRecord.field(DomainsDatabase::LOCAL_STORAGE).value().toInt())
372 // Set the default local storage status.
373 case (DomainsDatabase::SYSTEM_DEFAULT):
375 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
380 // Disable local storage.
381 case (DomainsDatabase::DISABLED):
383 currentPrivacyWebEngineViewPointer->localStorageEnabled = false;
388 // Enable local storage.
389 case (DomainsDatabase::ENABLED):
391 currentPrivacyWebEngineViewPointer->localStorageEnabled = true;
397 // Set the DOM storage status.
398 switch (domainRecord.field(DomainsDatabase::DOM_STORAGE).value().toInt())
400 // Set the default DOM storage status.
401 case (DomainsDatabase::SYSTEM_DEFAULT):
403 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
408 // Disable DOM storage.
409 case (DomainsDatabase::DISABLED):
411 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
416 // Enable DOM storage.
417 case (DomainsDatabase::ENABLED):
419 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
425 // Set the user agent.
426 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainRecord.field(DomainsDatabase::USER_AGENT).value().toString()));
428 // Check if a custom zoom factor is set.
429 if (domainRecord.field(DomainsDatabase::ZOOM_FACTOR).value().toInt())
431 // Store the current zoom factor.
432 currentZoomFactor = domainRecord.field(DomainsDatabase::CUSTOM_ZOOM_FACTOR).value().toDouble();
436 // Reset the current zoom factor.
437 currentZoomFactor = Settings::zoomFactor();
440 // Set the zoom factor. The use of `currentZoomFactor` can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
441 currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);
443 else // The hostname does not have domain settings.
445 // Reset the domain settings name.
446 currentPrivacyWebEngineViewPointer->domainSettingsName = QStringLiteral("");
448 // Set the JavaScript status.
449 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
451 // Set the local storage status.
452 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
454 // Set DOM storage. In QWebEngineSettings it is called Local Storage.
455 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
457 // Set the user agent.
458 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
460 // Store the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
461 currentZoomFactor = Settings::zoomFactor();
463 // Set the zoom factor.
464 currentPrivacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor());
468 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
469 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
470 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
471 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
472 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
473 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
475 // Reload the website if requested.
477 currentPrivacyWebEngineViewPointer->reload();
480 void TabWidget::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
482 // Store the search engine name.
483 QString searchEngineName = searchEngineActionPointer->text();
485 // Strip out any `&` characters.
486 searchEngineName.remove('&');
488 // Store the search engine string.
489 searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
491 // Update the search engine actionas.
492 emit updateSearchEngineActions(searchEngineName, false);
495 void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
497 // Get the user agent name.
498 QString userAgentName = userAgentActionPointer->text();
500 // Strip out any `&` characters.
501 userAgentName.remove('&');
503 // Apply the user agent.
504 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromTranslatedName(userAgentName));
506 // Update the user agent actions.
507 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), false);
509 // Reload the website.
510 currentPrivacyWebEngineViewPointer->reload();
513 // This can be const once <https://redmine.stoutner.com/issues/799> has been resolved.
514 void TabWidget::applyOnTheFlyZoomFactor(const double &zoomFactor)
516 // Update the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
517 currentZoomFactor = zoomFactor;
519 // Set the zoom factor.
520 currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactor);
523 void TabWidget::back() const
526 currentPrivacyWebEngineViewPointer->back();
529 void TabWidget::deleteAllCookies() const
531 // Delete all the cookies.
532 currentWebEngineCookieStorePointer->deleteAllCookies();
535 void TabWidget::deleteCookieFromStore(const QNetworkCookie &cookie) const
537 // Delete the cookie.
538 currentWebEngineCookieStorePointer->deleteCookie(cookie);
541 void TabWidget::deleteTab(const int tabIndex)
543 // Get the privacy WebEngine view.
544 PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->widget(tabIndex));
546 // Proccess the tab delete according to the number of tabs.
547 if (tabWidgetPointer->count() > 1) // There is more than one tab.
550 tabWidgetPointer->removeTab(tabIndex);
552 // Delete the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
553 delete privacyWebEngineViewPointer->page();
555 // Delete the privacy WebEngine view.
556 delete privacyWebEngineViewPointer;
558 else // There is only one tab.
560 // Close Privacy Browser.
565 void TabWidget::forward() const
568 currentPrivacyWebEngineViewPointer->forward();
571 void TabWidget::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) const
574 emit fullScreenRequested(fullScreenRequest.toggleOn());
576 // Accept the request.
577 fullScreenRequest.accept();
580 std::list<QNetworkCookie>* TabWidget::getCookieList() const
582 // Return the current cookie list.
583 return currentPrivacyWebEngineViewPointer->cookieListPointer;
586 QString& TabWidget::getDomainSettingsName() const
588 // Return the domain settings name.
589 return currentPrivacyWebEngineViewPointer->domainSettingsName;
592 void TabWidget::home() const
594 // Load the homepage.
595 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(Settings::homepage()));
598 void TabWidget::loadFinished() const
600 // Hide the progress bar.
601 emit hideProgressBar();
604 void TabWidget::loadInitialWebsite()
606 // Apply the application settings.
607 applyApplicationSettings();
609 // Get the arguments.
610 QStringList argumentsStringList = qApp->arguments();
612 // Check to see if the arguments lists contains a URL.
613 if (argumentsStringList.size() > 1)
615 // Load the URL from the arguments list.
616 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(argumentsStringList.at(1)));
620 // Load the homepage.
625 void TabWidget::loadProgress(const int &progress) const
627 // Show the progress bar.
628 emit showProgressBar(progress);
631 void TabWidget::loadStarted() const
633 // Show the progress bar.
634 emit showProgressBar(0);
637 void TabWidget::loadUrlFromLineEdit(QString url) const
639 // Decide if the text is more likely to be a URL or a search.
640 if (url.startsWith("file://")) // The text is likely a file URL.
643 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
645 else if (url.contains(".")) // The text is likely a URL.
647 // Check if the URL does not start with a valid protocol.
648 if (!url.startsWith("http"))
650 // Add `https://` to the beginning of the URL.
651 url = "https://" + url;
655 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
657 else // The text is likely a search.
660 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(searchEngineUrl + url));
664 void TabWidget::mouseBack() const
666 // Go back if possible.
667 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoBack())
669 // Clear the URL line edit focus.
670 emit clearUrlLineEditFocus();
673 currentPrivacyWebEngineViewPointer->back();
677 void TabWidget::mouseForward() const
679 // Go forward if possible.
680 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoForward())
682 // Clear the URL line edit focus.
683 emit clearUrlLineEditFocus();
686 currentPrivacyWebEngineViewPointer->forward();
690 void TabWidget::pageLinkHovered(const QString &linkUrl) const
692 // Emit a signal so that the browser window can update the status bar.
693 emit linkHovered(linkUrl);
696 void TabWidget::print() const
701 // Set the resolution to be 300 dpi.
702 printer.setResolution(300);
704 // Create a printer dialog.
705 QPrintDialog printDialog(&printer, currentPrivacyWebEngineViewPointer);
707 // Display the dialog and print the page if instructed.
708 if (printDialog.exec() == QDialog::Accepted)
709 printWebpage(&printer);
712 void TabWidget::printPreview() const
717 // Set the resolution to be 300 dpi.
718 printer.setResolution(300);
720 // Create a print preview dialog.
721 QPrintPreviewDialog printPreviewDialog(&printer, currentPrivacyWebEngineViewPointer);
723 // Generate the print preview.
724 connect(&printPreviewDialog, SIGNAL(paintRequested(QPrinter *)), this, SLOT(printWebpage(QPrinter *)));
726 // Display the dialog.
727 printPreviewDialog.exec();
730 void TabWidget::printWebpage(QPrinter *printerPointer) const
732 // Create an event loop. For some reason, the print preview doesn't produce any output unless it is run inside an event loop.
733 QEventLoop eventLoop;
735 // Print the webpage, converting the callback above into a `QWebEngineCallback<bool>`.
736 // Printing requires that the printer be a pointer, not a reference, or it will crash with much cursing.
737 currentWebEnginePagePointer->print(printerPointer, [&eventLoop](bool printSuccess)
739 // Instruct the compiler to ignore the unused parameter.
750 void TabWidget::refresh() const
752 // Reload the website.
753 currentPrivacyWebEngineViewPointer->reload();
756 void TabWidget::setTabBarVisible(const bool visible) const
758 // Set the tab bar visibility.
759 tabWidgetPointer->tabBar()->setVisible(visible);
762 void TabWidget::showSaveDialog(QWebEngineDownloadItem *downloadItemPointer) const
764 // Instantiate the save dialog.
765 SaveDialog *saveDialogPointer = new SaveDialog(downloadItemPointer);
767 // Connect the save button.
768 connect(saveDialogPointer, SIGNAL(showSaveFilePickerDialog(QUrl &, QString &)), this, SLOT(showSaveFilePickerDialog(QUrl &, QString &)));
771 saveDialogPointer->show();
774 void TabWidget::showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName)
776 // Get the download location.
777 QString downloadDirectory = Settings::downloadLocation();
779 // Resolve the system download directory if specified.
780 if (downloadDirectory == QStringLiteral("System Download Directory"))
781 downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
783 // Create a save file dialog.
784 QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory);
786 // Tell the dialog to use a save button.
787 saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave);
789 // Populate the file name from the download item pointer.
790 saveFileDialogPointer->selectFile(suggestedFileName);
792 // Prevent interaction with the parent window while the dialog is open.
793 saveFileDialogPointer->setWindowModality(Qt::WindowModal);
795 // Process the saving of the file. The save file dialog pointer must be captured directly instead of by reference or nasty crashes occur.
796 auto saveFile = [saveFileDialogPointer, &downloadUrl] () {
797 // Get the save location. The dialog box should only allow the selecting of one file location.
798 QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0);
800 // Create a file copy job. `-1` creates the file with default permissions.
801 KIO::FileCopyJob *fileCopyJobPointer = KIO::file_copy(downloadUrl, saveLocation, -1, KIO::Overwrite);
803 // Set the download job to display any error messages.
804 fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true);
806 // Start the download.
807 fileCopyJobPointer->start();
810 // Handle clicks on the save button.
811 connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile);
814 saveFileDialogPointer->show();
817 void TabWidget::toggleDomStorage() const
819 // Toggle DOM storage.
820 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
822 // Update the DOM storage action.
823 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
825 // Reload the website.
826 currentPrivacyWebEngineViewPointer->reload();
829 void TabWidget::toggleJavaScript() const
831 // Toggle JavaScript.
832 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
834 // Update the JavaScript action.
835 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
837 // Reload the website.
838 currentPrivacyWebEngineViewPointer->reload();
841 void TabWidget::toggleLocalStorage()
843 // Toggle local storeage.
844 currentPrivacyWebEngineViewPointer->localStorageEnabled = !currentPrivacyWebEngineViewPointer->localStorageEnabled;
846 // Update the local storage action.
847 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
849 // Reload the website.
850 currentPrivacyWebEngineViewPointer->reload();
853 void TabWidget::updateUiWithTabSettings()
855 // Update the current WebEngine pointers.
856 currentPrivacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->currentWidget());
857 currentWebEngineSettingsPointer = currentPrivacyWebEngineViewPointer->settings();
858 currentWebEnginePagePointer = currentPrivacyWebEngineViewPointer->page();
859 currentWebEngineProfilePointer = currentWebEnginePagePointer->profile();
860 currentWebEngineHistoryPointer = currentWebEnginePagePointer->history();
861 currentWebEngineCookieStorePointer = currentWebEngineProfilePointer->cookieStore();
863 // Clear the URL line edit focus.
864 emit clearUrlLineEditFocus();
867 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
868 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
869 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
870 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
871 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
872 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
873 emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url());
874 emit updateCookiesAction(currentPrivacyWebEngineViewPointer->cookieListPointer->size());
877 void TabWidget::updateUrl(const QUrl &url) const
879 // Update the URL line edit.
880 emit updateUrlLineEdit(url);
882 // Update the status of the forward and back buttons.
883 emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
884 emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
886 // Reapply the zoom factor. This is a bug in QWebEngineView that resets the zoom with every load. <https://redmine.stoutner.com/issues/799>
887 currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);