2 * Copyright 2022-2024 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 "DevToolsWebEngineView.h"
22 #include "TabWidget.h"
24 #include "ui_AddTabWidget.h"
26 #include "ui_TabWidget.h"
27 #include "databases/CookiesDatabase.h"
28 #include "dialogs/SaveDialog.h"
29 #include "filters/MouseEventFilter.h"
30 #include "helpers/SearchEngineHelper.h"
31 #include "windows/BrowserWindow.h"
33 // KDE Framework headers.
34 #include <KIO/FileCopyJob>
35 #include <KIO/JobUiDelegate>
36 #include <KNotification>
38 // Qt toolkit headers.
40 #include <QFileDialog>
41 #include <QGraphicsScene>
42 #include <QGraphicsView>
43 #include <QMessageBox>
44 #include <QPrintDialog>
45 #include <QPrintPreviewDialog>
48 // Initialize the public static variables.
49 QString TabWidget::webEngineDefaultUserAgent = QLatin1String("");
51 // Construct the class.
52 TabWidget::TabWidget(QWidget *windowPointer) : QWidget(windowPointer)
54 // Create a QProcess to check if KDE is running.
55 QProcess *checkIfRunningKdeQProcessPointer = new QProcess();
57 // Create an argument string list that contains `ksmserver` (KDE Session Manager).
58 QStringList argument = QStringList(QLatin1String("ksmserver"));
60 // Run `pidof` to check for the presence of `ksmserver`.
61 checkIfRunningKdeQProcessPointer->start(QLatin1String("pidof"), argument);
63 // Monitor any standard output.
64 connect(checkIfRunningKdeQProcessPointer, &QProcess::readyReadStandardOutput, [this]
66 // If there is any standard output, `ksmserver` is running.
70 // Instantiate the user agent helper.
71 userAgentHelperPointer = new UserAgentHelper();
73 // Instantiate the UIs.
74 Ui::TabWidget tabWidgetUi;
75 Ui::AddTabWidget addTabWidgetUi;
78 tabWidgetUi.setupUi(this);
80 // Get a handle for the tab widget.
81 qTabWidgetPointer = tabWidgetUi.tabWidget;
83 // Setup the add tab UI.
84 addTabWidgetUi.setupUi(qTabWidgetPointer);
86 // Get handles for the add tab widgets.
87 QWidget *addTabWidgetPointer = addTabWidgetUi.addTabQWidget;
88 QPushButton *addTabButtonPointer = addTabWidgetUi.addTabButton;
90 // Display the add tab widget.
91 qTabWidgetPointer->setCornerWidget(addTabWidgetPointer);
93 // Create the loading favorite icon movie.
94 loadingFavoriteIconMoviePointer = new QMovie();
96 // Set the loading favorite icon movie file name.
97 loadingFavoriteIconMoviePointer->setFileName(QStringLiteral(":/icons/loading.gif"));
99 // Stop the loading favorite icon movie if the window is destroyed. Otherwise, the app will crash if there is more than one window open and a window is closed while at tab is loading.
100 connect(windowPointer, SIGNAL(destroyed()), this, SLOT(stopLoadingFavoriteIconMovie()));
102 // Add the first tab.
105 // Process tab events.
106 connect(qTabWidgetPointer, SIGNAL(currentChanged(int)), this, SLOT(updateUiWithTabSettings()));
107 connect(addTabButtonPointer, SIGNAL(clicked()), this, SLOT(addTab()));
108 connect(qTabWidgetPointer, SIGNAL(tabCloseRequested(int)), this, SLOT(deleteTab(int)));
110 // Store a copy of the WebEngine default user agent.
111 webEngineDefaultUserAgent = currentWebEngineProfilePointer->httpUserAgent();
113 // Instantiate the mouse event filter pointer.
114 MouseEventFilter *mouseEventFilterPointer = new MouseEventFilter();
116 // Install the mouse event filter.
117 qApp->installEventFilter(mouseEventFilterPointer);
119 // Process mouse forward and back commands.
120 connect(mouseEventFilterPointer, SIGNAL(mouseBack()), this, SLOT(mouseBack()));
121 connect(mouseEventFilterPointer, SIGNAL(mouseForward()), this, SLOT(mouseForward()));
124 TabWidget::~TabWidget()
126 // Get the number of tabs.
127 int numberOfTabs = qTabWidgetPointer->count();
129 // Manually delete each WebEngine page.
130 for (int i = 0; i < numberOfTabs; ++i)
132 // Get the tab splitter widget.
133 QWidget *tabSplitterWidgetPointer = qTabWidgetPointer->widget(i);
135 // Get the WebEngine views.
136 PrivacyWebEngineView *privacyWebEngineViewPointer = tabSplitterWidgetPointer->findChild<PrivacyWebEngineView *>();
137 DevToolsWebEngineView *devToolsWebEngineViewPointer = tabSplitterWidgetPointer->findChild<DevToolsWebEngineView *>();
139 // Deletion the WebEngine pages to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
140 delete privacyWebEngineViewPointer->page();
141 delete devToolsWebEngineViewPointer->page();
145 // 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.
146 void TabWidget::addCookieToStore(QNetworkCookie cookie, QWebEngineCookieStore *webEngineCookieStorePointer) const
151 // 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>
152 if (!cookie.domain().startsWith(QLatin1String(".")))
155 url.setHost(cookie.domain());
156 url.setScheme(QLatin1String("https"));
158 // Clear the domain from the cookie.
159 cookie.setDomain(QLatin1String(""));
162 // Add the cookie to the store.
163 if (webEngineCookieStorePointer == nullptr)
164 currentWebEngineCookieStorePointer->setCookie(cookie, url);
166 webEngineCookieStorePointer->setCookie(cookie, url);
169 void TabWidget::addFirstTab()
171 // Create the first tab.
174 // Update the UI with the tab settings.
175 updateUiWithTabSettings();
177 // 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.
178 qTabWidgetPointer->currentWidget()->setFocus();
181 PrivacyWebEngineView* TabWidget::addTab(const bool removeUrlLineEditFocus, const bool adjacent, const bool backgroundTab, const QString urlString)
183 // Create a splitter widget.
184 QSplitter *splitterPointer = new QSplitter();
186 // Set the splitter to be vertical.
187 splitterPointer->setOrientation(Qt::Vertical);
189 // Set the splitter handle size.
190 splitterPointer->setHandleWidth(5);
192 // Create the WebEngines.
193 PrivacyWebEngineView *privacyWebEngineViewPointer = new PrivacyWebEngineView();
194 DevToolsWebEngineView *devToolsWebEngineViewPointer = new DevToolsWebEngineView();
196 // Add the WebEngines to the splitter.
197 splitterPointer->addWidget(privacyWebEngineViewPointer);
198 splitterPointer->addWidget(devToolsWebEngineViewPointer);
200 // Initialize the new tab index.
204 if (adjacent) // Add the new tab adjacent to the current tab.
205 newTabIndex = qTabWidgetPointer->insertTab((qTabWidgetPointer->currentIndex() + 1), splitterPointer, i18nc("New tab label.", "New Tab"));
206 else // Add the new tab at the end of the list.
207 newTabIndex = qTabWidgetPointer->addTab(splitterPointer, i18nc("New tab label.", "New Tab"));
209 // Set the default tab icon.
210 qTabWidgetPointer->setTabIcon(newTabIndex, defaultFavoriteIcon);
212 // Get handles for the WebEngine components.
213 QWebEnginePage *webEnginePagePointer = privacyWebEngineViewPointer->page();
214 QWebEngineProfile *webEngineProfilePointer = webEnginePagePointer->profile();
215 QWebEngineCookieStore *webEngineCookieStorePointer = webEngineProfilePointer->cookieStore();
216 QWebEngineSettings *webEngineSettingsPointer = webEnginePagePointer->settings();
218 // Set the development tools WebEngine. This must be done here to preserve the bottom half of the window as the initial development tools size.
219 webEnginePagePointer->setDevToolsPage(devToolsWebEngineViewPointer->page());
221 // Initially hide the development tools WebEngine.
222 devToolsWebEngineViewPointer->setVisible(false);
224 // Initially disable the development tools WebEngine.
225 webEnginePagePointer->setDevToolsPage(nullptr);
227 // Disable JavaScript on the development tools WebEngine to prevent error messages from being written to the console.
228 devToolsWebEngineViewPointer->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
230 // Update the URL line edit when the URL changes.
231 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::urlChanged, [this, privacyWebEngineViewPointer] (const QUrl &newUrl)
233 // Only update the UI if this is the current tab.
234 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
236 // Update the URL line edit.
237 emit updateUrlLineEdit(newUrl);
239 // Update the status of the forward and back buttons.
240 emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
241 emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
245 // Update the title when it changes.
246 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::titleChanged, [this, splitterPointer] (const QString &title)
248 // Get the index for this tab.
249 int tabIndex = qTabWidgetPointer->indexOf(splitterPointer);
251 // Update the title for this tab.
252 qTabWidgetPointer->setTabText(tabIndex, title);
254 // Update the window title if this is the current tab.
255 if (tabIndex == qTabWidgetPointer->currentIndex())
256 emit updateWindowTitle(title);
259 // Connect the loading favorite icon movie to the tab icon.
260 connect(loadingFavoriteIconMoviePointer, &QMovie::frameChanged, [this, splitterPointer, privacyWebEngineViewPointer]
262 // Get the index for this tab.
263 int tabIndex = qTabWidgetPointer->indexOf(splitterPointer);
265 // Display the loading favorite icon if this tab is loading.
266 if (privacyWebEngineViewPointer->isLoading)
267 qTabWidgetPointer->setTabIcon(tabIndex, loadingFavoriteIconMoviePointer->currentPixmap());
270 // Update the icon when it changes.
271 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::iconChanged, [this, splitterPointer, privacyWebEngineViewPointer] (const QIcon &newFavoriteIcon)
273 // Store the favorite icon in the privacy web engine view.
274 if (newFavoriteIcon.isNull())
275 privacyWebEngineViewPointer->favoriteIcon = defaultFavoriteIcon;
277 privacyWebEngineViewPointer->favoriteIcon = newFavoriteIcon;
279 // Get the index for this tab.
280 int tabIndex = qTabWidgetPointer->indexOf(splitterPointer);
282 // Update the icon for this tab.
283 if (newFavoriteIcon.isNull())
284 qTabWidgetPointer->setTabIcon(tabIndex, defaultFavoriteIcon);
286 qTabWidgetPointer->setTabIcon(tabIndex, newFavoriteIcon);
289 // Update the progress bar and the favorite icon when a load is started.
290 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadStarted, [this, privacyWebEngineViewPointer] ()
292 // Set the privacy web engine view to be loading.
293 privacyWebEngineViewPointer->isLoading = true;
295 // Store the load progress.
296 privacyWebEngineViewPointer->loadProgressInt = 0;
298 // Show the progress bar if this is the current tab.
299 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
300 emit showProgressBar(0);
302 // Start the loading favorite icon movie.
303 loadingFavoriteIconMoviePointer->start();
306 // Update the progress bar when a load progresses.
307 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadProgress, [this, privacyWebEngineViewPointer] (const int progress)
309 // Store the load progress.
310 privacyWebEngineViewPointer->loadProgressInt = progress;
312 // Update the progress bar if this is the current tab.
313 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
314 emit showProgressBar(progress);
317 // Update the progress bar when a load finishes.
318 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::loadFinished, [this, splitterPointer, privacyWebEngineViewPointer] ()
320 // Set the privacy web engine view to be not loading.
321 privacyWebEngineViewPointer->isLoading = false;
323 // Store the load progress.
324 privacyWebEngineViewPointer->loadProgressInt = -1;
326 // Hide the progress bar if this is the current tab.
327 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
328 emit hideProgressBar();
330 // Get the index for this tab.
331 int tabIndex = qTabWidgetPointer->indexOf(splitterPointer);
333 // Display the current favorite icon
334 qTabWidgetPointer->setTabIcon(tabIndex, privacyWebEngineViewPointer->favoriteIcon);
336 // Create a no tabs loading variable.
337 bool noTabsLoading = true;
339 // Get the number of tabs.
340 int numberOfTabs = qTabWidgetPointer->count();
342 // Check to see if any other tabs are loading.
343 for (int i = 0; i < numberOfTabs; i++)
345 // Get the privacy WebEngine view for the tab.
346 PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild<PrivacyWebEngineView *>();
348 // Check to see if it is currently loading. If at least one tab is loading, this flag will end up being marked `false` when the for loop has finished.
349 if (privacyWebEngineViewPointer->isLoading)
350 noTabsLoading = false;
353 // Stop the loading favorite icon movie if there are no loading tabs.
355 loadingFavoriteIconMoviePointer->stop();
358 // Display HTTP Ping blocked dialogs.
359 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::displayHttpPingBlockedDialog, [this, privacyWebEngineViewPointer] (const QString &httpPingUrl)
361 // Only display the HTTP Ping blocked dialog if this is the current tab.
362 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
364 // Instantiate an HTTP ping blocked message box.
365 QMessageBox httpPingBlockedMessageBox;
368 httpPingBlockedMessageBox.setIcon(QMessageBox::Information);
370 // Set the window title.
371 httpPingBlockedMessageBox.setWindowTitle(i18nc("HTTP Ping blocked dialog title", "HTTP Ping Blocked"));
374 httpPingBlockedMessageBox.setText(i18nc("HTTP Ping blocked dialog text", "This request has been blocked because it sends a naughty HTTP ping to %1.", httpPingUrl));
376 // Set the standard button.
377 httpPingBlockedMessageBox.setStandardButtons(QMessageBox::Ok);
379 // Display the message box.
380 httpPingBlockedMessageBox.exec();
384 // Update the zoom actions when changed by CTRL-Scrolling. This can be modified when <https://redmine.stoutner.com/issues/845> is fixed.
385 connect(webEnginePagePointer, &QWebEnginePage::contentsSizeChanged, [webEnginePagePointer, this] ()
387 // Only update the zoom actions if this is the current tab.
388 if (webEnginePagePointer == currentWebEnginePagePointer)
389 emit updateZoomActions(webEnginePagePointer->zoomFactor());
392 // Display find text results.
393 connect(webEnginePagePointer, SIGNAL(findTextFinished(const QWebEngineFindTextResult &)), this, SLOT(findTextFinished(const QWebEngineFindTextResult &)));
395 // Handle full screen requests.
396 connect(webEnginePagePointer, SIGNAL(fullScreenRequested(QWebEngineFullScreenRequest)), this, SLOT(fullScreenRequested(QWebEngineFullScreenRequest)));
398 // Listen for hovered link URLs.
399 connect(webEnginePagePointer, SIGNAL(linkHovered(const QString)), this, SLOT(pageLinkHovered(const QString)));
401 // Handle file downloads.
402 connect(webEngineProfilePointer, SIGNAL(downloadRequested(QWebEngineDownloadItem *)), this, SLOT(showSaveDialog(QWebEngineDownloadItem *)));
404 // Set the local storage filter.
405 webEngineCookieStorePointer->setCookieFilter([privacyWebEngineViewPointer](const QWebEngineCookieStore::FilterRequest &filterRequest)
407 // Block all third party local storage requests, including the sneaky ones that don't register a first party URL.
408 if (filterRequest.thirdParty || (filterRequest.firstPartyUrl == QStringLiteral("")))
410 //qDebug().noquote().nospace() << "Third-party request blocked: " << filterRequest.origin;
416 // Allow the request if local storage is enabled.
417 if (privacyWebEngineViewPointer->localStorageEnabled)
419 //qDebug().noquote().nospace() << "Request allowed by local storage: " << filterRequest.origin;
425 //qDebug().noquote().nospace() << "Request blocked by default: " << filterRequest.origin;
427 // Block any remaining local storage requests.
431 // Disable JavaScript by default (this prevents JavaScript from being enabled on a new tab before domain settings are loaded).
432 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
434 // Don't allow JavaScript to open windows.
435 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
437 // Allow keyboard navigation between links and input fields.
438 webEngineSettingsPointer->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, Settings::spatialNavigation());
440 // Enable full screen support.
441 webEngineSettingsPointer->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
443 // Require user interaction to play media.
444 webEngineSettingsPointer->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, true);
446 // Limit WebRTC to public IP addresses.
447 webEngineSettingsPointer->setAttribute(QWebEngineSettings::WebRTCPublicInterfacesOnly, true);
449 // Enable the PDF viewer (it should be enabled by default, but it is nice to be explicit in case the defaults change).
450 webEngineSettingsPointer->setAttribute(QWebEngineSettings::PdfViewerEnabled, true);
452 // Plugins must be enabled for the PDF viewer to work. <https://doc.qt.io/qt-5/qtwebengine-features.html#pdf-file-viewing>
453 webEngineSettingsPointer->setAttribute(QWebEngineSettings::PluginsEnabled, true);
455 // Update the blocked requests action.
456 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::requestBlocked, [this, privacyWebEngineViewPointer] (const int blockedRequests)
458 // Update the blocked requests action if the specified privacy WebEngine view is the current privacy WebEngine view.
459 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
460 emit blockedRequestsUpdated(blockedRequests);
463 // Update the cookies action.
464 connect(privacyWebEngineViewPointer, &PrivacyWebEngineView::numberOfCookiesChanged, [this, privacyWebEngineViewPointer] (const int numberOfCookies)
466 // Update the cookie action if the specified privacy WebEngine view is the current privacy WebEngine view.
467 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
468 emit cookiesChanged(numberOfCookies);
471 // Process cookie changes.
472 connect(webEngineCookieStorePointer, SIGNAL(cookieAdded(QNetworkCookie)), privacyWebEngineViewPointer, SLOT(addCookieToList(QNetworkCookie)));
473 connect(webEngineCookieStorePointer, SIGNAL(cookieRemoved(QNetworkCookie)), privacyWebEngineViewPointer, SLOT(removeCookieFromList(QNetworkCookie)));
475 // Get a list of durable cookies.
476 QList<QNetworkCookie*> *durableCookiesListPointer = CookiesDatabase::getCookies();
478 // Add the durable cookies to the store.
479 for (QNetworkCookie *cookiePointer : *durableCookiesListPointer)
480 addCookieToStore(*cookiePointer, webEngineCookieStorePointer);
482 // Enable spell checking.
483 webEngineProfilePointer->setSpellCheckEnabled(true);
485 // Set the spell check language.
486 webEngineProfilePointer->setSpellCheckLanguages(Settings::spellCheckLanguages());
488 // Populate the zoom factor. This is necessary if a URL is being loaded, like a local URL, that does not trigger `applyDomainSettings()`.
489 privacyWebEngineViewPointer->setZoomFactor(Settings::zoomFactor());
491 // Update the UI when domain settings are applied.
492 connect(privacyWebEngineViewPointer, SIGNAL(updateUi(const PrivacyWebEngineView*)), this, SLOT(updateUiFromWebEngineView(const PrivacyWebEngineView*)));
494 // Move to the new tab if it is not a background tab.
496 qTabWidgetPointer->setCurrentIndex(newTabIndex);
498 // Clear the URL line edit focus so that it populates correctly when opening a new tab from the context menu.
499 if (removeUrlLineEditFocus)
500 emit clearUrlLineEditFocus();
502 if (urlString != nullptr)
503 privacyWebEngineViewPointer->load(QUrl::fromUserInput(urlString));
505 // Return the privacy WebEngine view pointer.
506 return privacyWebEngineViewPointer;
509 void TabWidget::applyApplicationSettings()
511 // Set the tab position.
512 if (Settings::tabsOnTop())
513 qTabWidgetPointer->setTabPosition(QTabWidget::North);
515 qTabWidgetPointer->setTabPosition(QTabWidget::South);
517 // Get the number of tabs.
518 int numberOfTabs = qTabWidgetPointer->count();
520 // Apply the spatial navigation settings to each WebEngine.
521 for (int i = 0; i < numberOfTabs; ++i) {
522 // Get the WebEngine view pointer.
523 PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild<PrivacyWebEngineView *>();
525 // Apply the spatial navigation settings to each page.
526 privacyWebEngineViewPointer->page()->settings()->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, Settings::spatialNavigation());
529 // Set the search engine URL.
530 searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
532 // Emit the update search engine actions signal.
533 emit updateSearchEngineActions(Settings::searchEngine(), true);
536 void TabWidget::applyDomainSettingsAndReload()
538 // Get the number of tabs.
539 int numberOfTabs = qTabWidgetPointer->count();
541 // Apply the domain settings to each WebEngine.
542 for (int i = 0; i < numberOfTabs; ++i) {
543 // Get the WebEngine view pointer.
544 PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild<PrivacyWebEngineView *>();
546 // Apply the spatial navigation settings to each page.
547 privacyWebEngineViewPointer->applyDomainSettings(privacyWebEngineViewPointer->url().host(), true);
551 void TabWidget::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
553 // Store the search engine name.
554 QString searchEngineName = searchEngineActionPointer->text();
556 // Strip out any `&` characters.
557 searchEngineName.remove('&');
559 // Store the search engine string.
560 searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
562 // Update the search engine actions.
563 emit updateSearchEngineActions(searchEngineName, false);
566 void TabWidget::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
568 // Get the user agent name.
569 QString userAgentName = userAgentActionPointer->text();
571 // Strip out any `&` characters.
572 userAgentName.remove('&');
574 // Apply the user agent.
575 currentWebEngineProfilePointer->setHttpUserAgent(userAgentHelperPointer->getUserAgentFromTranslatedName(userAgentName));
577 // Update the user agent actions.
578 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), false);
580 // Reload the website.
581 currentPrivacyWebEngineViewPointer->reload();
584 void TabWidget::applyOnTheFlyZoomFactor(const double zoomFactorDouble) const
586 // Set the zoom factor.
587 currentPrivacyWebEngineViewPointer->setZoomFactor(zoomFactorDouble);
590 void TabWidget::applySpellCheckLanguages() const
592 // Get the number of tab.
593 int numberOfTabs = qTabWidgetPointer->count();
595 // Set the spell check languages for each tab.
596 for (int i = 0; i < numberOfTabs; ++i)
598 // Get the WebEngine view pointer.
599 PrivacyWebEngineView *privacyWebEngineViewPointer = qTabWidgetPointer->widget(i)->findChild<PrivacyWebEngineView *>();
601 // Get the WebEngine page pointer.
602 QWebEnginePage *webEnginePagePointer = privacyWebEngineViewPointer->page();
604 // Get the WebEngine profile pointer.
605 QWebEngineProfile *webEngineProfilePointer = webEnginePagePointer->profile();
607 // Set the spell check languages.
608 webEngineProfilePointer->setSpellCheckLanguages(Settings::spellCheckLanguages());
612 void TabWidget::back() const
615 currentPrivacyWebEngineViewPointer->back();
618 void TabWidget::deleteAllCookies() const
620 // Delete all the cookies.
621 currentWebEngineCookieStorePointer->deleteAllCookies();
624 void TabWidget::deleteCookieFromStore(const QNetworkCookie &cookie) const
626 // Delete the cookie.
627 currentWebEngineCookieStorePointer->deleteCookie(cookie);
630 void TabWidget::deleteTab(const int tabIndex)
632 // Get the tab splitter widget.
633 QWidget *tabSplitterWidgetPointer = qTabWidgetPointer->widget(tabIndex);
635 // Get the WebEngine views.
636 PrivacyWebEngineView *privacyWebEngineViewPointer = tabSplitterWidgetPointer->findChild<PrivacyWebEngineView *>();
637 DevToolsWebEngineView *devToolsWebEngineViewPointer = tabSplitterWidgetPointer->findChild<DevToolsWebEngineView *>();
639 // Process the tab delete according to the number of tabs.
640 if (qTabWidgetPointer->count() > 1) // There is more than one tab.
643 qTabWidgetPointer->removeTab(tabIndex);
645 // Delete the WebEngine pages to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
646 delete privacyWebEngineViewPointer->page();
647 delete devToolsWebEngineViewPointer->page();
649 // Delete the WebEngine views.
650 delete privacyWebEngineViewPointer;
651 delete devToolsWebEngineViewPointer;
653 // Delete the tab splitter widget.
654 delete tabSplitterWidgetPointer;
656 else // There is only one tab.
658 // Close Privacy Browser.
663 void TabWidget::findPrevious(const QString &text) const
665 // Store the current text.
666 currentPrivacyWebEngineViewPointer->findString = text;
668 // Find the previous text in the current privacy WebEngine.
669 if (currentPrivacyWebEngineViewPointer->findCaseSensitive)
670 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively|QWebEnginePage::FindBackward);
672 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindBackward);
675 void TabWidget::findText(const QString &text) const
677 // Store the current text.
678 currentPrivacyWebEngineViewPointer->findString = text;
680 // Find the text in the current privacy WebEngine.
681 if (currentPrivacyWebEngineViewPointer->findCaseSensitive)
682 currentPrivacyWebEngineViewPointer->findText(text, QWebEnginePage::FindCaseSensitively);
684 currentPrivacyWebEngineViewPointer->findText(text);
686 // Clear the currently selected text in the WebEngine page if the find text is empty.
688 currentWebEnginePagePointer->action(QWebEnginePage::Unselect)->activate(QAction::Trigger);
691 void TabWidget::findTextFinished(const QWebEngineFindTextResult &findTextResult)
693 // Update the find text UI if it wasn't simply wiping the current find text selection. Otherwise the UI temporarily flashes `0/0`.
694 if (wipingCurrentFindTextSelection) // The current selection is being wiped.
697 wipingCurrentFindTextSelection = false;
699 else // A new search has been performed.
702 currentPrivacyWebEngineViewPointer->findTextResult = findTextResult;
705 emit updateFindTextResults(findTextResult);
709 void TabWidget::forward() const
712 currentPrivacyWebEngineViewPointer->forward();
715 void TabWidget::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) const
718 emit fullScreenRequested(fullScreenRequest.toggleOn());
720 // Accept the request.
721 fullScreenRequest.accept();
724 std::list<QNetworkCookie>* TabWidget::getCookieList() const
726 // Return the current cookie list.
727 return currentPrivacyWebEngineViewPointer->cookieListPointer;
730 QIcon TabWidget::getCurrentTabFavoritIcon() const
732 // Return the current Privacy WebEngine favorite icon.
733 return currentPrivacyWebEngineViewPointer->favoriteIcon;
736 QString TabWidget::getCurrentTabTitle() const
738 // Return the current Privacy WebEngine title.
739 return currentPrivacyWebEngineViewPointer->title();
742 QString TabWidget::getCurrentTabUrl() const
744 // Return the current Privacy WebEngine URL as a string.
745 return currentPrivacyWebEngineViewPointer->url().toString();
748 QString TabWidget::getCurrentUserAgent() const
750 // Return the current WebEngine user agent.
751 return currentWebEngineProfilePointer->httpUserAgent();
754 QString& TabWidget::getDomainSettingsName() const
756 // Return the domain settings name.
757 return currentPrivacyWebEngineViewPointer->domainSettingsName;
760 void TabWidget::home() const
762 // Load the homepage.
763 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(Settings::homepage()));
766 PrivacyWebEngineView* TabWidget::loadBlankInitialWebsite()
768 // Apply the application settings.
769 applyApplicationSettings();
771 // Return the current privacy WebEngine view pointer.
772 return currentPrivacyWebEngineViewPointer;
775 void TabWidget::loadInitialWebsite()
777 // Apply the application settings.
778 applyApplicationSettings();
780 // Get the arguments.
781 QStringList argumentsStringList = qApp->arguments();
783 // Check to see if the arguments lists contains a URL.
784 if (argumentsStringList.size() > 1)
786 // Load the URL from the arguments list.
787 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(argumentsStringList.at(1)));
791 // Load the homepage.
796 void TabWidget::loadUrlFromLineEdit(QString url) const
798 // Decide if the text is more likely to be a URL or a search.
799 if (url.startsWith("file://") || url.startsWith("view-source:")) // The text is likely a file or view source URL.
802 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
804 else if (url.contains(".")) // The text is likely a URL.
806 // Check if the URL does not start with a valid protocol.
807 if (!url.startsWith("http"))
809 // Add `https://` to the beginning of the URL.
810 url = "https://" + url;
814 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(url));
816 else // The text is likely a search.
819 currentPrivacyWebEngineViewPointer->load(QUrl::fromUserInput(searchEngineUrl + url));
823 void TabWidget::mouseBack() const
825 // Go back if possible.
826 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoBack())
828 // Clear the URL line edit focus.
829 emit clearUrlLineEditFocus();
832 currentPrivacyWebEngineViewPointer->back();
836 void TabWidget::mouseForward() const
838 // Go forward if possible.
839 if (currentPrivacyWebEngineViewPointer->isActiveWindow() && currentWebEngineHistoryPointer->canGoForward())
841 // Clear the URL line edit focus.
842 emit clearUrlLineEditFocus();
845 currentPrivacyWebEngineViewPointer->forward();
849 void TabWidget::pageLinkHovered(const QString &linkUrl) const
851 // Emit a signal so that the browser window can update the status bar.
852 emit linkHovered(linkUrl);
855 void TabWidget::stopLoadingFavoriteIconMovie() const
857 // Stop the loading favorite icon movie. Otherwise, the browser will crash if a second window is closed while a tab in it is loading. <https://redmine.stoutner.com/issues/1010>
858 loadingFavoriteIconMoviePointer->stop();
861 void TabWidget::print() const
866 // Set the resolution to be 300 dpi.
867 printer.setResolution(300);
869 // Create a printer dialog.
870 QPrintDialog printDialog(&printer, currentPrivacyWebEngineViewPointer);
872 // Display the dialog and print the page if instructed.
873 if (printDialog.exec() == QDialog::Accepted)
874 printWebpage(&printer);
877 void TabWidget::printPreview() const
882 // Set the resolution to be 300 dpi.
883 printer.setResolution(300);
885 // Create a print preview dialog.
886 QPrintPreviewDialog printPreviewDialog(&printer, currentPrivacyWebEngineViewPointer);
888 // Generate the print preview.
889 connect(&printPreviewDialog, SIGNAL(paintRequested(QPrinter *)), this, SLOT(printWebpage(QPrinter *)));
891 // Display the dialog.
892 printPreviewDialog.exec();
895 void TabWidget::printWebpage(QPrinter *printerPointer) const
897 // Create an event loop. For some reason, the print preview doesn't produce any output unless it is run inside an event loop.
898 QEventLoop eventLoop;
900 // Print the webpage, converting the callback above into a `QWebEngineCallback<bool>`.
901 // Printing requires that the printer be a pointer, not a reference, or it will crash with much cursing.
902 currentWebEnginePagePointer->print(printerPointer, [&eventLoop](bool printSuccess)
904 // Instruct the compiler to ignore the unused parameter.
915 void TabWidget::refresh() const
917 // Reset the HTTP authentication dialog counter.
918 currentPrivacyWebEngineViewPointer->httpAuthenticationDialogsDisplayed = 0;
920 // Reload the website.
921 currentPrivacyWebEngineViewPointer->reload();
924 void TabWidget::reloadAndBypassCache() const
926 // Reload the website, bypassing the cache.
927 currentWebEnginePagePointer->triggerAction(QWebEnginePage::ReloadAndBypassCache);
930 void TabWidget::saveArchive()
932 // Get the suggested file name.
933 QString suggestedFileName = currentPrivacyWebEngineViewPointer->title() + ".mht";
935 // Get the download directory.
936 QString downloadDirectory = Settings::downloadDirectory();
938 // Resolve the system download directory if specified.
939 if (downloadDirectory == QLatin1String("System Download Directory"))
940 downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
942 // Get a file path from the file picker.
943 QString saveFilePath = QFileDialog::getSaveFileName(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory + QLatin1Char('/') + suggestedFileName);
945 // Save the webpage as an archive if the file save path is populated.
946 if (!saveFilePath.isEmpty())
948 // Update the download directory if specified.
949 if (Settings::autoUpateDownloadDirectory())
950 updateDownloadDirectory(saveFilePath);
952 // Set the saving archive flag. Otherwise, a second download tries to run.
953 savingArchive = true;
956 currentWebEnginePagePointer->save(saveFilePath);
960 void TabWidget::setTabBarVisible(const bool visible) const
962 // Set the tab bar visibility.
963 qTabWidgetPointer->tabBar()->setVisible(visible);
966 void TabWidget::showSaveDialog(QWebEngineDownloadItem *webEngineDownloadItemPointer)
968 // Only show the save dialog if an archive is not currently being saved. Otherwise, two save dialogs will be shown.
971 // Get the download attributes.
972 QUrl downloadUrl = webEngineDownloadItemPointer->url();
973 QString mimeTypeString = webEngineDownloadItemPointer->mimeType();
974 QString suggestedFileName = webEngineDownloadItemPointer->suggestedFileName();
975 int totalBytes = webEngineDownloadItemPointer->totalBytes();
977 // Check to see if Privacy Browser is not running KDE or if local storage (cookies) is enabled.
978 if (!isRunningKde || currentPrivacyWebEngineViewPointer->localStorageEnabled) // KDE is not running or local storage (cookies) is enabled. Use WebEngine's downloader.
980 // Instantiate the save dialog.
981 SaveDialog *saveDialogPointer = new SaveDialog(this, downloadUrl, mimeTypeString, totalBytes);
983 // Display the save dialog.
984 int saveDialogResult = saveDialogPointer->exec();
986 // Process the save dialog results.
987 if (saveDialogResult == QDialog::Accepted) // Save was selected.
989 // Get the download directory.
990 QString downloadDirectory = Settings::downloadDirectory();
992 // Resolve the system download directory if specified.
993 if (downloadDirectory == QLatin1String("System Download Directory"))
994 downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
996 // Get a file path from the file picker.
997 QString saveFilePath = QFileDialog::getSaveFileName(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory + QLatin1Char('/') + suggestedFileName);
999 // Process the save file path.
1000 if (!saveFilePath.isEmpty()) // The file save path is populated.
1002 // Update the download directory if specified.
1003 if (Settings::autoUpateDownloadDirectory())
1004 updateDownloadDirectory(saveFilePath);
1006 // Create a save file path file info.
1007 QFileInfo saveFilePathFileInfo = QFileInfo(saveFilePath);
1009 // Get the canonical save path and file name.
1010 QString absoluteSavePath = saveFilePathFileInfo.absolutePath();
1011 QString saveFileName = saveFilePathFileInfo.fileName();
1013 // Set the download directory and file name.
1014 webEngineDownloadItemPointer->setDownloadDirectory(absoluteSavePath);
1015 webEngineDownloadItemPointer->setDownloadFileName(saveFileName);
1017 // Create a file download notification.
1018 KNotification *fileDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
1020 // Set the notification title.
1021 fileDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
1023 // Set the notification text.
1024 fileDownloadNotificationPointer->setText(i18nc("Downloading notification text", "Downloading %1", saveFileName));
1026 // Get the download icon from the theme.
1027 QIcon downloadIcon = QIcon::fromTheme(QLatin1String("download"), QIcon::fromTheme(QLatin1String("document-save")));
1029 // Set the notification icon.
1030 fileDownloadNotificationPointer->setIconName(downloadIcon.name());
1032 // Set the action list cancel button.
1033 fileDownloadNotificationPointer->setActions(QStringList({i18nc("Download notification action","Cancel")}));
1035 // Prevent the notification from being autodeleted if it is closed. Otherwise, the updates to the notification below cause a crash.
1036 fileDownloadNotificationPointer->setAutoDelete(false);
1038 // Handle clicks on the cancel button.
1039 connect(fileDownloadNotificationPointer, &KNotification::action1Activated, [webEngineDownloadItemPointer, saveFileName] ()
1041 // Cancel the download.
1042 webEngineDownloadItemPointer->cancel();
1044 // Create a file download notification.
1045 KNotification *canceledDownloadNotificationPointer = new KNotification(QLatin1String("FileDownload"));
1047 // Set the notification title.
1048 canceledDownloadNotificationPointer->setTitle(i18nc("Download notification title", "Download"));
1050 // Set the new text.
1051 canceledDownloadNotificationPointer->setText(i18nc("Download canceled notification", "%1 download canceled", saveFileName));
1053 // Set the notification icon.
1054 canceledDownloadNotificationPointer->setIconName(QLatin1String("download"));
1056 // Display the notification.
1057 canceledDownloadNotificationPointer->sendEvent();
1060 // Update the notification when the download progresses.
1061 connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::downloadProgress, [fileDownloadNotificationPointer, saveFileName] (qint64 bytesReceived, qint64 totalBytes)
1063 // Set the new text. Total bytes will be 0 if the download size is unknown.
1066 // Calculate the download percentage.
1067 int downloadPercentage = 100 * bytesReceived / totalBytes;
1069 // Set the file download notification text.
1070 fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1\% of %2 downloaded (%3 of %4 bytes)", downloadPercentage, saveFileName,
1071 bytesReceived, totalBytes));
1075 // Set the file download notification text.
1076 fileDownloadNotificationPointer->setText(i18nc("Download progress notification text", "%1: %2 bytes downloaded", saveFileName, bytesReceived));
1079 // Display the updated notification.
1080 fileDownloadNotificationPointer->update();
1083 // Update the notification when the download finishes. The save file name must be copied into the lambda or a crash occurs.
1084 connect(webEngineDownloadItemPointer, &QWebEngineDownloadItem::finished, [fileDownloadNotificationPointer, saveFileName, saveFilePath] ()
1086 // Set the new text.
1087 fileDownloadNotificationPointer->setText(i18nc("Download finished notification text", "%1 download finished", saveFileName));
1089 // Set the URL so the file options will be displayed.
1090 fileDownloadNotificationPointer->setUrls(QList<QUrl> {QUrl(saveFilePath)});
1092 // Remove the actions from the notification.
1093 fileDownloadNotificationPointer->setActions(QStringList());
1095 // Set the notification to disappear after a timeout.
1096 fileDownloadNotificationPointer->setFlags(KNotification::CloseOnTimeout);
1098 // Display the updated notification.
1099 fileDownloadNotificationPointer->update();
1102 // Display the notification.
1103 fileDownloadNotificationPointer->sendEvent();
1105 // Start the download.
1106 webEngineDownloadItemPointer->accept();
1108 else // The file save path is not populated.
1110 // Cancel the download.
1111 webEngineDownloadItemPointer->cancel();
1114 else // Cancel was selected.
1116 // Cancel the download.
1117 webEngineDownloadItemPointer->cancel();
1120 else // KDE is running and local storage (cookies) is disabled. Use KDE's native downloader.
1121 // This must use the show command to launch a separate dialog which cancels WebEngine's automatic background download of the file to a temporary location.
1123 // Instantiate the save dialog. `true` instructs it to use the native downloader
1124 SaveDialog *saveDialogPointer = new SaveDialog(this, downloadUrl, mimeTypeString, totalBytes, suggestedFileName, true);
1126 // Connect the save button.
1127 connect(saveDialogPointer, SIGNAL(useNativeKdeDownloader(QUrl &, QString &)), this, SLOT(useNativeKdeDownloader(QUrl &, QString &)));
1130 saveDialogPointer->show();
1134 // Reset the saving archive flag.
1135 savingArchive = false;
1138 void TabWidget::stop() const
1140 // Stop the loading of the current privacy WebEngine.
1141 currentPrivacyWebEngineViewPointer->stop();
1144 void TabWidget::toggleDeveloperTools(const bool enabled) const
1146 // Get a handle for the current developer tools WebEngine.
1147 DevToolsWebEngineView *devToolsWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild<DevToolsWebEngineView *>();
1151 // Set the zoom factor on the development tools WebEngine.
1152 devToolsWebEngineViewPointer->setZoomFactor(currentWebEnginePagePointer->zoomFactor());
1154 // Enable the development tools.
1155 currentWebEnginePagePointer->setDevToolsPage(devToolsWebEngineViewPointer->page());
1157 // Enable JavaScript on the development tools WebEngine.
1158 devToolsWebEngineViewPointer->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
1160 // Display the developer tools.
1161 devToolsWebEngineViewPointer->setVisible(true);
1165 // Disable JavaScript on the development tools WebEngine to prevent error messages from being written to the console.
1166 devToolsWebEngineViewPointer->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
1168 // Disable the development tools.
1169 currentWebEnginePagePointer->setDevToolsPage(nullptr);
1171 // Hide the developer tools.
1172 devToolsWebEngineViewPointer->setVisible(false);
1176 void TabWidget::toggleDomStorage() const
1178 // Toggle DOM storage.
1179 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
1181 // Update the DOM storage action.
1182 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
1184 // Reload the website.
1185 currentPrivacyWebEngineViewPointer->reload();
1188 void TabWidget::toggleFindCaseSensitive(const QString &text)
1190 // Toggle find case sensitive.
1191 currentPrivacyWebEngineViewPointer->findCaseSensitive = !currentPrivacyWebEngineViewPointer->findCaseSensitive;
1193 // Set the wiping current find text selection flag.
1194 wipingCurrentFindTextSelection = true;
1196 // Wipe the previous search. Otherwise currently highlighted words will remain highlighted.
1197 findText(QLatin1String(""));
1199 // Update the find text.
1203 void TabWidget::toggleJavaScript() const
1205 // Toggle JavaScript.
1206 currentWebEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, !currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
1208 // Update the JavaScript action.
1209 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
1211 // Reload the website.
1212 currentPrivacyWebEngineViewPointer->reload();
1215 void TabWidget::toggleLocalStorage()
1217 // Toggle local storage.
1218 currentPrivacyWebEngineViewPointer->localStorageEnabled = !currentPrivacyWebEngineViewPointer->localStorageEnabled;
1220 // Update the local storage action.
1221 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
1223 // Reload the website.
1224 currentPrivacyWebEngineViewPointer->reload();
1227 void TabWidget::updateDownloadDirectory(QString newDownloadDirectory) const
1229 // Remove the file name from the save file path.
1230 newDownloadDirectory.truncate(newDownloadDirectory.lastIndexOf(QLatin1Char('/')));
1232 // Update the download location.
1233 Settings::setDownloadDirectory(newDownloadDirectory);
1235 // Get a handle for the KConfig skeleton.
1236 KConfigSkeleton *kConfigSkeletonPointer = Settings::self();
1238 // Write the settings to disk.
1239 kConfigSkeletonPointer->save();
1242 void TabWidget::updateUiFromWebEngineView(const PrivacyWebEngineView *privacyWebEngineViewPointer) const
1244 // Only update the UI if the signal was emitted from the current privacy WebEngine.
1245 if (privacyWebEngineViewPointer == currentPrivacyWebEngineViewPointer)
1248 emit updateDefaultZoomFactor(currentPrivacyWebEngineViewPointer->defaultZoomFactor);
1249 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QLatin1String(""));
1250 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
1251 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
1252 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
1253 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
1254 emit updateZoomActions(currentPrivacyWebEngineViewPointer->zoomFactor());
1258 void TabWidget::updateUiWithTabSettings()
1260 // Update the current WebEngine pointers.
1261 currentPrivacyWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild<PrivacyWebEngineView *>();
1262 currentWebEngineSettingsPointer = currentPrivacyWebEngineViewPointer->settings();
1263 currentWebEnginePagePointer = currentPrivacyWebEngineViewPointer->page();
1264 currentWebEngineProfilePointer = currentWebEnginePagePointer->profile();
1265 currentWebEngineHistoryPointer = currentWebEnginePagePointer->history();
1266 currentWebEngineCookieStorePointer = currentWebEngineProfilePointer->cookieStore();
1268 // Clear the URL line edit focus.
1269 emit clearUrlLineEditFocus();
1271 // Get a handle for the development tools WebEngine view.
1272 DevToolsWebEngineView *devToolsWebEngineViewPointer = qTabWidgetPointer->currentWidget()->findChild<DevToolsWebEngineView *>();
1274 // Update the actions.
1275 emit blockedRequestsUpdated(currentPrivacyWebEngineViewPointer->blockedRequests);
1276 emit cookiesChanged(currentPrivacyWebEngineViewPointer->cookieListPointer->size());
1277 emit updateDefaultZoomFactor(currentPrivacyWebEngineViewPointer->defaultZoomFactor);
1278 emit updateBackAction(currentWebEngineHistoryPointer->canGoBack());
1279 emit updateDeveloperToolsAction(devToolsWebEngineViewPointer->isVisible());
1280 emit updateDomStorageAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
1281 emit updateForwardAction(currentWebEngineHistoryPointer->canGoForward());
1282 emit updateJavaScriptAction(currentWebEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
1283 emit updateLocalStorageAction(currentPrivacyWebEngineViewPointer->localStorageEnabled);
1284 emit updateUserAgentActions(currentWebEngineProfilePointer->httpUserAgent(), true);
1285 emit updateZoomActions(currentPrivacyWebEngineViewPointer->zoomFactor());
1288 emit updateWindowTitle(currentPrivacyWebEngineViewPointer->title());
1289 emit updateDomainSettingsIndicator(currentPrivacyWebEngineViewPointer->domainSettingsName != QLatin1String(""));
1290 emit updateUrlLineEdit(currentPrivacyWebEngineViewPointer->url());
1292 // Update the find text.
1293 emit updateFindText(currentPrivacyWebEngineViewPointer->findString, currentPrivacyWebEngineViewPointer->findCaseSensitive);
1294 emit updateFindTextResults(currentPrivacyWebEngineViewPointer->findTextResult);
1296 // Update the progress bar.
1297 if (currentPrivacyWebEngineViewPointer->loadProgressInt >= 0)
1298 emit showProgressBar(currentPrivacyWebEngineViewPointer->loadProgressInt);
1300 emit hideProgressBar();
1303 void TabWidget::useNativeKdeDownloader(QUrl &downloadUrl, QString &suggestedFileName)
1305 // Get the download directory.
1306 QString downloadDirectory = Settings::downloadDirectory();
1308 // Resolve the system download directory if specified.
1309 if (downloadDirectory == QLatin1String("System Download Directory"))
1310 downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
1312 // Create a save file dialog.
1313 QFileDialog *saveFileDialogPointer = new QFileDialog(this, i18nc("Save file dialog caption", "Save File"), downloadDirectory);
1315 // Tell the dialog to use a save button.
1316 saveFileDialogPointer->setAcceptMode(QFileDialog::AcceptSave);
1318 // Populate the file name from the download item pointer.
1319 saveFileDialogPointer->selectFile(suggestedFileName);
1321 // Prevent interaction with the parent window while the dialog is open.
1322 saveFileDialogPointer->setWindowModality(Qt::WindowModal);
1324 // Process the saving of the file. The save file dialog pointer must be captured directly instead of by reference or nasty crashes occur.
1325 auto saveFile = [saveFileDialogPointer, downloadUrl, this] ()
1327 // Get the save location. The dialog box should only allow the selecting of one file location.
1328 QUrl saveLocation = saveFileDialogPointer->selectedUrls().value(0);
1330 // Update the download directory if specified.
1331 if (Settings::autoUpateDownloadDirectory())
1332 updateDownloadDirectory(saveLocation.toLocalFile());
1334 // Create a file copy job. `-1` creates the file with default permissions.
1335 KIO::FileCopyJob *fileCopyJobPointer = KIO::file_copy(downloadUrl, saveLocation, -1, KIO::Overwrite);
1337 // Set the download job to display any warning and error messages.
1338 fileCopyJobPointer->uiDelegate()->setAutoWarningHandlingEnabled(true);
1339 fileCopyJobPointer->uiDelegate()->setAutoErrorHandlingEnabled(true);
1341 // Start the download.
1342 fileCopyJobPointer->start();
1345 // Handle clicks on the save button.
1346 connect(saveFileDialogPointer, &QDialog::accepted, this, saveFile);
1349 saveFileDialogPointer->show();