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 "BrowserView.h"
23 #include "ui_BrowserView.h"
24 #include "filters/MouseEventFilter.h"
25 #include "helpers/DomainsDatabaseHelper.h"
26 #include "helpers/SearchEngineHelper.h"
27 #include "helpers/UserAgentHelper.h"
28 #include "interceptors/UrlRequestInterceptor.h"
29 #include "windows/BrowserWindow.h"
31 // Qt framework headers.
34 // Initialize the public static variables.
35 QString BrowserView::webEngineDefaultUserAgent = QStringLiteral("");
37 // Construct the class.
38 BrowserView::BrowserView(QWidget *parent) : QWidget(parent)
40 // Initialize the variables.
41 privacyWebEngineListPointer = new QList<PrivacyWebEngine*>;
43 // Instantiate the browser view UI.
44 Ui::BrowserView browserViewUi;
47 browserViewUi.setupUi(this);
49 // Get handles for the views.
50 webEngineViewPointer = browserViewUi.webEngineView;
52 // Create an off-the-record profile (the default when no profile name is specified).
53 webEngineProfilePointer = new QWebEngineProfile(QStringLiteral(""));
55 // Create a WebEngine page.
56 webEnginePagePointer = new QWebEnginePage(webEngineProfilePointer);
58 // Set the WebEngine page.
59 webEngineViewPointer->setPage(webEnginePagePointer);
61 // Get handles for the aspects of the WebEngine.
62 webEngineHistoryPointer = webEnginePagePointer->history();
63 webEngineSettingsPointer = webEngineViewPointer->settings();
64 webEngineCookieStorePointer = webEngineProfilePointer->cookieStore();
66 // Initialize the current privacy web engine pointer.
67 currentPrivacyWebEnginePointer = new PrivacyWebEngine(webEngineViewPointer);
69 // Populate the privacy web engine list.
70 privacyWebEngineListPointer->append(currentPrivacyWebEnginePointer);
72 // Set the local storage filter.
73 webEngineCookieStorePointer->setCookieFilter([this](const QWebEngineCookieStore::FilterRequest &filterRequest)
75 // qDebug() << "Page URL: " << filterRequest.firstPartyUrl << ", Local storage URL: " << filterRequest.origin << ", Is third-party: " << filterRequest.thirdParty;
77 // Block all third party local storage requests, including the sneaky ones that don't register a first party URL.
78 if (filterRequest.thirdParty || (filterRequest.firstPartyUrl == QStringLiteral("")))
81 // Check each tab to see if this local storage request should be allowed.
82 for (PrivacyWebEngine *privacyWebEnginePointer : *privacyWebEngineListPointer)
84 // Allow this local storage request if it comes from a tab with local storage enabled.
85 if (privacyWebEnginePointer->localStorageEnabled && (webEngineViewPointer->url().host() == filterRequest.firstPartyUrl.host()))
89 // Block any remaining local storage requests.
93 // Process cookie changes.
94 connect(webEngineCookieStorePointer, SIGNAL(cookieAdded(QNetworkCookie)), this, SLOT(cookieAdded(QNetworkCookie)));
95 connect(webEngineCookieStorePointer, SIGNAL(cookieRemoved(QNetworkCookie)), this, SLOT(cookieRemoved(QNetworkCookie)));
97 // Store a copy of the WebEngine default user agent.
98 webEngineDefaultUserAgent = webEngineProfilePointer->httpUserAgent();
100 // Update the URL line edit when the URL changes.
101 connect(webEngineViewPointer, SIGNAL(urlChanged(const QUrl)), this, SLOT(updateUrl(const QUrl)));
103 // Update the progress bar.
104 connect(webEngineViewPointer, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
105 connect(webEngineViewPointer, SIGNAL(loadProgress(const int)), this, SLOT(loadProgress(const int)));
106 connect(webEngineViewPointer, SIGNAL(loadFinished(const bool)), this, SLOT(loadFinished()));
108 // Instantiate the mouse event filter pointer.
109 MouseEventFilter *mouseEventFilterPointer = new MouseEventFilter();
111 // Install the mouse event filter.
112 qApp->installEventFilter(mouseEventFilterPointer);
114 // Process mouse forward and back commands.
115 connect(mouseEventFilterPointer, SIGNAL(mouseBack()), this, SLOT(mouseBack()));
116 connect(mouseEventFilterPointer, SIGNAL(mouseForward()), this, SLOT(mouseForward()));
118 // Listen for hovered link URLs.
119 connect(webEnginePagePointer, SIGNAL(linkHovered(const QString)), this, SLOT(pageLinkHovered(const QString)));
121 // Instantiate the URL request interceptor.
122 UrlRequestInterceptor *urlRequestInterceptorPointer = new UrlRequestInterceptor();
124 // Set the URL request interceptor.
125 webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer);
127 // Reapply the domain settings when the host changes.
128 connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings(QString)), this, SLOT(applyDomainSettingsWithoutReloading(QString)));
130 // Don't allow JavaScript to open windows.
131 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
133 // Allow keyboard navigation.
134 webEngineSettingsPointer->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, true);
136 // Enable full screen support.
137 webEngineSettingsPointer->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
139 // Require user interaction to play media.
140 webEngineSettingsPointer->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, true);
142 // Limit WebRTC to public IP addresses.
143 webEngineSettingsPointer->setAttribute(QWebEngineSettings::WebRTCPublicInterfacesOnly, true);
145 // Set the focus on the WebEngine view.
146 webEngineViewPointer->setFocus();
149 BrowserView::~BrowserView()
151 // Delay the deletion of the WebEngine page to prevent the following error: `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
152 webEnginePagePointer->deleteLater();
155 void BrowserView::addCookieToStore(QNetworkCookie &cookie) const
160 // 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>
161 if (!cookie.domain().startsWith(QStringLiteral(".")))
164 url.setHost(cookie.domain());
165 url.setScheme(QStringLiteral("https"));
167 // Clear the domain from the cookie.
168 cookie.setDomain(QStringLiteral(""));
171 // Add the cookie to the store.
172 webEngineCookieStorePointer->setCookie(cookie, url);
175 void BrowserView::applyApplicationSettings()
177 // Set the search engine URL.
178 searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
180 // Emit the update search engine actions signal.
181 emit updateSearchEngineActions(Settings::searchEngine());
184 // 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.
185 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
186 void BrowserView::applyDomainSettingsAndReload()
188 // Apply the domain settings. `true` reloads the website.
189 applyDomainSettings(webEngineViewPointer->url().host(), true);
192 // 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.
193 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
194 void BrowserView::applyDomainSettingsWithoutReloading(const QString &hostname)
196 // Apply the domain settings `false` does not reload the website.
197 applyDomainSettings(hostname, false);
200 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
201 void BrowserView::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
203 // Get the record for the hostname.
204 QSqlQuery domainQuery = DomainsDatabaseHelper::getDomainQuery(hostname);
206 // Check if the hostname has domain settings.
207 if (domainQuery.isValid()) // The hostname has domain settings.
209 // Get the domain record.
210 QSqlRecord domainRecord = domainQuery.record();
212 // Set the JavaScript status.
213 switch (domainRecord.field(DomainsDatabaseHelper::JAVASCRIPT).value().toInt())
215 // Set the default JavaScript status.
216 case (DomainsDatabaseHelper::SYSTEM_DEFAULT):
218 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
223 // Disable JavaScript.
224 case (DomainsDatabaseHelper::DISABLED):
226 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
231 // Enable JavaScript.
232 case (DomainsDatabaseHelper::ENABLED):
234 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
240 // Set the local storage status.
241 switch (domainRecord.field(DomainsDatabaseHelper::LOCAL_STORAGE).value().toInt())
243 // Set the default local storage status.
244 case (DomainsDatabaseHelper::SYSTEM_DEFAULT):
246 currentPrivacyWebEnginePointer->localStorageEnabled = Settings::localStorageEnabled();
251 // Disable local storage.
252 case (DomainsDatabaseHelper::DISABLED):
254 currentPrivacyWebEnginePointer->localStorageEnabled = false;
259 // Enable local storage.
260 case (DomainsDatabaseHelper::ENABLED):
262 currentPrivacyWebEnginePointer->localStorageEnabled = true;
268 // Set the DOM storage status.
269 switch (domainRecord.field(DomainsDatabaseHelper::DOM_STORAGE).value().toInt())
271 // Set the default DOM storage status.
272 case (DomainsDatabaseHelper::SYSTEM_DEFAULT):
274 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
279 // Disable DOM storage.
280 case (DomainsDatabaseHelper::DISABLED):
282 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
287 // Enable DOM storage.
288 case (DomainsDatabaseHelper::ENABLED):
290 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
296 // Set the user agent.
297 webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainRecord.field(DomainsDatabaseHelper::USER_AGENT).value().toString()));
299 // Check if a custom zoom factor is set.
300 if (domainRecord.field(DomainsDatabaseHelper::ZOOM_FACTOR).value().toInt())
302 // Store the current zoom factor.
303 currentZoomFactor = domainRecord.field(DomainsDatabaseHelper::CUSTOM_ZOOM_FACTOR).value().toDouble();
307 // Reset the current zoom factor.
308 currentZoomFactor = Settings::zoomFactor();
311 // Set the zoom factor. The use of `currentZoomFactor` can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
312 webEngineViewPointer->setZoomFactor(currentZoomFactor);
314 // Apply the domain settings palette to the URL line edit.
315 emit updateDomainSettingsIndicator(true, domainRecord.field(DomainsDatabaseHelper::DOMAIN_NAME).value().toString());
317 else // The hostname does not have domain settings.
319 // Set the JavaScript status.
320 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
322 // Set the local storage status.
323 currentPrivacyWebEnginePointer->localStorageEnabled = Settings::localStorageEnabled();
326 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
328 // Set the user agent.
329 webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
331 // Store the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
332 currentZoomFactor = Settings::zoomFactor();
334 // Set the zoom factor.
335 webEngineViewPointer->setZoomFactor(Settings::zoomFactor());
337 // Apply the no domain settings palette to the URL line edit.
338 emit updateDomainSettingsIndicator(false, QStringLiteral(""));
341 // Emit the update actions signals.
342 emit updateJavaScriptAction(webEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
343 emit updateLocalStorageAction(currentPrivacyWebEnginePointer->localStorageEnabled);
344 emit updateDomStorageAction(webEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
345 emit updateUserAgentActions(webEngineProfilePointer->httpUserAgent());
346 emit updateZoomFactorAction(webEngineViewPointer->zoomFactor());
348 // Reload the website if requested.
351 webEngineViewPointer->reload();
355 void BrowserView::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
357 // Store the search engine name.
358 QString searchEngineName = searchEngineActionPointer->text();
360 // Strip out any `&` characters.
361 searchEngineName.remove('&');
363 // Store the search engine string.
364 searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
367 void BrowserView::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
369 // Get the user agent name.
370 QString userAgentName = userAgentActionPointer->text();
372 // Strip out any `&` characters.
373 userAgentName.remove('&');
375 // Apply the user agent.
376 webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromTranslatedName(userAgentName));
378 // Reload the website.
379 webEngineViewPointer->reload();
382 // This can be const once <https://redmine.stoutner.com/issues/799> has been resolved.
383 void BrowserView::applyOnTheFlyZoomFactor(const double &zoomFactor)
385 // Update the current zoom factor. This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
386 currentZoomFactor = zoomFactor;
388 // Set the zoom factor.
389 webEngineViewPointer->setZoomFactor(zoomFactor);
392 void BrowserView::back() const
395 webEngineViewPointer->back();
398 void BrowserView::cookieAdded(const QNetworkCookie &cookie) const
400 // Add the cookie to the cookie list.
401 emit addCookie(cookie);
404 void BrowserView::cookieRemoved(const QNetworkCookie &cookie) const
406 // Remove the cookie from the cookie list.
407 emit removeCookie(cookie);
410 void BrowserView::deleteAllCookies() const
412 // Delete all the cookies.
413 webEngineCookieStorePointer->deleteAllCookies();
416 void BrowserView::deleteCookieFromStore(const QNetworkCookie &cookie) const
418 // Delete the cookie.
419 webEngineCookieStorePointer->deleteCookie(cookie);
422 void BrowserView::forward() const
425 webEngineViewPointer->forward();
428 void BrowserView::home() const
430 // Load the homepage.
431 webEngineViewPointer->load(QUrl::fromUserInput(Settings::homepage()));
434 void BrowserView::loadFinished() const
436 // Hide the progress bar.
437 emit hideProgressBar();
440 void BrowserView::loadInitialWebsite()
442 // Apply the application settings.
443 applyApplicationSettings();
445 // Get the arguments.
446 QStringList argumentsStringList = qApp->arguments();
448 // Check to see if the arguments lists contains a URL.
449 if (argumentsStringList.size() > 1)
451 // Load the URL from the arguments list.
452 webEngineViewPointer->load(QUrl::fromUserInput(argumentsStringList.at(1)));
456 // Load the homepage.
461 void BrowserView::loadProgress(const int &progress) const
463 // Show the progress bar.
464 emit showProgressBar(progress);
467 void BrowserView::loadStarted() const
469 // Show the progress bar.
470 emit showProgressBar(0);
473 void BrowserView::loadUrlFromLineEdit(QString url) const
475 // Decide if the text is more likely to be a URL or a search.
476 if (url.startsWith("file://")) // The text is likely a file URL.
479 webEngineViewPointer->load(QUrl::fromUserInput(url));
481 else if (url.contains(".")) // The text is likely a URL.
483 // Check if the URL does not start with a valid protocol.
484 if (!url.startsWith("http"))
486 // Add `https://` to the beginning of the URL.
487 url = "https://" + url;
491 webEngineViewPointer->load(QUrl::fromUserInput(url));
493 else // The text is likely a search.
496 webEngineViewPointer->load(QUrl::fromUserInput(searchEngineUrl + url));
500 void BrowserView::mouseBack() const
502 // Go back if possible.
503 if (webEngineHistoryPointer->canGoBack())
505 // Clear the URL line edit focus.
506 emit clearUrlLineEditFocus();
509 webEngineViewPointer->back();
513 void BrowserView::mouseForward() const
515 // Go forward if possible.
516 if (webEngineHistoryPointer->canGoForward())
518 // Clear the URL line edit focus.
519 emit clearUrlLineEditFocus();
522 webEngineViewPointer->forward();
526 void BrowserView::pageLinkHovered(const QString &linkUrl) const
528 // Emit a signal so that the browser window can update the status bar.
529 emit linkHovered(linkUrl);
532 void BrowserView::refresh() const
534 // Reload the website.
535 webEngineViewPointer->reload();
538 void BrowserView::toggleDomStorage() const
540 // Toggle DOM storage.
541 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, !webEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
543 // Update the DOM storage action.
544 emit updateDomStorageAction(webEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
546 // Reload the website.
547 webEngineViewPointer->reload();
550 void BrowserView::toggleJavaScript() const
552 // Toggle JavaScript.
553 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, !webEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
555 // Update the JavaScript action.
556 emit updateJavaScriptAction(webEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
558 // Reload the website.
559 webEngineViewPointer->reload();
562 void BrowserView::toggleLocalStorage()
564 // Toggle local storeage.
565 currentPrivacyWebEnginePointer->localStorageEnabled = !currentPrivacyWebEnginePointer->localStorageEnabled;
567 // Update the local storage action.
568 emit updateLocalStorageAction(currentPrivacyWebEnginePointer->localStorageEnabled);
570 // Reload the website.
571 webEngineViewPointer->reload();
574 void BrowserView::updateUrl(const QUrl &url) const
576 // Update the URL line edit.
577 emit updateUrlLineEdit(url);
579 // Update the status of the forward and back buttons.
580 emit updateBackAction(webEngineHistoryPointer->canGoBack());
581 emit updateForwardAction(webEngineHistoryPointer->canGoForward());
583 // Reapply the zoom factor. This is a bug in QWebEngineView that resets the zoom with every load. <https://redmine.stoutner.com/issues/799>
584 webEngineViewPointer->setZoomFactor(currentZoomFactor);