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 "PrivacyWebEngineView.h"
23 #include "ui_HttpAuthenticationDialog.h"
24 #include "databases/CookiesDatabase.h"
25 #include "databases/DomainsDatabase.h"
26 #include "dialogs/HttpAuthenticationDialog.h"
27 #include "interceptors/UrlRequestInterceptor.h"
28 #include "windows/BrowserWindow.h"
30 // Qt toolkit headers.
31 #include <QContextMenuEvent>
34 // Construct the class.
35 PrivacyWebEngineView::PrivacyWebEngineView(QWidget *parentWidgetPointer) : QWebEngineView(parentWidgetPointer)
37 // Create an off-the-record profile (the default when no profile name is specified).
38 webEngineProfilePointer = new QWebEngineProfile(QLatin1String(""));
40 // Create a WebEngine page.
41 QWebEnginePage *webEnginePagePointer = new QWebEnginePage(webEngineProfilePointer);
43 // Set the WebEngine page.
44 setPage(webEnginePagePointer);
46 // Get handles for the various aspects of the WebEngine.
47 webEngineSettingsPointer = webEnginePagePointer->settings();
49 // Instantiate the URL request interceptor.
50 UrlRequestInterceptor *urlRequestInterceptorPointer = new UrlRequestInterceptor(this);
52 // Set the URL request interceptor.
53 webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer);
55 // Reapply the domain settings when the host changes.
56 connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings(const QString&)), this, SLOT(applyDomainSettingsWithoutReloading(const QString&)));
58 // Display HTTP Ping blocked dialogs.
59 connect(urlRequestInterceptorPointer, SIGNAL(displayHttpPingDialog(const QString&)), this, SLOT(displayHttpPingDialog(const QString&)));
61 // Handle HTTP authentication requests.
62 connect(webEnginePagePointer, SIGNAL(authenticationRequired(const QUrl&, QAuthenticator*)), this, SLOT(handleAuthenticationRequest(const QUrl&, QAuthenticator*)));
65 void PrivacyWebEngineView::addCookieToList(const QNetworkCookie &cookie) const
67 //qDebug() << "Add cookie: " << cookie.toRawForm();
69 // Add the new cookie to the list.
70 cookieListPointer->push_front(cookie);
72 // Update the cookie if it is durable and has new data.
73 if (CookiesDatabase::isUpdate(cookie))
74 CookiesDatabase::updateCookie(cookie);
76 // Update the cookies action.
77 emit updateCookiesAction(cookieListPointer->size());
80 void PrivacyWebEngineView::applyDomainSettingsWithoutReloading(const QString &hostname)
82 // Apply the domain settings `false` does not reload the website.
83 applyDomainSettings(hostname, false);
86 void PrivacyWebEngineView::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
88 // Get the record for the hostname.
89 QSqlQuery domainQuery = DomainsDatabase::getDomainQuery(hostname);
91 // Check if the hostname has domain settings.
92 if (domainQuery.isValid()) // The hostname has domain settings.
94 // Store the domain settings name.
95 domainSettingsName = domainQuery.value(DomainsDatabase::DOMAIN_NAME).toString();
97 // Set the JavaScript status.
98 switch (domainQuery.value(DomainsDatabase::JAVASCRIPT).toInt())
100 // Set the default JavaScript status.
101 case (DomainsDatabase::SYSTEM_DEFAULT):
103 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
108 // Enable JavaScript.
109 case (DomainsDatabase::ENABLED):
111 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
116 // Disable JavaScript.
117 case (DomainsDatabase::DISABLED):
119 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
125 // Set the local storage status.
126 switch (domainQuery.value(DomainsDatabase::LOCAL_STORAGE).toInt())
128 // Set the default local storage status.
129 case (DomainsDatabase::SYSTEM_DEFAULT):
131 localStorageEnabled = Settings::localStorageEnabled();
136 // Enable local storage.
137 case (DomainsDatabase::ENABLED):
139 localStorageEnabled = true;
144 // Disable local storage.
145 case (DomainsDatabase::DISABLED):
147 localStorageEnabled = false;
153 // Set the DOM storage status.
154 switch (domainQuery.value(DomainsDatabase::DOM_STORAGE).toInt())
156 // Set the default DOM storage status. QWebEngineSettings confusingly calls this local storage.
157 case (DomainsDatabase::SYSTEM_DEFAULT):
159 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
164 // Enable DOM storage. QWebEngineSettings confusingly calls this local storage.
165 case (DomainsDatabase::ENABLED):
167 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
172 // Disable DOM storage. QWebEngineSettings confusingly calls this local storage.
173 case (DomainsDatabase::DISABLED):
175 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
181 // Set the user agent.
182 webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainQuery.value(DomainsDatabase::USER_AGENT).toString()));
184 // Check if a custom zoom factor is set.
185 if (domainQuery.value(DomainsDatabase::ZOOM_FACTOR).toInt())
187 // Store the current zoom factor.
188 defaultZoomFactor = domainQuery.value(DomainsDatabase::CUSTOM_ZOOM_FACTOR).toDouble();
192 // Store the current zoom factor.
193 defaultZoomFactor = Settings::zoomFactor();
196 else // The hostname does not have domain settings.
198 // Reset the domain settings name.
199 domainSettingsName = QLatin1String("");
201 // Set the JavaScript status.
202 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
204 // Set the local storage status.
205 localStorageEnabled = Settings::localStorageEnabled();
207 // Set DOM storage. In QWebEngineSettings it is called Local Storage.
208 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
210 // Set the user agent.
211 webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
213 // Store the zoom factor.
214 defaultZoomFactor = Settings::zoomFactor();
217 // Set the current zoom factor.
218 setZoomFactor(defaultZoomFactor);
220 // Reload the website if requested.
224 // Reset the HTTP authentication dialog counter.
225 httpAuthenticationDialogsDisplayed = 0;
231 void PrivacyWebEngineView::contextMenuEvent(QContextMenuEvent *contextMenuEvent) {
232 // Get a handle for the
233 QWebEnginePage *webEnginePagePointer = page();
235 // Get a handle for the menu.
236 QMenu *contextMenu = webEnginePagePointer->createStandardContextMenu();
238 // Get the list of context menu actions.
239 const QList<QAction *> contextMenuActionsList = contextMenu->actions();
241 // Add the open link in new background tab action if the context menu already contains the open link in new window action.
242 if (contextMenuActionsList.contains(webEnginePagePointer->action(QWebEnginePage::OpenLinkInNewWindow)))
244 // Move the open in new tab action to the top of the list.
245 contextMenu->insertAction(webEnginePagePointer->action(QWebEnginePage::Back), webEnginePagePointer->action(QWebEnginePage::OpenLinkInNewTab));
247 // Add the open link in background tab action below the open in new tab action.
248 contextMenu->insertAction(webEnginePagePointer->action(QWebEnginePage::Back), webEnginePagePointer->action(QWebEnginePage::OpenLinkInNewBackgroundTab));
250 // Move the open in new window action below the open in background tab action.
251 contextMenu->insertAction(webEnginePagePointer->action(QWebEnginePage::Back), webEnginePagePointer->action(QWebEnginePage::OpenLinkInNewWindow));
253 // Add a separator below the open in new window action.
254 contextMenu->insertSeparator(webEnginePagePointer->action(QWebEnginePage::Back));
257 // Display the menu using the location in the context menu event.
258 contextMenu->popup(contextMenuEvent->globalPos());
261 QWebEngineView* PrivacyWebEngineView::createWindow(QWebEnginePage::WebWindowType webWindowType) {
262 // Get a handle for the browser window.
263 BrowserWindow *browserWindowPointer = qobject_cast<BrowserWindow*>(window());
265 // Create the requested window type.
266 switch (webWindowType)
268 case QWebEnginePage::WebBrowserTab:
270 // Create the new tab and return the privacy WebEngine view pointer. `true` removes the focus from the blank URL line edit. `true` adds the new tab adjacent to the current tab.
271 // The new privacy WebEngine view pointer is returned so it can be populated with the link from the context menu.
272 return browserWindowPointer->tabWidgetPointer->addTab(true, true);
275 case QWebEnginePage::WebBrowserWindow:
277 // Create a new browser window.
278 BrowserWindow *newBrowserWindowPointer = new BrowserWindow();
280 // Show the new browser window.
281 newBrowserWindowPointer->show();
283 // The new privacy WebEngine view pointer is returned so it can be populated with the link from the context menu.
284 return newBrowserWindowPointer->tabWidgetPointer->loadBlankInitialWebsite();
287 case QWebEnginePage::WebBrowserBackgroundTab:
289 // Create the new tab and return the privacy WebEngine view pointer. `false` does not clear the URL line edit. `true` adds the new tab adjacent to the current tab.
290 // `true` creates a background tab.
291 // The new privacy WebEngine view pointer is returned so it can be populated with the link from the context menu.
292 return browserWindowPointer->tabWidgetPointer->addTab(false, true, true);
297 // Return a null pointer for opening a web dialog.
303 void PrivacyWebEngineView::displayHttpPingDialog(const QString &httpPingUrl) const
305 // Display the HTTP Ping blocked dialog.
306 emit displayHttpPingBlockedDialog(httpPingUrl);
309 void PrivacyWebEngineView::handleAuthenticationRequest(const QUrl &requestUrl, QAuthenticator *authenticatorPointer)
311 // Only display the HTTP authentication dialog if it hasn't already been displayed three times for this URL.
312 if (httpAuthenticationDialogsDisplayed < 3) {
313 // Increment the HTTP authentication dialog display counter.
314 ++httpAuthenticationDialogsDisplayed;
316 // Instantiate an HTTP authentication dialog.
317 HttpAuthenticationDialog *httpAuthenticationDialogPointer = new HttpAuthenticationDialog(parentWidget(), requestUrl, authenticatorPointer);
319 // Display the dialog. This must be `exec()` instead of `show()` so that the website doesn't proceed before populating the authentication pointer.
320 httpAuthenticationDialogPointer->exec();
324 void PrivacyWebEngineView::removeCookieFromList(const QNetworkCookie &cookie) const
326 //qDebug() << "Remove cookie: " << cookie.toRawForm();
328 // Remove the cookie from the list.
329 cookieListPointer->remove(cookie);
331 // Update the cookies action.
332 emit updateCookiesAction(cookieListPointer->size());