2 * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
6 * Privacy Browser PC is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Browser PC is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser PC. If not, see <http://www.gnu.org/licenses/>.
20 // Application headers.
21 #include "TabWidget.h"
23 #include "ui_AddTabWidget.h"
24 #include "ui_TabWidget.h"
25 #include "databases/CookiesDatabase.h"
26 #include "databases/DomainsDatabase.h"
27 #include "dialogs/SaveDialog.h"
28 #include "filters/MouseEventFilter.h"
29 #include "helpers/SearchEngineHelper.h"
30 #include "helpers/UserAgentHelper.h"
31 #include "interceptors/UrlRequestInterceptor.h"
32 #include "windows/BrowserWindow.h"
34 // KDE Framework headers.
35 #include <KIO/FileCopyJob>
36 #include <KIO/JobUiDelegate>
38 // Qt toolkit headers.
40 #include <QFileDialog>
41 #include <QGraphicsScene>
42 #include <QGraphicsView>
43 #include <QPrintDialog>
44 #include <QPrintPreviewDialog>
47 // Initialize the public static variables.
48 QString TabWidget::webEngineDefaultUserAgent = QStringLiteral("");
50 // Construct the class.
51 TabWidget::TabWidget(QWidget *parent) : QWidget(parent)
53 // Instantiate the UIs.
54 Ui::TabWidget tabWidgetUi;
55 Ui::AddTabWidget addTabWidgetUi;
58 tabWidgetUi.setupUi(this);
60 // Get a handle for the tab widget.
61 tabWidgetPointer = tabWidgetUi.tabWidget;
63 // Setup the add tab UI.
64 addTabWidgetUi.setupUi(tabWidgetPointer);
66 // Get handles for the add tab widgets.
67 QWidget *addTabWidgetPointer = addTabWidgetUi.addTabQWidget;
68 QPushButton *addTabButtonPointer = addTabWidgetUi.addTabButton;
70 // Display the add tab widget.
71 tabWidgetPointer->setCornerWidget(addTabWidgetPointer);
76 // Process tab events.
77 connect(tabWidgetPointer, SIGNAL(currentChanged(int)), this, SLOT(updateUiWithTabSettings()));
78 connect(addTabButtonPointer, SIGNAL(clicked()), this, SLOT(addTab()));
79 connect(tabWidgetPointer, SIGNAL(tabCloseRequested(int)), this, SLOT(deleteTab(int)));
81 // Store a copy of the WebEngine default user agent.
82 webEngineDefaultUserAgent = currentWebEngineProfilePointer->httpUserAgent();
84 // Instantiate the mouse event filter pointer.
85 MouseEventFilter *mouseEventFilterPointer = new MouseEventFilter();
87 // Install the mouse event filter.
88 qApp->installEventFilter(mouseEventFilterPointer);
90 // Process mouse forward and back commands.
91 connect(mouseEventFilterPointer, SIGNAL(mouseBack()), this, SLOT(mouseBack()));
92 connect(mouseEventFilterPointer, SIGNAL(mouseForward()), this, SLOT(mouseForward()));
95 TabWidget::~TabWidget()
97 // Manually delete each WebEngine page.
98 for (int i = 0; i < tabWidgetPointer->count(); ++i)
100 // Get the privacy WebEngine view.
101 PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->widget(i));
103 // Deletion the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
104 delete privacyWebEngineViewPointer->page();
108 // The cookie is copied instead of referenced so that changes made to the cookie do not create a race condition with the display of the cookie in the dialog.
109 void TabWidget::addCookieToStore(QNetworkCookie cookie, QWebEngineCookieStore *webEngineCookieStorePointer) const
114 // Check to see if the domain does not start with a `.` because Qt makes this harder than it should be. <https://doc.qt.io/qt-5/qwebenginecookiestore.html#setCookie>
115 if (!cookie.domain().startsWith(QStringLiteral(".")))
118 url.setHost(cookie.domain());
119 url.setScheme(QStringLiteral("https"));
121 // Clear the domain from the cookie.
122 cookie.setDomain(QStringLiteral(""));
125 // Add the cookie to the store.
126 if (webEngineCookieStorePointer == nullptr)
127 currentWebEngineCookieStorePointer->setCookie(cookie, url);
129 webEngineCookieStorePointer->setCookie(cookie, url);
132 void TabWidget::addFirstTab()
134 // Create the first tab.
137 // Update the UI with the tab settings.
138 updateUiWithTabSettings();
140 // Set the focus on the current tab widget. This prevents the tab bar from showing a blue bar under the label of the first tab.
141 tabWidgetPointer->currentWidget()->setFocus();
144 PrivacyWebEngineView* TabWidget::addTab(const bool focusNewWebEngineView)
146 // Create a privacy WebEngine view.
147 PrivacyWebEngineView *privacyWebEngineViewPointer = new PrivacyWebEngineView();
150 int newTabIndex = tabWidgetPointer->addTab(privacyWebEngineViewPointer, i18nc("New tab label.", "New Tab"));
152 // Set the default tab icon.
153 tabWidgetPointer->setTabIcon(newTabIndex, defaultTabIcon);
155 // Create an off-the-record profile (the default when no profile name is specified).
156 QWebEngineProfile *webEngineProfilePointer = new QWebEngineProfile(QStringLiteral(""));
158 // Create a WebEngine page.
159 QWebEnginePage *webEnginePagePointer = new QWebEnginePage(webEngineProfilePointer);
161 // Set the WebEngine page.
162 privacyWebEngineViewPointer->setPage(webEnginePagePointer);
164 // Get handles for the web engine elements.
165 QWebEngineCookieStore *webEngineCookieStorePointer = webEngineProfilePointer->cookieStore();
166 QWebEngineSettings *webEngineSettingsPointer = webEnginePagePointer->settings();
168 // Update the URL line edit when the URL changes.
169 connect(privacyWebEngineViewPointer, 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 // Clear the URL line edit focus so that it populates correctly when opening a new tab from the context menu.
294 if (focusNewWebEngineView)
295 emit clearUrlLineEditFocus();
297 // Return the privacy WebEngine view pointer.
298 return privacyWebEngineViewPointer;
301 void TabWidget::applyApplicationSettings()
303 // Set the tab position.
304 if (Settings::tabsOnTop())
305 tabWidgetPointer->setTabPosition(QTabWidget::North);
307 tabWidgetPointer->setTabPosition(QTabWidget::South);
309 // Set the search engine URL.
310 searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
312 // Emit the update search engine actions signal.
313 emit updateSearchEngineActions(Settings::searchEngine(), true);
316 // 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.
317 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
318 void TabWidget::applyDomainSettingsAndReload()
320 // Apply the domain settings. `true` reloads the website.
321 applyDomainSettings(currentPrivacyWebEngineViewPointer->url().host(), true);
324 // 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.
325 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
326 void TabWidget::applyDomainSettingsWithoutReloading(const QString &hostname)
328 // Apply the domain settings `false` does not reload the website.
329 applyDomainSettings(hostname, false);
332 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
333 void TabWidget::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
335 // Get the record for the hostname.
336 QSqlQuery domainQuery = DomainsDatabase::getDomainQuery(hostname);
338 // Check if the hostname has domain settings.
339 if (domainQuery.isValid()) // The hostname has domain settings.
341 // Get the domain record.
342 QSqlRecord domainRecord = domainQuery.record();
344 // Store the domain settings name.
345 currentPrivacyWebEngineViewPointer->domainSettingsName = domainRecord.field(DomainsDatabase::DOMAIN_NAME).value().toString();
347 // Set the JavaScript status.
348 switch (domainRecord.field(DomainsDatabase::JAVASCRIPT).value().toInt())
350 // Set the default JavaScript status.
351 case (DomainsDatabase::SYSTEM_DEFAULT):
353 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
358 // Disable JavaScript.
359 case (DomainsDatabase::DISABLED):
361 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
366 // Enable JavaScript.
367 case (DomainsDatabase::ENABLED):
369 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
375 // Set the local storage status.
376 switch (domainRecord.field(DomainsDatabase::LOCAL_STORAGE).value().toInt())
378 // Set the default local storage status.
379 case (DomainsDatabase::SYSTEM_DEFAULT):
381 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
386 // Disable local storage.
387 case (DomainsDatabase::DISABLED):
389 currentPrivacyWebEngineViewPointer->localStorageEnabled = false;
394 // Enable local storage.
395 case (DomainsDatabase::ENABLED):
397 currentPrivacyWebEngineViewPointer->localStorageEnabled = true;
403 // Set the DOM storage status.
404 switch (domainRecord.field(DomainsDatabase::DOM_STORAGE).value().toInt())
406 // Set the default DOM storage status.
407 case (DomainsDatabase::SYSTEM_DEFAULT):
409 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
414 // Disable DOM storage.
415 case (DomainsDatabase::DISABLED):
417 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
422 // Enable DOM storage.
423 case (DomainsDatabase::ENABLED):
425 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
431 // Set the user agent.
432 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainRecord.field(DomainsDatabase::USER_AGENT).value().toString()));
434 // Check if a custom zoom factor is set.
435 if (domainRecord.field(DomainsDatabase::ZOOM_FACTOR).value().toInt())
437 // Store the current zoom factor.
438 currentZoomFactor = domainRecord.field(DomainsDatabase::CUSTOM_ZOOM_FACTOR).value().toDouble();
442 // Reset the current zoom factor.
443 currentZoomFactor = Settings::zoomFactor();
446 // Set the zoom factor. The use of `currentZoomFactor` can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
447 currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);
449 else // The hostname does not have domain settings.
451 // Reset the domain settings name.
452 currentPrivacyWebEngineViewPointer->domainSettingsName = QStringLiteral("");
454 // Set the JavaScript status.
455 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
457 // Set the local storage status.
458 currentPrivacyWebEngineViewPointer->localStorageEnabled = Settings::localStorageEnabled();
460 // Set DOM storage. In QWebEngineSettings it is called Local Storage.
461 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
463 // Set the user agent.
464 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
466 // Store the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
467 currentZoomFactor = Settings::zoomFactor();
469 // Set the zoom factor.
470 currentPrivacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor());
474 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
475 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
476 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
477 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
478 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
479 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
481 // Reload the website if requested.
483 currentPrivacyWebEngineViewPointer->reload();
486 void TabWidget::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
488 // Store the search engine name.
489 QString searchEngineName = searchEngineActionPointer->text();
491 // Strip out any `&` characters.
492 searchEngineName.remove('&');
494 // Store the search engine string.
495 searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
497 // Update the search engine actionas.
498 emit updateSearchEngineActions(searchEngineName, false);
501 void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
503 // Get the user agent name.
504 QString userAgentName = userAgentActionPointer->text();
506 // Strip out any `&` characters.
507 userAgentName.remove('&');
509 // Apply the user agent.
510 currentWebEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromTranslatedName(userAgentName));
512 // Update the user agent actions.
513 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), false);
515 // Reload the website.
516 currentPrivacyWebEngineViewPointer->reload();
519 // This can be const once <https://redmine.stoutner.com/issues/799> has been resolved.
520 void TabWidget::applyOnTheFlyZoomFactor(const double &zoomFactor)
522 // Update the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
523 currentZoomFactor = zoomFactor;
525 // Set the zoom factor.
526 currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactor);
529 void TabWidget::back() const
532 currentPrivacyWebEngineViewPointer->back();
535 void TabWidget::deleteAllCookies() const
537 // Delete all the cookies.
538 currentWebEngineCookieStorePointer->deleteAllCookies();
541 void TabWidget::deleteCookieFromStore(const QNetworkCookie &cookie) const
543 // Delete the cookie.
544 currentWebEngineCookieStorePointer->deleteCookie(cookie);
547 void TabWidget::deleteTab(const int tabIndex)
549 // Get the privacy WebEngine view.
550 PrivacyWebEngineView *privacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->widget(tabIndex));
552 // Proccess the tab delete according to the number of tabs.
553 if (tabWidgetPointer->count() > 1) // There is more than one tab.
556 tabWidgetPointer->removeTab(tabIndex);
558 // Delete the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
559 delete privacyWebEngineViewPointer->page();
561 // Delete the privacy WebEngine view.
562 delete privacyWebEngineViewPointer;
564 else // There is only one tab.
566 // Close Privacy Browser.
571 void TabWidget::forward() const
574 currentPrivacyWebEngineViewPointer->forward();
577 void TabWidget::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) const
580 emit fullScreenRequested(fullScreenRequest.toggleOn());
582 // Accept the request.
583 fullScreenRequest.accept();
586 std::list<QNetworkCookie>* TabWidget::getCookieList() const
588 // Return the current cookie list.
589 return currentPrivacyWebEngineViewPointer->cookieListPointer;
592 QString& TabWidget::getDomainSettingsName() const
594 // Return the domain settings name.
595 return currentPrivacyWebEngineViewPointer->domainSettingsName;
598 void TabWidget::home() const
600 // Load the homepage.
601 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(Settings::homepage()));
604 void TabWidget::loadFinished() const
606 // Hide the progress bar.
607 emit hideProgressBar();
610 void TabWidget::loadInitialWebsite()
612 // Apply the application settings.
613 applyApplicationSettings();
615 // Get the arguments.
616 QStringList argumentsStringList = qApp->arguments();
618 // Check to see if the arguments lists contains a URL.
619 if (argumentsStringList.size() > 1)
621 // Load the URL from the arguments list.
622 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(argumentsStringList.at(1)));
626 // Load the homepage.
631 void TabWidget::loadProgress(const int &progress) const
633 // Show the progress bar.
634 emit showProgressBar(progress);
637 void TabWidget::loadStarted() const
639 // Show the progress bar.
640 emit showProgressBar(0);
643 void TabWidget::loadUrlFromLineEdit(QString url) const
645 // Decide if the text is more likely to be a URL or a search.
646 if (url.startsWith("file://")) // The text is likely a file URL.
649 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
651 else if (url.contains(".")) // The text is likely a URL.
653 // Check if the URL does not start with a valid protocol.
654 if (!url.startsWith("http"))
656 // Add `https://` to the beginning of the URL.
657 url = "https://" + url;
661 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
663 else // The text is likely a search.
666 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(searchEngineUrl + url));
670 void TabWidget::mouseBack() const
672 // Go back if possible.
673 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoBack())
675 // Clear the URL line edit focus.
676 emit clearUrlLineEditFocus();
679 currentPrivacyWebEngineViewPointer->back();
683 void TabWidget::mouseForward() const
685 // Go forward if possible.
686 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoForward())
688 // Clear the URL line edit focus.
689 emit clearUrlLineEditFocus();
692 currentPrivacyWebEngineViewPointer->forward();
696 void TabWidget::pageLinkHovered(const QString &linkUrl) const
698 // Emit a signal so that the browser window can update the status bar.
699 emit linkHovered(linkUrl);
702 void TabWidget::print() const
707 // Set the resolution to be 300 dpi.
708 printer.setResolution(300);
710 // Create a printer dialog.
711 QPrintDialog printDialog(&printer, currentPrivacyWebEngineViewPointer);
713 // Display the dialog and print the page if instructed.
714 if (printDialog.exec() == QDialog::Accepted)
715 printWebpage(&printer);
718 void TabWidget::printPreview() const
723 // Set the resolution to be 300 dpi.
724 printer.setResolution(300);
726 // Create a print preview dialog.
727 QPrintPreviewDialog printPreviewDialog(&printer, currentPrivacyWebEngineViewPointer);
729 // Generate the print preview.
730 connect(&printPreviewDialog, SIGNAL(paintRequested(QPrinter *)), this, SLOT(printWebpage(QPrinter *)));
732 // Display the dialog.
733 printPreviewDialog.exec();
736 void TabWidget::printWebpage(QPrinter *printerPointer) const
738 // Create an event loop. For some reason, the print preview doesn't produce any output unless it is run inside an event loop.
739 QEventLoop eventLoop;
741 // Print the webpage, converting the callback above into a `QWebEngineCallback<bool>`.
742 // Printing requires that the printer be a pointer, not a reference, or it will crash with much cursing.
743 currentWebEnginePagePointer->print(printerPointer, [&eventLoop](bool printSuccess)
745 // Instruct the compiler to ignore the unused parameter.
756 void TabWidget::refresh() const
758 // Reload the website.
759 currentPrivacyWebEngineViewPointer->reload();
762 void TabWidget::setTabBarVisible(const bool visible) const
764 // Set the tab bar visibility.
765 tabWidgetPointer->tabBar()->setVisible(visible);
768 void TabWidget::showSaveDialog(QWebEngineDownloadItem *downloadItemPointer) const
770 // Instantiate the save dialog.
771 SaveDialog *saveDialogPointer = new SaveDialog(downloadItemPointer);
773 // Connect the save button.
774 connect(saveDialogPointer, SIGNAL(showSaveFilePickerDialog(QUrl &, QString &)), this, SLOT(showSaveFilePickerDialog(QUrl &, QString &)));
777 saveDialogPointer->show();
780 void TabWidget::showSaveFilePickerDialog(QUrl &downloadUrl, QString &suggestedFileName)
782 // Get the download location.
783 QString downloadDirectory = Settings::downloadLocation();
785 // Resolve the system download directory if specified.
786 if (downloadDirectory == QStringLiteral("System Download Directory"))
787 downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
789 // Create a save file dialog.
790 QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory);
792 // Tell the dialog to use a save button.
793 saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave);
795 // Populate the file name from the download item pointer.
796 saveFileDialogPointer->selectFile(suggestedFileName);
798 // Prevent interaction with the parent window while the dialog is open.
799 saveFileDialogPointer->setWindowModality(Qt::WindowModal);
801 // Process the saving of the file. The save file dialog pointer must be captured directly instead of by reference or nasty crashes occur.
802 auto saveFile = [saveFileDialogPointer, &downloadUrl] () {
803 // Get the save location. The dialog box should only allow the selecting of one file location.
804 QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0);
806 // Create a file copy job. `-1` creates the file with default permissions.
807 KIO::FileCopyJob *fileCopyJobPointer = KIO::file_copy(downloadUrl, saveLocation, -1, KIO::Overwrite);
809 // Set the download job to display any error messages.
810 fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true);
812 // Start the download.
813 fileCopyJobPointer->start();
816 // Handle clicks on the save button.
817 connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile);
820 saveFileDialogPointer->show();
823 void TabWidget::toggleDomStorage() const
825 // Toggle DOM storage.
826 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
828 // Update the DOM storage action.
829 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
831 // Reload the website.
832 currentPrivacyWebEngineViewPointer->reload();
835 void TabWidget::toggleJavaScript() const
837 // Toggle JavaScript.
838 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
840 // Update the JavaScript action.
841 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
843 // Reload the website.
844 currentPrivacyWebEngineViewPointer->reload();
847 void TabWidget::toggleLocalStorage()
849 // Toggle local storeage.
850 currentPrivacyWebEngineViewPointer->localStorageEnabled = !currentPrivacyWebEngineViewPointer->localStorageEnabled;
852 // Update the local storage action.
853 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
855 // Reload the website.
856 currentPrivacyWebEngineViewPointer->reload();
859 void TabWidget::updateUiWithTabSettings()
861 // Update the current WebEngine pointers.
862 currentPrivacyWebEngineViewPointer = qobject_cast<PrivacyWebEngineView *>(tabWidgetPointer->currentWidget());
863 currentWebEngineSettingsPointer = currentPrivacyWebEngineViewPointer->settings();
864 currentWebEnginePagePointer = currentPrivacyWebEngineViewPointer->page();
865 currentWebEngineProfilePointer = currentWebEnginePagePointer->profile();
866 currentWebEngineHistoryPointer = currentWebEnginePagePointer->history();
867 currentWebEngineCookieStorePointer = currentWebEngineProfilePointer->cookieStore();
869 // Clear the URL line edit focus.
870 emit clearUrlLineEditFocus();
873 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QStringLiteral(""));
874 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
875 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
876 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
877 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
878 emit updateZoomFactorAction(currentPrivacyWebEngineViewPointer->zoomFactor());
879 emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url());
880 emit updateCookiesAction(currentPrivacyWebEngineViewPointer->cookieListPointer->size());
883 void TabWidget::updateUrl(const QUrl &url) const
885 // Update the URL line edit.
886 emit updateUrlLineEdit(url);
888 // Update the status of the forward and back buttons.
889 emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
890 emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
892 // Reapply the zoom factor. This is a bug in QWebEngineView that resets the zoom with every load. <https://redmine.stoutner.com/issues/799>
893 currentPrivacyWebEngineViewPointer->setZoomFactor(currentZoomFactor);