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()
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)));
171 // Update the progress bar.
172 connect(privacyWebEngineViewPointer, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
173 connect(privacyWebEngineViewPointer, SIGNAL(loadProgress(const int)), this, SLOT(loadProgress(const int)));
174 connect(privacyWebEngineViewPointer, SIGNAL(loadFinished(const bool)), this, SLOT(loadFinished()));
176 // Handle full screen requests.
177 connect(webEnginePagePointer, SIGNAL(fullScreenRequested(QWebEngineFullScreenRequest)), this, SLOT(fullScreenRequested(QWebEngineFullScreenRequest)));
179 // Listen for hovered link URLs.
180 connect(webEnginePagePointer, SIGNAL(linkHovered(const QString)), this, SLOT(pageLinkHovered(const QString)));
182 // Handle file downloads.
183 connect(webEngineProfilePointer, SIGNAL(downloadRequested(QWebEngineDownloadItem *)), this, SLOT(showSaveDialog(QWebEngineDownloadItem *)));
185 // Instantiate the URL request interceptor.
186 UrlRequestInterceptor *urlRequestInterceptorPointer = new UrlRequestInterceptor();
188 // Set the URL request interceptor.
189 webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer);
191 // Reapply the domain settings when the host changes.
192 connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings(QString)), this, SLOT(applyDomainSettingsWithoutReloading(QString)));
194 // Set the local storage filter.
195 webEngineCookieStorePointer->setCookieFilter([privacyWebEngineViewPointer](const QWebEngineCookieStore::FilterRequest &filterRequest)
197 // Block all third party local storage requests, including the sneaky ones that don't register a first party URL.
198 if (filterRequest.thirdParty || (filterRequest.firstPartyUrl == QStringLiteral("")))
200 //qDebug().noquote().nospace() << "Third-party request blocked: " << filterRequest.origin;
206 // Allow the request if local storage is enabled.
207 if (privacyWebEngineViewPointer->localStorageEnabled)
209 //qDebug().noquote().nospace() << "Request allowed by local storage: " << filterRequest.origin;
215 //qDebug().noquote().nospace() << "Request blocked by default: " << filterRequest.origin;
217 // Block any remaining local storage requests.
221 // Disable JavaScript by default (this prevetns JavaScript from being enabled on a new tab before domain settings are loaded).
222 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
224 // Don't allow JavaScript to open windows.
225 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
227 // Allow keyboard navigation.
228 webEngineSettingsPointer->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, true);
230 // Enable full screen support.
231 webEngineSettingsPointer->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
233 // Require user interaction to play media.
234 webEngineSettingsPointer->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, true);
236 // Limit WebRTC to public IP addresses.
237 webEngineSettingsPointer->setAttribute(QWebEngineSettings::WebRTCPublicInterfacesOnly, true);
239 // Define an update cookie count lambda.
240 auto updateCookieCount = [privacyWebEngineViewPointer, this] (const int numberOfCookies)
242 // Update the cookie action if the specified privacy WebEngine view is the current privacy WebEngine view.
243 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
244 emit updateCookiesAction(numberOfCookies);
247 // Update the cookies action.
248 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::updateCookiesAction, this, updateCookieCount);
250 // Process cookie changes.
251 connect(webEngineCookieStorePointer, SIGNAL(cookieAdded(QNetworkCookie)), privacyWebEngineViewPointer, SLOT(addCookieToList(QNetworkCookie)));
252 connect(webEngineCookieStorePointer, SIGNAL(cookieRemoved(QNetworkCookie)), privacyWebEngineViewPointer, SLOT(removeCookieFromList(QNetworkCookie)));
254 // Get a list of durable cookies.
255 QList<QNetworkCookie*> *durableCookiesListPointer = CookiesDatabase::getCookies();
257 // Add the durable cookies to the store.
258 for (QNetworkCookie *cookiePointer : *durableCookiesListPointer)
259 addCookieToStore(*cookiePointer, webEngineCookieStorePointer);
261 // Define an update tab title lambda.
262 auto updateTabTitle = [privacyWebEngineViewPointer, this] (const QString &title)
264 // Get the index for this tab.
265 int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer);
267 // Update the title for this tab.
268 tabWidgetPointer->setTabText(tabIndex, title);
271 // Update the title when it changes.
272 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::titleChanged, this, updateTabTitle);
274 // Define an update tab icon lambda.
275 auto updateTabIcon = [privacyWebEngineViewPointer, this] (const QIcon &icon)
277 // Get the index for this tab.
278 int tabIndex = tabWidgetPointer->indexOf(privacyWebEngineViewPointer);
280 // Update the icon for this tab.
282 tabWidgetPointer->setTabIcon(tabIndex, defaultTabIcon);
284 tabWidgetPointer->setTabIcon(tabIndex, icon);
287 // Update the icon when it changes.
288 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::iconChanged, this, updateTabIcon);
290 // Move to the new tab.
291 tabWidgetPointer->setCurrentIndex(newTabIndex);
293 // Return the privacy WebEngine view pointer.
294 return privacyWebEngineViewPointer;
297 void TabWidget::applyApplicationSettings()
299 // Set the tab position.
300 if (Settings::tabsOnTop())
301 tabWidgetPointer->setTabPosition(QTabWidget::North);
303 tabWidgetPointer->setTabPosition(QTabWidget::South);
305 // Set the search engine URL.
306 searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
308 // Emit the update search engine actions signal.
309 emit updateSearchEngineActions(Settings::searchEngine(), true);
312 // 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.
313 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
314 void TabWidget::applyDomainSettingsAndReload()
316 // Apply the domain settings. `true` reloads the website.
317 applyDomainSettings(currentPrivacyWebEngineViewPointer->url().host(), true);
320 // 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.
321 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
322 void TabWidget::applyDomainSettingsWithoutReloading(const QString &hostname)
324 // Apply the domain settings `false` does not reload the website.
325 applyDomainSettings(hostname, false);
328 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
329 void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
331 // Get the record for the hostname.
332 QSqlQuery domainQuery = DomainsDatabase::getDomainQuery(hostname);
334 // Check if the hostname has domain settings.
335 if (domainQuery.isValid()) // The hostname has domain settings.
337 // Get the domain record.
338 QSqlRecord domainRecord = domainQuery.record();
340 // Store the domain settings name.
341 currentPrivacyWebEngineViewPointer->domainSettingsName = domainRecord.field(DomainsDatabase::DOMAIN_NAME).value().toString();
343 // Set the JavaScript status.
344 switch (domainRecord.field(DomainsDatabase::JAVASCRIPT).value().toInt())
346 // Set the default JavaScript status.
347 case (DomainsDatabase::SYSTEM_DEFAULT):
349 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
354 // Disable JavaScript.
355 case (DomainsDatabase::DISABLED):
357 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
362 // Enable JavaScript.
363 case (DomainsDatabase::ENABLED):
365 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
371 // Set the local storage status.
372 switch (domainRecord.field(DomainsDatabase::LOCAL_STORAGE).value().toInt())
374 // Set the default local storage status.
375 case (DomainsDatabase::SYSTEM_DEFAULT):
377 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
382 // Disable local storage.
383 case (DomainsDatabase::DISABLED):
385 currentPrivacyWebEngineViewPointer->localStorageEnabled = false;
390 // Enable local storage.
391 case (DomainsDatabase::ENABLED):
393 currentPrivacyWebEngineViewPointer->localStorageEnabled = true;
399 // Set the DOM storage status.
400 switch (domainRecord.field(DomainsDatabase::DOM_STORAGE).value().toInt())
402 // Set the default DOM storage status.
403 case (DomainsDatabase::SYSTEM_DEFAULT):
405 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
410 // Disable DOM storage.
411 case (DomainsDatabase::DISABLED):
413 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
418 // Enable DOM storage.
419 case (DomainsDatabase::ENABLED):
421 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
427 // Set the user agent.
428 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainRecord.field(DomainsDatabase::USER_AGENT).value().toString()));
430 // Check if a custom zoom factor is set.
431 if (domainRecord.field(DomainsDatabase::ZOOM_FACTOR).value().toInt())
433 // Store the current zoom factor.
434 currentZoomFactor = domainRecord.field(DomainsDatabase::CUSTOM_ZOOM_FACTOR).value().toDouble();
438 // Reset the current zoom factor.
439 currentZoomFactor = Settings::zoomFactor();
442 // Set the zoom factor. The use of `currentZoomFactor` can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
443 currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);
445 else // The hostname does not have domain settings.
447 // Reset the domain settings name.
448 currentPrivacyWebEngineViewPointer->domainSettingsName = QStringLiteral("");
450 // Set the JavaScript status.
451 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
453 // Set the local storage status.
454 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
456 // Set DOM storage. In QWebEngineSettings it is called Local Storage.
457 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
459 // Set the user agent.
460 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
462 // Store the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
463 currentZoomFactor = Settings::zoomFactor();
465 // Set the zoom factor.
466 currentPrivacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor());
470 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
471 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
472 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
473 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
474 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
475 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
477 // Reload the website if requested.
479 currentPrivacyWebEngineViewPointer->reload();
482 void TabWidget::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
484 // Store the search engine name.
485 QString searchEngineName = searchEngineActionPointer->text();
487 // Strip out any `&` characters.
488 searchEngineName.remove('&');
490 // Store the search engine string.
491 searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
493 // Update the search engine actionas.
494 emit updateSearchEngineActions(searchEngineName, false);
497 void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
499 // Get the user agent name.
500 QString userAgentName = userAgentActionPointer->text();
502 // Strip out any `&` characters.
503 userAgentName.remove('&');
505 // Apply the user agent.
506 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromTranslatedName(userAgentName));
508 // Update the user agent actions.
509 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), false);
511 // Reload the website.
512 currentPrivacyWebEngineViewPointer->reload();
515 // This can be const once <https://redmine.stoutner.com/issues/799> has been resolved.
516 void TabWidget::applyOnTheFlyZoomFactor(const double &zoomFactor)
518 // Update the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
519 currentZoomFactor = zoomFactor;
521 // Set the zoom factor.
522 currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactor);
525 void TabWidget::back() const
528 currentPrivacyWebEngineViewPointer->back();
531 void TabWidget::deleteAllCookies() const
533 // Delete all the cookies.
534 currentWebEngineCookieStorePointer->deleteAllCookies();
537 void TabWidget::deleteCookieFromStore(const QNetworkCookie &cookie) const
539 // Delete the cookie.
540 currentWebEngineCookieStorePointer->deleteCookie(cookie);
543 void TabWidget::deleteTab(const int tabIndex)
545 // Get the privacy WebEngine view.
546 PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->widget(tabIndex));
548 // Proccess the tab delete according to the number of tabs.
549 if (tabWidgetPointer->count() > 1) // There is more than one tab.
552 tabWidgetPointer->removeTab(tabIndex);
554 // Delete the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
555 delete privacyWebEngineViewPointer->page();
557 // Delete the privacy WebEngine view.
558 delete privacyWebEngineViewPointer;
560 else // There is only one tab.
562 // Close Privacy Browser.
567 void TabWidget::forward() const
570 currentPrivacyWebEngineViewPointer->forward();
573 void TabWidget::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) const
576 emit fullScreenRequested(fullScreenRequest.toggleOn());
578 // Accept the request.
579 fullScreenRequest.accept();
582 std::list<QNetworkCookie>* TabWidget::getCookieList() const
584 // Return the current cookie list.
585 return currentPrivacyWebEngineViewPointer->cookieListPointer;
588 QString& TabWidget::getDomainSettingsName() const
590 // Return the domain settings name.
591 return currentPrivacyWebEngineViewPointer->domainSettingsName;
594 void TabWidget::home() const
596 // Load the homepage.
597 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(Settings::homepage()));
600 void TabWidget::loadFinished() const
602 // Hide the progress bar.
603 emit hideProgressBar();
606 void TabWidget::loadInitialWebsite()
608 // Apply the application settings.
609 applyApplicationSettings();
611 // Get the arguments.
612 QStringList argumentsStringList = qApp->arguments();
614 // Check to see if the arguments lists contains a URL.
615 if (argumentsStringList.size() > 1)
617 // Load the URL from the arguments list.
618 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(argumentsStringList.at(1)));
622 // Load the homepage.
627 void TabWidget::loadProgress(const int &progress) const
629 // Show the progress bar.
630 emit showProgressBar(progress);
633 void TabWidget::loadStarted() const
635 // Show the progress bar.
636 emit showProgressBar(0);
639 void TabWidget::loadUrlFromLineEdit(QString url) const
641 // Decide if the text is more likely to be a URL or a search.
642 if (url.startsWith("file://")) // The text is likely a file URL.
645 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
647 else if (url.contains(".")) // The text is likely a URL.
649 // Check if the URL does not start with a valid protocol.
650 if (!url.startsWith("http"))
652 // Add `https://` to the beginning of the URL.
653 url = "https://" + url;
657 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
659 else // The text is likely a search.
662 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(searchEngineUrl + url));
666 void TabWidget::mouseBack() const
668 // Go back if possible.
669 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoBack())
671 // Clear the URL line edit focus.
672 emit clearUrlLineEditFocus();
675 currentPrivacyWebEngineViewPointer->back();
679 void TabWidget::mouseForward() const
681 // Go forward if possible.
682 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoForward())
684 // Clear the URL line edit focus.
685 emit clearUrlLineEditFocus();
688 currentPrivacyWebEngineViewPointer->forward();
692 void TabWidget::pageLinkHovered(const QString &linkUrl) const
694 // Emit a signal so that the browser window can update the status bar.
695 emit linkHovered(linkUrl);
698 void TabWidget::print() const
703 // Set the resolution to be 300 dpi.
704 printer.setResolution(300);
706 // Create a printer dialog.
707 QPrintDialog printDialog(&printer, currentPrivacyWebEngineViewPointer);
709 // Display the dialog and print the page if instructed.
710 if (printDialog.exec() == QDialog::Accepted)
711 printWebpage(&printer);
714 void TabWidget::printPreview() const
719 // Set the resolution to be 300 dpi.
720 printer.setResolution(300);
722 // Create a print preview dialog.
723 QPrintPreviewDialog printPreviewDialog(&printer, currentPrivacyWebEngineViewPointer);
725 // Generate the print preview.
726 connect(&printPreviewDialog, SIGNAL(paintRequested(QPrinter *)), this, SLOT(printWebpage(QPrinter *)));
728 // Display the dialog.
729 printPreviewDialog.exec();
732 void TabWidget::printWebpage(QPrinter *printerPointer) const
734 // Create an event loop. For some reason, the print preview doesn't produce any output unless it is run inside an event loop.
735 QEventLoop eventLoop;
737 // Print the webpage, converting the callback above into a `QWebEngineCallback<bool>`.
738 // Printing requires that the printer be a pointer, not a reference, or it will crash with much cursing.
739 currentWebEnginePagePointer->print(printerPointer, [&eventLoop](bool printSuccess)
741 // Instruct the compiler to ignore the unused parameter.
752 void TabWidget::refresh() const
754 // Reload the website.
755 currentPrivacyWebEngineViewPointer->reload();
758 void TabWidget::setTabBarVisible(const bool visible) const
760 // Set the tab bar visibility.
761 tabWidgetPointer->tabBar()->setVisible(visible);
764 void TabWidget::showSaveDialog(QWebEngineDownloadItem *downloadItemPointer) const
766 // Instantiate the save dialog.
767 SaveDialog *saveDialogPointer = new SaveDialog(downloadItemPointer);
769 // Connect the save button.
770 connect(saveDialogPointer, SIGNAL(showSaveFilePickerDialog(QUrl &, QString &)), this, SLOT(showSaveFilePickerDialog(QUrl &, QString &)));
773 saveDialogPointer->show();
776 void TabWidget::showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName)
778 // Get the download location.
779 QString downloadDirectory = Settings::downloadLocation();
781 // Resolve the system download directory if specified.
782 if (downloadDirectory == QStringLiteral("System Download Directory"))
783 downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
785 // Create a save file dialog.
786 QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory);
788 // Tell the dialog to use a save button.
789 saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave);
791 // Populate the file name from the download item pointer.
792 saveFileDialogPointer->selectFile(suggestedFileName);
794 // Prevent interaction with the parent window while the dialog is open.
795 saveFileDialogPointer->setWindowModality(Qt::WindowModal);
797 // Process the saving of the file. The save file dialog pointer must be captured directly instead of by reference or nasty crashes occur.
798 auto saveFile = [saveFileDialogPointer, &downloadUrl] () {
799 // Get the save location. The dialog box should only allow the selecting of one file location.
800 QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0);
802 // Create a file copy job. `-1` creates the file with default permissions.
803 KIO::FileCopyJob *fileCopyJobPointer = KIO::file_copy(downloadUrl, saveLocation, -1, KIO::Overwrite);
805 // Set the download job to display any error messages.
806 fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true);
808 // Start the download.
809 fileCopyJobPointer->start();
812 // Handle clicks on the save button.
813 connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile);
816 saveFileDialogPointer->show();
819 void TabWidget::toggleDomStorage() const
821 // Toggle DOM storage.
822 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
824 // Update the DOM storage action.
825 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
827 // Reload the website.
828 currentPrivacyWebEngineViewPointer->reload();
831 void TabWidget::toggleJavaScript() const
833 // Toggle JavaScript.
834 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
836 // Update the JavaScript action.
837 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
839 // Reload the website.
840 currentPrivacyWebEngineViewPointer->reload();
843 void TabWidget::toggleLocalStorage()
845 // Toggle local storeage.
846 currentPrivacyWebEngineViewPointer->localStorageEnabled = !currentPrivacyWebEngineViewPointer->localStorageEnabled;
848 // Update the local storage action.
849 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
851 // Reload the website.
852 currentPrivacyWebEngineViewPointer->reload();
855 void TabWidget::updateUiWithTabSettings()
857 // Update the current WebEngine pointers.
858 currentPrivacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->currentWidget());
859 currentWebEngineSettingsPointer = currentPrivacyWebEngineViewPointer->settings();
860 currentWebEnginePagePointer = currentPrivacyWebEngineViewPointer->page();
861 currentWebEngineProfilePointer = currentWebEnginePagePointer->profile();
862 currentWebEngineHistoryPointer = currentWebEnginePagePointer->history();
863 currentWebEngineCookieStorePointer = currentWebEngineProfilePointer->cookieStore();
865 // Clear the URL line edit focus.
866 emit clearUrlLineEditFocus();
869 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
870 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
871 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
872 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
873 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
874 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
875 emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url());
876 emit updateCookiesAction(currentPrivacyWebEngineViewPointer->cookieListPointer->size());
879 void TabWidget::updateUrl(const QUrl &url) const
881 // Update the URL line edit.
882 emit updateUrlLineEdit(url);
884 // Update the status of the forward and back buttons.
885 emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
886 emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
888 // Reapply the zoom factor. This is a bug in QWebEngineView that resets the zoom with every load. <https://redmine.stoutner.com/issues/799>
889 currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);