]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/widgets/PrivacyWebEngineView.cpp
Partial filter list implementation.
[PrivacyBrowserPC.git] / src / widgets / PrivacyWebEngineView.cpp
1 /*
2  * Copyright 2022-2024 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc/>.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 // Application headers.
21 #include "PrivacyWebEngineView.h"
22 #include "Settings.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"
30
31 // Qt toolkit headers.
32 #include <QContextMenuEvent>
33 #include <QMenu>
34
35 // Construct the class.
36 PrivacyWebEngineView::PrivacyWebEngineView(QWidget *parentWidgetPointer) : QWebEngineView(parentWidgetPointer)
37 {
38     // Create an off-the-record profile (the default when no profile name is specified).
39     webEngineProfilePointer = new QWebEngineProfile(QLatin1String(""));
40
41     // Create a WebEngine page.
42     QWebEnginePage *webEnginePagePointer = new QWebEnginePage(webEngineProfilePointer);
43
44     // Set the WebEngine page.
45     setPage(webEnginePagePointer);
46
47     // Get handles for the various aspects of the WebEngine.
48     webEngineSettingsPointer = webEnginePagePointer->settings();
49
50     // Instantiate the URL request interceptor.
51     UrlRequestInterceptor *urlRequestInterceptorPointer = new UrlRequestInterceptor(this);
52
53     // Set the URL request interceptor.
54     webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer);
55
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*)));
61
62     // Handle HTTP authentication requests.
63     connect(webEnginePagePointer, SIGNAL(authenticationRequired(const QUrl&, QAuthenticator*)), this, SLOT(handleAuthenticationRequest(const QUrl&, QAuthenticator*)));
64 }
65
66 void PrivacyWebEngineView::addCookieToList(const QNetworkCookie &cookie) const
67 {
68     //qDebug() << "Add cookie:  " << cookie.toRawForm();
69
70     // Add the new cookie to the list.
71     cookieListPointer->push_front(cookie);
72
73     // Update the cookie if it is durable and has new data.
74     if (CookiesDatabase::isUpdate(cookie))
75         CookiesDatabase::updateCookie(cookie);
76
77     // Update the cookies action.
78     emit numberOfCookiesChanged(cookieListPointer->size());
79 }
80
81 void PrivacyWebEngineView::applyDomainSettingsWithoutReloading(const QString &hostname)
82 {
83     // Apply the domain settings  `false` does not reload the website.
84     applyDomainSettings(hostname, false);
85 }
86
87 void PrivacyWebEngineView::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
88 {
89     // Get the record for the hostname.
90     QSqlQuery domainQuery = DomainsDatabase::getDomainQuery(hostname);
91
92     // Check if the hostname has domain settings.
93     if (domainQuery.isValid())  // The hostname has domain settings.
94     {
95         // Store the domain settings name.
96         domainSettingsName = domainQuery.value(DomainsDatabase::DOMAIN_NAME).toString();
97
98         // Set the JavaScript status.
99         switch (domainQuery.value(DomainsDatabase::JAVASCRIPT).toInt())
100         {
101             // Set the default JavaScript status.
102             case (DomainsDatabase::SYSTEM_DEFAULT):
103             {
104                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
105
106                 break;
107             }
108
109             // Enable JavaScript.
110             case (DomainsDatabase::ENABLED):
111             {
112                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
113
114                 break;
115             }
116
117             // Disable JavaScript.
118             case (DomainsDatabase::DISABLED):
119             {
120                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
121
122                 break;
123             }
124         }
125
126         // Set the local storage status.
127         switch (domainQuery.value(DomainsDatabase::LOCAL_STORAGE).toInt())
128         {
129             // Set the default local storage status.
130             case (DomainsDatabase::SYSTEM_DEFAULT):
131             {
132                 localStorageEnabled = Settings::localStorageEnabled();
133
134                 break;
135             }
136
137             // Enable local storage.
138             case (DomainsDatabase::ENABLED):
139             {
140                 localStorageEnabled = true;
141
142                 break;
143             }
144
145             // Disable local storage.
146             case (DomainsDatabase::DISABLED):
147             {
148                 localStorageEnabled = false;
149
150                 break;
151             }
152         }
153
154         // Set the DOM storage status.
155         switch (domainQuery.value(DomainsDatabase::DOM_STORAGE).toInt())
156         {
157             // Set the default DOM storage status.  QWebEngineSettings confusingly calls this local storage.
158             case (DomainsDatabase::SYSTEM_DEFAULT):
159             {
160                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
161
162                 break;
163             }
164
165             // Enable DOM storage.  QWebEngineSettings confusingly calls this local storage.
166             case (DomainsDatabase::ENABLED):
167             {
168                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
169
170                 break;
171             }
172
173             // Disable DOM storage.  QWebEngineSettings confusingly calls this local storage.
174             case (DomainsDatabase::DISABLED):
175             {
176                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
177
178                 break;
179             }
180         }
181
182         // Set the user agent.
183         webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainQuery.value(DomainsDatabase::USER_AGENT).toString()));
184
185         // Check if a custom zoom factor is set.
186         if (domainQuery.value(DomainsDatabase::ZOOM_FACTOR).toInt())
187         {
188             // Store the current zoom factor.
189             defaultZoomFactor = domainQuery.value(DomainsDatabase::CUSTOM_ZOOM_FACTOR).toDouble();
190         }
191         else
192         {
193             // Store the current zoom factor.
194             defaultZoomFactor = Settings::zoomFactor();
195         }
196     }
197     else  // The hostname does not have domain settings.
198     {
199         // Reset the domain settings name.
200         domainSettingsName = QLatin1String("");
201
202         // Set the JavaScript status.
203         webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
204
205         // Set the local storage status.
206         localStorageEnabled = Settings::localStorageEnabled();
207
208         // Set DOM storage.  In QWebEngineSettings it is called Local Storage.
209         webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
210
211         // Set the user agent.
212         webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
213
214         // Store the zoom factor.
215         defaultZoomFactor = Settings::zoomFactor();
216     }
217
218     // Set the current zoom factor.
219     setZoomFactor(defaultZoomFactor);
220
221     // Reload the website if requested.
222     if (reloadWebsite)
223         reload();
224
225     // Reset the HTTP authentication dialog counter.
226     httpAuthenticationDialogsDisplayed = 0;
227
228     // Update the UI.
229     emit updateUi(this);
230 }
231
232 void PrivacyWebEngineView::clearRequestsList()
233 {
234     // Reset the number of blocked requests.
235     blockedRequests = 0;
236
237     // Clear the requests list.
238     requestsListPointer->clear();
239
240     // Update the blocked requests action.
241     emit(requestBlocked(blockedRequests));
242 }
243
244 void PrivacyWebEngineView::contextMenuEvent(QContextMenuEvent *contextMenuEvent) {
245     // Get a handle for the
246     QWebEnginePage *webEnginePagePointer = page();
247
248     // Get a handle for the menu.
249     QMenu *contextMenu = webEnginePagePointer->createStandardContextMenu();
250
251     // Get the list of context menu actions.
252     const QList<QAction *> contextMenuActionsList = contextMenu->actions();
253
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)))
256     {
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));
259
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));
262
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));
265
266         // Add a separator below the open in new window action.
267         contextMenu->insertSeparator(webEnginePagePointer->action(QWebEnginePage::Back));
268     }
269
270     // Display the menu using the location in the context menu event.
271     contextMenu->popup(contextMenuEvent->globalPos());
272 }
273
274 QWebEngineView* PrivacyWebEngineView::createWindow(QWebEnginePage::WebWindowType webWindowType) {
275     // Get a handle for the browser window.
276     BrowserWindow *browserWindowPointer = qobject_cast<BrowserWindow*>(window());
277
278     // Create the requested window type.
279     switch (webWindowType)
280     {
281         case QWebEnginePage::WebBrowserTab:
282         {
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);
286         }
287
288         case QWebEnginePage::WebBrowserWindow:
289         {
290             // Create a new browser window.
291             BrowserWindow *newBrowserWindowPointer = new BrowserWindow();
292
293             // Show the new browser window.
294             newBrowserWindowPointer->show();
295
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();
298         }
299
300         case QWebEnginePage::WebBrowserBackgroundTab:
301         {
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);
306         }
307
308         default:
309         {
310             // Return a null pointer for opening a web dialog.
311             return nullptr;
312         }
313     }
314 }
315
316 void PrivacyWebEngineView::displayHttpPingDialog(const QString &httpPingUrl) const
317 {
318     // Display the HTTP Ping blocked dialog.
319     emit displayHttpPingBlockedDialog(httpPingUrl);
320 }
321
322 void PrivacyWebEngineView::handleAuthenticationRequest(const QUrl &requestUrl, QAuthenticator *authenticatorPointer)
323 {
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;
328
329         // Instantiate an HTTP authentication dialog.
330         HttpAuthenticationDialog *httpAuthenticationDialogPointer = new HttpAuthenticationDialog(parentWidget(), requestUrl, authenticatorPointer);
331
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();
334     }
335 }
336
337 void PrivacyWebEngineView::removeCookieFromList(const QNetworkCookie &cookie) const
338 {
339     //qDebug() << "Remove cookie:  " << cookie.toRawForm();
340
341     // Remove the cookie from the list.
342     cookieListPointer->remove(cookie);
343
344     // Update the cookies action.
345     emit numberOfCookiesChanged(cookieListPointer->size());
346 }
347
348 void PrivacyWebEngineView::storeRequest(RequestStruct *requestStructPointer)
349 {
350     // Store the request struct in the list.
351     requestsListPointer->append(requestStructPointer);
352
353     // Track blocked requests.
354     if (requestStructPointer->dispositionInt == FilterListHelper::BLOCKED)
355     {
356         // Update the blocked requests counter.
357         ++blockedRequests;
358
359         // Update the blocked requests action.
360         emit(requestBlocked(blockedRequests));
361     }
362 }