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 "helpers/FilterListHelper.h"
28 #include "interceptors/UrlRequestInterceptor.h"
29 #include "windows/BrowserWindow.h"
31 // Qt toolkit headers.
32 #include <QContextMenuEvent>
35 // Construct the class.
36 PrivacyWebEngineView::PrivacyWebEngineView(QWidget *parentWidgetPointer) : QWebEngineView(parentWidgetPointer)
38 // Create an off-the-record profile (the default when no profile name is specified).
39 webEngineProfilePointer = new QWebEngineProfile(QLatin1String(""));
41 // Create a WebEngine page.
42 QWebEnginePage *webEnginePagePointer = new QWebEnginePage(webEngineProfilePointer);
44 // Set the WebEngine page.
45 setPage(webEnginePagePointer);
47 // Get handles for the various aspects of the WebEngine.
48 webEngineSettingsPointer = webEnginePagePointer->settings();
50 // Instantiate the URL request interceptor.
51 UrlRequestInterceptor *urlRequestInterceptorPointer = new UrlRequestInterceptor(this);
53 // Set the URL request interceptor.
54 webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer);
56 // Connect the URL request interceptor signals.
57 connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings(const QString&)), this, SLOT(applyDomainSettingsWithoutReloading(const QString&)));
58 connect(urlRequestInterceptorPointer, SIGNAL(newMainFrameResource()), this, SLOT(clearRequestsList()));
59 connect(urlRequestInterceptorPointer, SIGNAL(displayHttpPingDialog(const QString&)), this, SLOT(displayHttpPingDialog(const QString&)));
60 connect(urlRequestInterceptorPointer, SIGNAL(requestProcessed(RequestStruct*)), this, SLOT(storeRequest(RequestStruct*)));
62 // Handle HTTP authentication requests.
63 connect(webEnginePagePointer, SIGNAL(authenticationRequired(const QUrl&, QAuthenticator*)), this, SLOT(handleAuthenticationRequest(const QUrl&, QAuthenticator*)));
66 void PrivacyWebEngineView::addCookieToList(const QNetworkCookie &cookie) const
68 //qDebug() << "Add cookie: " << cookie.toRawForm();
70 // Add the new cookie to the list.
71 cookieListPointer->push_front(cookie);
73 // Update the cookie if it is durable and has new data.
74 if (CookiesDatabase::isUpdate(cookie))
75 CookiesDatabase::updateCookie(cookie);
77 // Update the cookies action.
78 emit numberOfCookiesChanged(cookieListPointer->size());
81 void PrivacyWebEngineView::applyDomainSettingsWithoutReloading(const QString &hostname)
83 // Apply the domain settings `false` does not reload the website.
84 applyDomainSettings(hostname, false);
87 void PrivacyWebEngineView::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
89 // Get the record for the hostname.
90 QSqlQuery domainQuery = DomainsDatabase::getDomainQuery(hostname);
92 // Check if the hostname has domain settings.
93 if (domainQuery.isValid()) // The hostname has domain settings.
95 // Store the domain settings name.
96 domainSettingsName = domainQuery.value(DomainsDatabase::DOMAIN_NAME).toString();
98 // Set the JavaScript status.
99 switch (domainQuery.value(DomainsDatabase::JAVASCRIPT).toInt())
101 // Set the default JavaScript status.
102 case (DomainsDatabase::SYSTEM_DEFAULT):
104 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
109 // Enable JavaScript.
110 case (DomainsDatabase::ENABLED):
112 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
117 // Disable JavaScript.
118 case (DomainsDatabase::DISABLED):
120 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
126 // Set the local storage status.
127 switch (domainQuery.value(DomainsDatabase::LOCAL_STORAGE).toInt())
129 // Set the default local storage status.
130 case (DomainsDatabase::SYSTEM_DEFAULT):
132 localStorageEnabled = Settings::localStorageEnabled();
137 // Enable local storage.
138 case (DomainsDatabase::ENABLED):
140 localStorageEnabled = true;
145 // Disable local storage.
146 case (DomainsDatabase::DISABLED):
148 localStorageEnabled = false;
154 // Set the DOM storage status.
155 switch (domainQuery.value(DomainsDatabase::DOM_STORAGE).toInt())
157 // Set the default DOM storage status. QWebEngineSettings confusingly calls this local storage.
158 case (DomainsDatabase::SYSTEM_DEFAULT):
160 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
165 // Enable DOM storage. QWebEngineSettings confusingly calls this local storage.
166 case (DomainsDatabase::ENABLED):
168 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
173 // Disable DOM storage. QWebEngineSettings confusingly calls this local storage.
174 case (DomainsDatabase::DISABLED):
176 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
182 // Set the user agent.
183 webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainQuery.value(DomainsDatabase::USER_AGENT).toString()));
185 // Check if a custom zoom factor is set.
186 if (domainQuery.value(DomainsDatabase::ZOOM_FACTOR).toInt())
188 // Store the current zoom factor.
189 defaultZoomFactor = domainQuery.value(DomainsDatabase::CUSTOM_ZOOM_FACTOR).toDouble();
193 // Store the current zoom factor.
194 defaultZoomFactor = Settings::zoomFactor();
197 else // The hostname does not have domain settings.
199 // Reset the domain settings name.
200 domainSettingsName = QLatin1String("");
202 // Set the JavaScript status.
203 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
205 // Set the local storage status.
206 localStorageEnabled = Settings::localStorageEnabled();
208 // Set DOM storage. In QWebEngineSettings it is called Local Storage.
209 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
211 // Set the user agent.
212 webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
214 // Store the zoom factor.
215 defaultZoomFactor = Settings::zoomFactor();
218 // Set the current zoom factor.
219 setZoomFactor(defaultZoomFactor);
221 // Reload the website if requested.
225 // Reset the HTTP authentication dialog counter.
226 httpAuthenticationDialogsDisplayed = 0;
232 void PrivacyWebEngineView::clearRequestsList()
234 // Reset the number of blocked requests.
237 // Clear the requests list.
238 requestsListPointer->clear();
240 // Update the blocked requests action.
241 emit(requestBlocked(blockedRequests));
244 void PrivacyWebEngineView::contextMenuEvent(QContextMenuEvent *contextMenuEvent) {
245 // Get a handle for the
246 QWebEnginePage *webEnginePagePointer = page();
248 // Get a handle for the menu.
249 QMenu *contextMenu = webEnginePagePointer->createStandardContextMenu();
251 // Get the list of context menu actions.
252 const QList<QAction *> contextMenuActionsList = contextMenu->actions();
254 // Add the open link in new background tab action if the context menu already contains the open link in new window action.
255 if (contextMenuActionsList.contains(webEnginePagePointer->action(QWebEnginePage::OpenLinkInNewWindow)))
257 // Move the open in new tab action to the top of the list.
258 contextMenu->insertAction(webEnginePagePointer->action(QWebEnginePage::Back), webEnginePagePointer->action(QWebEnginePage::OpenLinkInNewTab));
260 // Add the open link in background tab action below the open in new tab action.
261 contextMenu->insertAction(webEnginePagePointer->action(QWebEnginePage::Back), webEnginePagePointer->action(QWebEnginePage::OpenLinkInNewBackgroundTab));
263 // Move the open in new window action below the open in background tab action.
264 contextMenu->insertAction(webEnginePagePointer->action(QWebEnginePage::Back), webEnginePagePointer->action(QWebEnginePage::OpenLinkInNewWindow));
266 // Add a separator below the open in new window action.
267 contextMenu->insertSeparator(webEnginePagePointer->action(QWebEnginePage::Back));
270 // Display the menu using the location in the context menu event.
271 contextMenu->popup(contextMenuEvent->globalPos());
274 QWebEngineView* PrivacyWebEngineView::createWindow(QWebEnginePage::WebWindowType webWindowType) {
275 // Get a handle for the browser window.
276 BrowserWindow *browserWindowPointer = qobject_cast<BrowserWindow*>(window());
278 // Create the requested window type.
279 switch (webWindowType)
281 case QWebEnginePage::WebBrowserTab:
283 // 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.
284 // The new privacy WebEngine view pointer is returned so it can be populated with the link from the context menu.
285 return browserWindowPointer->tabWidgetPointer->addTab(true, true);
288 case QWebEnginePage::WebBrowserWindow:
290 // Create a new browser window.
291 BrowserWindow *newBrowserWindowPointer = new BrowserWindow();
293 // Show the new browser window.
294 newBrowserWindowPointer->show();
296 // The new privacy WebEngine view pointer is returned so it can be populated with the link from the context menu.
297 return newBrowserWindowPointer->tabWidgetPointer->loadBlankInitialWebsite();
300 case QWebEnginePage::WebBrowserBackgroundTab:
302 // 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.
303 // `true` creates a background tab.
304 // The new privacy WebEngine view pointer is returned so it can be populated with the link from the context menu.
305 return browserWindowPointer->tabWidgetPointer->addTab(false, true, true);
310 // Return a null pointer for opening a web dialog.
316 void PrivacyWebEngineView::displayHttpPingDialog(const QString &httpPingUrl) const
318 // Display the HTTP Ping blocked dialog.
319 emit displayHttpPingBlockedDialog(httpPingUrl);
322 void PrivacyWebEngineView::handleAuthenticationRequest(const QUrl &requestUrl, QAuthenticator *authenticatorPointer)
324 // Only display the HTTP authentication dialog if it hasn't already been displayed three times for this URL.
325 if (httpAuthenticationDialogsDisplayed < 3) {
326 // Increment the HTTP authentication dialog display counter.
327 ++httpAuthenticationDialogsDisplayed;
329 // Instantiate an HTTP authentication dialog.
330 HttpAuthenticationDialog *httpAuthenticationDialogPointer = new HttpAuthenticationDialog(parentWidget(), requestUrl, authenticatorPointer);
332 // Display the dialog. This must be `exec()` instead of `show()` so that the website doesn't proceed before populating the authentication pointer.
333 httpAuthenticationDialogPointer->exec();
337 void PrivacyWebEngineView::removeCookieFromList(const QNetworkCookie &cookie) const
339 //qDebug() << "Remove cookie: " << cookie.toRawForm();
341 // Remove the cookie from the list.
342 cookieListPointer->remove(cookie);
344 // Update the cookies action.
345 emit numberOfCookiesChanged(cookieListPointer->size());
348 void PrivacyWebEngineView::storeRequest(RequestStruct *requestStructPointer)
350 // Store the request struct in the list.
351 requestsListPointer->append(requestStructPointer);
353 // Track blocked requests.
354 if (requestStructPointer->dispositionInt == FilterListHelper::BLOCKED)
356 // Update the blocked requests counter.
359 // Update the blocked requests action.
360 emit(requestBlocked(blockedRequests));