]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/views/BrowserView.cpp
b3f593ff93352a1d023a895207cb5832af21e036
[PrivacyBrowserPC.git] / src / views / BrowserView.cpp
1 /*
2  * Copyright © 2022 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 "BrowserView.h"
22 #include "Settings.h"
23 #include "ui_BrowserView.h"
24 #include "databases/CookiesDatabase.h"
25 #include "databases/DomainsDatabase.h"
26 #include "filters/MouseEventFilter.h"
27 #include "helpers/SearchEngineHelper.h"
28 #include "helpers/UserAgentHelper.h"
29 #include "interceptors/UrlRequestInterceptor.h"
30 #include "windows/BrowserWindow.h"
31
32 // Qt framework headers.
33 #include <QAction>
34 #include <QPrintDialog>
35 #include <QPrintPreviewDialog>
36 #include <QPrinter>
37
38 // Initialize the public static variables.
39 QString BrowserView::webEngineDefaultUserAgent = QStringLiteral("");
40
41 // Construct the class.
42 BrowserView::BrowserView(QWidget *parent) : QWidget(parent)
43 {
44     // Initialize the variables.
45     privacyWebEngineListPointer = new QList<PrivacyWebEngine*>;
46
47     // Instantiate the browser view UI.
48     Ui::BrowserView browserViewUi;
49
50     // Setup the UI.
51     browserViewUi.setupUi(this);
52
53     // Get handles for the views.
54     webEngineViewPointer = browserViewUi.webEngineView;
55
56     // Create an off-the-record profile (the default when no profile name is specified).
57     webEngineProfilePointer = new QWebEngineProfile(QStringLiteral(""));
58
59     // Create a WebEngine page.
60     webEnginePagePointer = new QWebEnginePage(webEngineProfilePointer);
61
62     // Set the WebEngine page.
63     webEngineViewPointer->setPage(webEnginePagePointer);
64
65     // Handle full screen requests.
66     connect(webEnginePagePointer, SIGNAL(fullScreenRequested(QWebEngineFullScreenRequest)), this, SLOT(fullScreenRequested(QWebEngineFullScreenRequest)));
67
68     // Get handles for the aspects of the WebEngine.
69     webEngineHistoryPointer = webEnginePagePointer->history();
70     webEngineSettingsPointer = webEngineViewPointer->settings();
71     webEngineCookieStorePointer = webEngineProfilePointer->cookieStore();
72
73     // Initialize the current privacy web engine pointer.
74     currentPrivacyWebEnginePointer = new PrivacyWebEngine(webEngineViewPointer);
75
76     // Populate the privacy web engine list.
77     privacyWebEngineListPointer->append(currentPrivacyWebEnginePointer);
78
79     // Set the local storage filter.
80     webEngineCookieStorePointer->setCookieFilter([this](const QWebEngineCookieStore::FilterRequest &filterRequest)
81     {
82         //qDebug().noquote().nospace() << "Page URL:  " << filterRequest.firstPartyUrl << ", Local storage URL:  " << filterRequest.origin << ",  Is third-party:  " << filterRequest.thirdParty;
83
84         // Block all third party local storage requests, including the sneaky ones that don't register a first party URL.
85         if (filterRequest.thirdParty || (filterRequest.firstPartyUrl == QStringLiteral("")))
86         {
87             //qDebug() << "Request blocked.";
88
89             // Return false.
90             return false;
91         }
92
93         /*  TODO.  Waiting for a solution to <https://redmine.stoutner.com/issues/857>.
94         // Check each tab to see if this local storage request should be allowed.
95         for (PrivacyWebEngine *privacyWebEnginePointer : *privacyWebEngineListPointer)
96         {
97             //qDebug().noquote().nospace() << "Local storage:  " << privacyWebEnginePointer->localStorageEnabled << ".  WebEngine URL:  " << webEngineViewPointer->url().host() << ".  Request Host:  " << filterRequest.firstPartyUrl.host();
98
99             // Allow this local storage request if it comes from a tab with local storage enabled.
100             if (privacyWebEnginePointer->localStorageEnabled && (webEngineViewPointer->url().host() == filterRequest.firstPartyUrl.host()))
101             {
102                 //qDebug() << "Request allowed.";
103
104                 // Return true.
105                 return true;
106             }
107         }
108         */
109
110         // Allow the request if it is first party and local storage is enabled.
111         if (!filterRequest.thirdParty && currentPrivacyWebEnginePointer->localStorageEnabled)
112         {
113             // Return true.
114             return true;
115         }
116
117         //qDebug() << "Request blocked.";
118
119         // Block any remaining local storage requests.
120         return false;
121     });
122
123     // Process cookie changes.
124     connect(webEngineCookieStorePointer, SIGNAL(cookieAdded(QNetworkCookie)), this, SLOT(cookieAdded(QNetworkCookie)));
125     connect(webEngineCookieStorePointer, SIGNAL(cookieRemoved(QNetworkCookie)), this, SLOT(cookieRemoved(QNetworkCookie)));
126
127     // Get a list of durable cookies.
128     QList<QNetworkCookie*> *durableCookiesListPointer = CookiesDatabase::getCookies();
129
130     // Add the durable cookies to the store.
131     for (QNetworkCookie *cookiePointer : *durableCookiesListPointer)
132         addCookieToStore(*cookiePointer);
133
134     // Store a copy of the WebEngine default user agent.
135     webEngineDefaultUserAgent = webEngineProfilePointer->httpUserAgent();
136
137     // Update the URL line edit when the URL changes.
138     connect(webEngineViewPointer, SIGNAL(urlChanged(const QUrl)), this, SLOT(updateUrl(const QUrl)));
139
140     // Update the progress bar.
141     connect(webEngineViewPointer, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
142     connect(webEngineViewPointer, SIGNAL(loadProgress(const int)), this, SLOT(loadProgress(const int)));
143     connect(webEngineViewPointer, SIGNAL(loadFinished(const bool)), this, SLOT(loadFinished()));
144
145     // Instantiate the mouse event filter pointer.
146     MouseEventFilter *mouseEventFilterPointer = new MouseEventFilter();
147
148     // Install the mouse event filter.
149     qApp->installEventFilter(mouseEventFilterPointer);
150
151     // Process mouse forward and back commands.
152     connect(mouseEventFilterPointer, SIGNAL(mouseBack()), this, SLOT(mouseBack()));
153     connect(mouseEventFilterPointer, SIGNAL(mouseForward()), this, SLOT(mouseForward()));
154
155     // Listen for hovered link URLs.
156     connect(webEnginePagePointer, SIGNAL(linkHovered(const QString)), this, SLOT(pageLinkHovered(const QString)));
157
158     // Instantiate the URL request interceptor.
159     UrlRequestInterceptor *urlRequestInterceptorPointer = new UrlRequestInterceptor();
160
161     // Set the URL request interceptor.
162     webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer);
163
164     // Reapply the domain settings when the host changes.
165     connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings(QString)), this, SLOT(applyDomainSettingsWithoutReloading(QString)));
166
167     // Don't allow JavaScript to open windows.
168     webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
169
170     // Allow keyboard navigation.
171     webEngineSettingsPointer->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, true);
172
173     // Enable full screen support.
174     webEngineSettingsPointer->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
175
176     // Require user interaction to play media.
177     webEngineSettingsPointer->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, true);
178
179     // Limit WebRTC to public IP addresses.
180     webEngineSettingsPointer->setAttribute(QWebEngineSettings::WebRTCPublicInterfacesOnly, true);
181
182     // Set the focus on the WebEngine view.
183     webEngineViewPointer->setFocus();
184 }
185
186 BrowserView::~BrowserView()
187 {
188     // Delay the deletion of the WebEngine page to prevent the following error:  `Release of profile requested but WebEnginePage still not deleted. Expect troubles !`
189     webEnginePagePointer->deleteLater();
190 }
191
192 // 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.
193 void BrowserView::addCookieToStore(QNetworkCookie cookie) const
194 {
195     // Create a url.
196     QUrl url;
197
198     // 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>
199     if (!cookie.domain().startsWith(QStringLiteral(".")))
200     {
201         // Populate the URL.
202         url.setHost(cookie.domain());
203         url.setScheme(QStringLiteral("https"));
204
205         // Clear the domain from the cookie.
206         cookie.setDomain(QStringLiteral(""));
207     }
208
209     // Add the cookie to the store.
210     webEngineCookieStorePointer->setCookie(cookie, url);
211 }
212
213 void BrowserView::applyApplicationSettings()
214 {
215     // Set the search engine URL.
216     searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
217
218     // Emit the update search engine actions signal.
219     emit updateSearchEngineActions(Settings::searchEngine(), true);
220 }
221
222 // 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.
223 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
224 void BrowserView::applyDomainSettingsAndReload()
225 {
226     // Apply the domain settings.  `true` reloads the website.
227     applyDomainSettings(webEngineViewPointer->url().host(), true);
228 }
229
230 // 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.
231 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
232 void BrowserView::applyDomainSettingsWithoutReloading(const QString &hostname)
233 {
234     // Apply the domain settings  `false` does not reload the website.
235     applyDomainSettings(hostname, false);
236 }
237
238 // Once <https://redmine.stoutner.com/issues/799> has been resolved this can be `const`.
239 void BrowserView::applyDomainSettings(const QString &hostname, const bool reloadWebsite)
240 {
241     // Get the record for the hostname.
242     QSqlQuery domainQuery = DomainsDatabase::getDomainQuery(hostname);
243
244     // Check if the hostname has domain settings.
245     if (domainQuery.isValid())  // The hostname has domain settings.
246     {
247         // Get the domain record.
248         QSqlRecord domainRecord = domainQuery.record();
249
250         // Set the JavaScript status.
251         switch (domainRecord.field(DomainsDatabase::JAVASCRIPT).value().toInt())
252         {
253             // Set the default JavaScript status.
254             case (DomainsDatabase::SYSTEM_DEFAULT):
255             {
256                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
257
258                 break;
259             }
260
261             // Disable JavaScript.
262             case (DomainsDatabase::DISABLED):
263             {
264                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
265
266                 break;
267             }
268
269             // Enable JavaScript.
270             case (DomainsDatabase::ENABLED):
271             {
272                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
273
274                 break;
275             }
276         }
277
278         // Set the local storage status.
279         switch (domainRecord.field(DomainsDatabase::LOCAL_STORAGE).value().toInt())
280         {
281             // Set the default local storage status.
282             case (DomainsDatabase::SYSTEM_DEFAULT):
283             {
284                 currentPrivacyWebEnginePointer->localStorageEnabled = Settings::localStorageEnabled();
285
286                 break;
287             }
288
289             // Disable local storage.
290             case (DomainsDatabase::DISABLED):
291             {
292                 currentPrivacyWebEnginePointer->localStorageEnabled = false;
293
294                 break;
295             }
296
297             // Enable local storage.
298             case (DomainsDatabase::ENABLED):
299             {
300                 currentPrivacyWebEnginePointer->localStorageEnabled = true;
301
302                 break;
303             }
304         }
305
306         // Set the DOM storage status.
307         switch (domainRecord.field(DomainsDatabase::DOM_STORAGE).value().toInt())
308         {
309             // Set the default DOM storage status.
310             case (DomainsDatabase::SYSTEM_DEFAULT):
311             {
312                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
313
314                 break;
315             }
316
317             // Disable DOM storage.
318             case (DomainsDatabase::DISABLED):
319             {
320                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
321
322                 break;
323             }
324
325             // Enable DOM storage.
326             case (DomainsDatabase::ENABLED):
327             {
328                 webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
329
330                 break;
331             }
332         }
333
334         // Set the user agent.
335         webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getResultingDomainSettingsUserAgent(domainRecord.field(DomainsDatabase::USER_AGENT).value().toString()));
336
337         // Check if a custom zoom factor is set.
338         if (domainRecord.field(DomainsDatabase::ZOOM_FACTOR).value().toInt())
339         {
340             // Store the current zoom factor.
341             currentZoomFactor = domainRecord.field(DomainsDatabase::CUSTOM_ZOOM_FACTOR).value().toDouble();
342         }
343         else
344         {
345             // Reset the current zoom factor.
346             currentZoomFactor = Settings::zoomFactor();
347         }
348
349         // Set the zoom factor.    The use of `currentZoomFactor` can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
350         webEngineViewPointer->setZoomFactor(currentZoomFactor);
351
352         // Apply the domain settings palette to the URL line edit.
353         emit updateDomainSettingsIndicator(true, domainRecord.field(DomainsDatabase::DOMAIN_NAME).value().toString());
354     }
355     else  // The hostname does not have domain settings.
356     {
357         // Set the JavaScript status.
358         webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScriptEnabled());
359
360         // Set the local storage status.
361         currentPrivacyWebEnginePointer->localStorageEnabled = Settings::localStorageEnabled();
362
363         // Set DOM storage.
364         webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, Settings::domStorageEnabled());
365
366         // Set the user agent.
367         webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromDatabaseName(Settings::userAgent()));
368
369         // Store the current zoom factor.  This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
370         currentZoomFactor = Settings::zoomFactor();
371
372         // Set the zoom factor.
373         webEngineViewPointer->setZoomFactor(Settings::zoomFactor());
374
375         // Apply the no domain settings palette to the URL line edit.
376         emit updateDomainSettingsIndicator(false, QStringLiteral(""));
377     }
378
379     // Emit the update actions signals.
380     emit updateJavaScriptAction(webEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
381     emit updateLocalStorageAction(currentPrivacyWebEnginePointer->localStorageEnabled);
382     emit updateDomStorageAction(webEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
383     emit updateUserAgentActions(webEngineProfilePointer->httpUserAgent(), true);
384     emit updateZoomFactorAction(webEngineViewPointer->zoomFactor());
385
386     // Reload the website if requested.
387     if (reloadWebsite)
388         webEngineViewPointer->reload();
389 }
390
391 void BrowserView::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
392 {
393     // Store the search engine name.
394     QString searchEngineName = searchEngineActionPointer->text();
395
396     // Strip out any `&` characters.
397     searchEngineName.remove('&');
398
399     // Store the search engine string.
400     searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
401
402     // Update the search engine actionas.
403     emit updateSearchEngineActions(searchEngineName, false);
404 }
405
406 void BrowserView::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
407 {
408     // Get the user agent name.
409     QString userAgentName = userAgentActionPointer->text();
410
411     // Strip out any `&` characters.
412     userAgentName.remove('&');
413
414     // Apply the user agent.
415     webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgentFromTranslatedName(userAgentName));
416
417     // Update the user agent actions.
418     emit updateUserAgentActions(webEngineProfilePointer->httpUserAgent(), false);
419
420     // Reload the website.
421     webEngineViewPointer->reload();
422 }
423
424 // This can be const once <https://redmine.stoutner.com/issues/799> has been resolved.
425 void BrowserView::applyOnTheFlyZoomFactor(const double &zoomFactor)
426 {
427     // Update the current zoom factor.  This can be removed once <https://redmine.stoutner.com/issues/799> has been resolved.
428     currentZoomFactor = zoomFactor;
429
430     // Set the zoom factor.
431     webEngineViewPointer->setZoomFactor(zoomFactor);
432 }
433
434 void BrowserView::back() const
435 {
436     // Go back.
437     webEngineViewPointer->back();
438 }
439
440 void BrowserView::cookieAdded(const QNetworkCookie &cookie) const
441 {
442     // Add the cookie to the cookie list.
443     emit addCookie(cookie);
444 }
445
446 void BrowserView::cookieRemoved(const QNetworkCookie &cookie) const
447 {
448     // Remove the cookie from the cookie list.
449     emit removeCookie(cookie);
450 }
451
452 void BrowserView::deleteAllCookies() const
453 {
454     // Delete all the cookies.
455     webEngineCookieStorePointer->deleteAllCookies();
456 }
457
458 void BrowserView::deleteCookieFromStore(const QNetworkCookie &cookie) const
459 {
460     // Delete the cookie.
461     webEngineCookieStorePointer->deleteCookie(cookie);
462 }
463
464 void BrowserView::forward() const
465 {
466     // Go forward.
467     webEngineViewPointer->forward();
468 }
469
470 void BrowserView::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) const
471 {
472     // Make it so.
473     emit fullScreenRequested(fullScreenRequest.toggleOn());
474
475     // Accept the request.
476     fullScreenRequest.accept();
477 }
478
479 void BrowserView::home() const
480 {
481     // Load the homepage.
482     webEngineViewPointer->load(QUrl::fromUserInput(Settings::homepage()));
483 }
484
485 void BrowserView::loadFinished() const
486 {
487     // Hide the progress bar.
488     emit hideProgressBar();
489 }
490
491 void BrowserView::loadInitialWebsite()
492 {
493     // Apply the application settings.
494     applyApplicationSettings();
495
496     // Get the arguments.
497     QStringList argumentsStringList = qApp->arguments();
498
499     // Check to see if the arguments lists contains a URL.
500     if (argumentsStringList.size() > 1)
501     {
502         // Load the URL from the arguments list.
503         webEngineViewPointer->load(QUrl::fromUserInput(argumentsStringList.at(1)));
504     }
505     else
506     {
507         // Load the homepage.
508         home();
509     }
510 }
511
512 void BrowserView::loadProgress(const int &progress) const
513 {
514     // Show the progress bar.
515     emit showProgressBar(progress);
516 }
517
518 void BrowserView::loadStarted() const
519 {
520     // Show the progress bar.
521     emit showProgressBar(0);
522 }
523
524 void BrowserView::loadUrlFromLineEdit(QString url) const
525 {
526     // Decide if the text is more likely to be a URL or a search.
527     if (url.startsWith("file://"))  // The text is likely a file URL.
528     {
529         // Load the URL.
530         webEngineViewPointer->load(QUrl::fromUserInput(url));
531     }
532     else if (url.contains("."))  // The text is likely a URL.
533     {
534         // Check if the URL does not start with a valid protocol.
535         if (!url.startsWith("http"))
536         {
537             // Add `https://` to the beginning of the URL.
538             url = "https://" + url;
539         }
540
541         // Load the URL.
542         webEngineViewPointer->load(QUrl::fromUserInput(url));
543     }
544     else  // The text is likely a search.
545     {
546         // Load the search.
547         webEngineViewPointer->load(QUrl::fromUserInput(searchEngineUrl + url));
548     }
549 }
550
551 void BrowserView::mouseBack() const
552 {
553     // Go back if possible.
554     if (webEngineViewPointer->isActiveWindow() && webEngineHistoryPointer->canGoBack())
555     {
556         // Clear the URL line edit focus.
557         emit clearUrlLineEditFocus();
558
559         // Go back.
560         webEngineViewPointer->back();
561     }
562 }
563
564 void BrowserView::mouseForward() const
565 {
566     // Go forward if possible.
567     if (webEngineViewPointer->isActiveWindow() && webEngineHistoryPointer->canGoForward())
568     {
569         // Clear the URL line edit focus.
570         emit clearUrlLineEditFocus();
571
572         // Go forward.
573         webEngineViewPointer->forward();
574     }
575 }
576
577 void BrowserView::pageLinkHovered(const QString &linkUrl) const
578 {
579     // Emit a signal so that the browser window can update the status bar.
580     emit linkHovered(linkUrl);
581 }
582
583 void BrowserView::print() const
584 {
585     // Create a printer.
586     QPrinter printer;
587
588     // Set the resolution to be 300 dpi.
589     printer.setResolution(300);
590
591     // Create a printer dialog.
592     QPrintDialog printDialog(&printer, webEngineViewPointer);
593
594     // Display the dialog and print the page if instructed.
595     if (printDialog.exec() == QDialog::Accepted)
596         printWebpage(&printer);
597 }
598
599 void BrowserView::printPreview() const
600 {
601     // Create a printer.
602     QPrinter printer;
603
604     // Set the resolution to be 300 dpi.
605     printer.setResolution(300);
606
607     // Create a print preview dialog.
608     QPrintPreviewDialog printPreviewDialog(&printer, webEngineViewPointer);
609
610     // Generate the print preview.
611     connect(&printPreviewDialog, SIGNAL(paintRequested(QPrinter *)), this, SLOT(printWebpage(QPrinter *)));
612
613     // Display the dialog.
614     printPreviewDialog.exec();
615 }
616
617 void BrowserView::printWebpage(QPrinter *printerPointer) const
618 {
619     // Create an event loop.  For some reason, the print preview doesn't produce any output unless it is run inside an event loop.
620     QEventLoop eventLoop;
621
622     // Print the webpage, converting the callback above into a `QWebEngineCallback<bool>`.
623     // Printing requires that the printer be a pointer, not a reference, or it will crash with much cursing.
624     webEnginePagePointer->print(printerPointer, [&eventLoop](bool printSuccess)
625     {
626         // Instruct the compiler to ignore the unused parameter.
627         (void) printSuccess;
628
629         // Quit the loop.
630         eventLoop.quit();
631     });
632
633     // Execute the loop.
634     eventLoop.exec();
635 }
636
637 void BrowserView::refresh() const
638 {
639     // Reload the website.
640     webEngineViewPointer->reload();
641 }
642
643 void BrowserView::toggleDomStorage() const
644 {
645     // Toggle DOM storage.
646     webEngineSettingsPointer->setAttribute(QWebEngineSettings::LocalStorageEnabled, !webEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
647
648     // Update the DOM storage action.
649     emit updateDomStorageAction(webEngineSettingsPointer->testAttribute(QWebEngineSettings::LocalStorageEnabled));
650
651     // Reload the website.
652     webEngineViewPointer->reload();
653 }
654
655 void BrowserView::toggleJavaScript() const
656 {
657     // Toggle JavaScript.
658     webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, !webEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
659
660     // Update the JavaScript action.
661     emit updateJavaScriptAction(webEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
662
663     // Reload the website.
664     webEngineViewPointer->reload();
665 }
666
667 void BrowserView::toggleLocalStorage()
668 {
669     // Toggle local storeage.
670     currentPrivacyWebEnginePointer->localStorageEnabled = !currentPrivacyWebEnginePointer->localStorageEnabled;
671
672     // Update the local storage action.
673     emit updateLocalStorageAction(currentPrivacyWebEnginePointer->localStorageEnabled);
674
675     // Reload the website.
676     webEngineViewPointer->reload();
677 }
678
679 void BrowserView::updateUrl(const QUrl &url) const
680 {
681     // Update the URL line edit.
682     emit updateUrlLineEdit(url);
683
684     // Update the status of the forward and back buttons.
685     emit updateBackAction(webEngineHistoryPointer->canGoBack());
686     emit updateForwardAction(webEngineHistoryPointer->canGoForward());
687
688     // Reapply the zoom factor.  This is a bug in QWebEngineView that resets the zoom with every load.  <https://redmine.stoutner.com/issues/799>
689     webEngineViewPointer->setZoomFactor(currentZoomFactor);
690 }