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