]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/views/BrowserView.cpp
Begin implementing Domain Settings using KXmlGuiWindow part 2.
[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 "MouseEventFilter.h"
23 #include "Settings.h"
24 #include "ui_BrowserView.h"
25 #include "UrlRequestInterceptor.h"
26 #include "helpers/SearchEngineHelper.h"
27 #include "helpers/UserAgentHelper.h"
28 #include "windows/BrowserWindow.h"
29 #include "windows/DomainSettingsWindow.h"
30
31 // Qt framework headers.
32 #include <QAction>
33 #include <QWebEngineProfile>
34
35 BrowserView::BrowserView(QWidget *parent) : QWidget(parent)
36 {
37     // Instantiate the browser view UI.
38     Ui::BrowserView browserViewUi;
39
40     // Setup the UI.
41     browserViewUi.setupUi(this);
42
43     // Get handles for the views.
44     backButtonPointer = browserViewUi.backButton;
45     forwardButtonPointer = browserViewUi.forwardButton;
46     QPushButton *refreshButtonPointer = browserViewUi.refreshButton;
47     QPushButton *homeButtonPointer = browserViewUi.homeButton;
48     urlLineEditPointer = browserViewUi.urlLineEdit;
49     javaScriptButtonPointer = browserViewUi.javaScript;
50     QPushButton *domainSettingsButtonPointer = browserViewUi.domainSettingsButton;
51     webEngineViewPointer = browserViewUi.webEngineView;
52
53     // Get handles for the aspects of the WebEngine.
54     QWebEnginePage *webEnginePagePointer = webEngineViewPointer->page();
55     webEngineHistoryPointer = webEnginePagePointer->history();
56     webEngineProfilePointer = webEnginePagePointer->profile();
57     webEngineSettingsPointer = webEngineViewPointer->settings();
58
59     // Update the webengine view from the URL line edit.
60     connect(urlLineEditPointer, SIGNAL(returnKeyPressed(const QString)), this, SLOT(loadUrlFromTextBox(const QString)));
61
62     // Update the URL line edit form the webengine view.
63     connect(webEngineViewPointer, SIGNAL(loadStarted()), this, SLOT(updateInterface()));
64     connect(webEngineViewPointer, SIGNAL(loadProgress(const int)), this, SLOT(updateInterface()));
65     connect(webEngineViewPointer, SIGNAL(loadFinished(const bool)), this, SLOT(updateInterface()));
66
67     // Setup the URL bar buttons.
68     connect(backButtonPointer, SIGNAL(clicked()), webEngineViewPointer, SLOT(back()));
69     connect(forwardButtonPointer, SIGNAL(clicked()), webEngineViewPointer, SLOT(forward()));
70     connect(refreshButtonPointer, SIGNAL(clicked()), webEngineViewPointer, SLOT(reload()));
71     connect(homeButtonPointer, SIGNAL(clicked()), this, SLOT(goHome()));
72     connect(javaScriptButtonPointer, SIGNAL(clicked()), this, SLOT(toggleJavaScript()));
73     connect(domainSettingsButtonPointer, SIGNAL(clicked()), this, SLOT(openDomainSettings()));
74
75     // Instantiate the mouse event pointer.
76     MouseEventFilter *mouseEventFilterPointer = new MouseEventFilter(webEngineViewPointer);
77
78     // Install the mouse event filter.
79     qApp->installEventFilter(mouseEventFilterPointer);
80
81     // Listen for hovered link URLs.
82     connect(webEnginePagePointer, SIGNAL(linkHovered(const QString)), this, SLOT(pageLinkHovered(const QString)));
83
84     // Instantiate the URL request interceptor.
85     UrlRequestInterceptor *urlRequestInterceptorPointer = new UrlRequestInterceptor();
86
87     // Set the URL request interceptor.
88     webEngineProfilePointer->setUrlRequestInterceptor(urlRequestInterceptorPointer);
89
90     // Reapply the domain settings when the host changes.
91     connect(urlRequestInterceptorPointer, SIGNAL(applyDomainSettings()), this, SLOT(applyDomainSettingsWithoutReloading()));
92
93     // Disable the cache.
94     webEngineProfilePointer->setHttpCacheType(QWebEngineProfile::NoCache);
95
96     // Don't allow JavaScript to open windows.
97     webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
98
99     // Set the focus on the WebEngine view.
100     webEngineViewPointer->setFocus();
101 }
102
103 void BrowserView::applyApplicationSettings()
104 {
105     // Set the search engine URL.
106     searchEngineUrl = SearchEngineHelper::getSearchUrl(Settings::searchEngine());
107
108     // Emit the search engine updated signal, which causes the on-the-fly menu to be updated.
109     emit searchEngineUpdated(Settings::searchEngine());
110 }
111
112 // 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.
113 void BrowserView::applyDomainSettingsAndReload() const
114 {
115     // Apply the domain settings.  `true` reloads the website.
116     applyDomainSettings(true);
117 }
118
119 // 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.
120 void BrowserView::applyDomainSettingsWithoutReloading() const
121 {
122     // Apply the domain settings  `false` does not reload the website.
123     applyDomainSettings(false);
124 }
125
126 void BrowserView::applyDomainSettings(bool reloadWebsite) const
127 {
128     // Set the JavaScript status.
129     webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, Settings::javaScript());
130
131     // Update the JavaScript button.
132     if (Settings::javaScript())
133     {
134         javaScriptButtonPointer->setIcon(QIcon(":/icons/javascript-warning"));
135     }
136     else
137     {
138         javaScriptButtonPointer->setIcon(QIcon(":/icons/privacy-mode"));
139     }
140
141     // Apply the user agent.
142     webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgent(Settings::userAgent()));
143
144     // Set the zoom factor.
145     webEngineViewPointer->setZoomFactor(Settings::zoomFactor());
146
147     // Emit the on-the-fly menu update signals.
148     emit userAgentUpdated(Settings::userAgent());
149     emit zoomFactorUpdated(Settings::zoomFactor());
150
151     // Reload the website if requested.
152     if (reloadWebsite)
153     {
154         webEngineViewPointer->reload();
155     }
156 }
157
158 void BrowserView::applyOnTheFlySearchEngine(QAction *searchEngineActionPointer)
159 {
160     // Store the search engine name.
161     QString searchEngineName = searchEngineActionPointer->text();
162
163     // Strip out any `&` characters.
164     searchEngineName.remove('&');
165
166     // Store the search engine string.
167     searchEngineUrl = SearchEngineHelper::getSearchUrl(searchEngineName);
168 }
169
170 void BrowserView::applyOnTheFlyUserAgent(QAction *userAgentActionPointer) const
171 {
172     // Get the user agent name.
173     QString userAgentName = userAgentActionPointer->text();
174
175     // Strip out any `&` characters.
176     userAgentName.remove('&');
177
178     // Apply the user agent.
179     webEngineProfilePointer->setHttpUserAgent(UserAgentHelper::getUserAgent(userAgentName));
180
181     // Reload the website.
182     webEngineViewPointer->reload();
183 }
184
185 void BrowserView::applyOnTheFlyZoomFactor(const double &zoomFactor) const
186 {
187     // Set the zoom factor.
188     webEngineViewPointer->setZoomFactor(zoomFactor);
189 }
190
191 void BrowserView::goHome() const
192 {
193     // Load the homepage.
194     webEngineViewPointer->setUrl(QUrl::fromUserInput(Settings::homepage()));
195 }
196
197 void BrowserView::loadInitialWebsite()
198 {
199     // Apply the application settings.
200     applyApplicationSettings();
201
202     // Get the arguments.
203     QStringList argumentsStringList = qApp->arguments();
204
205     // Check to see if the arguments lists contains a URL.
206     if (argumentsStringList.size() > 1)
207     {
208         // Load the URL from the arguments list.
209         webEngineViewPointer->setUrl(QUrl::fromUserInput(argumentsStringList.at(1)));
210     }
211     else
212     {
213         // Load the homepage.
214         goHome();
215     }
216 }
217
218 void BrowserView::loadUrlFromTextBox(QString urlFromUser) const
219 {
220     // Remove the focus from the URL line edit.
221     urlLineEditPointer->clearFocus();
222
223     // Decide if the text is more likely to be a URL or a search.
224     if (urlFromUser.contains("."))  // The text is likely a URL.
225     {
226         // Check if the URL does not start with a valid protocol.
227         if (!urlFromUser.startsWith("http") && !urlFromUser.startsWith("file://"))
228         {
229             // Add `https://` to the beginning of the URL.
230             urlFromUser = "https://" + urlFromUser;
231         }
232
233         // Load the URL.
234         webEngineViewPointer->setUrl(QUrl::fromUserInput(urlFromUser));
235     }
236     else  // The text is likely a search.
237     {
238         // Load the search.
239         webEngineViewPointer->setUrl(QUrl::fromUserInput(searchEngineUrl + urlFromUser));
240     }
241 }
242
243 void BrowserView::openDomainSettings() const
244 {
245     // Get a list of the top level widgets.
246     const QWidgetList topLevelWidgets = QApplication::topLevelWidgets();
247
248     // Initialize a domain settings window exists boolean.
249     bool domainSettingsWindowExists = false;
250
251     // Iterate through the top level widgets.
252     for (QWidget *widgetPointer : topLevelWidgets)
253     {
254         // Check for an existing domain settings window.
255         if (widgetPointer->objectName() == QStringLiteral("domain_settings"))
256         {
257             // Show the existing domain settings window if it is hidden.
258             widgetPointer->show();
259
260             // Raise the existing domain settings window if it is below other windows.
261             widgetPointer->raise();
262
263             // Restore the existing domain settings window if it has been minimized.
264             if (widgetPointer->isMinimized()) {
265                 widgetPointer->showNormal();
266             }
267
268             // Activate the existing domain settings window, which brings its virtual desktop into focus.
269             widgetPointer->activateWindow();
270
271             // Update the domain settings window exists boolean.
272             domainSettingsWindowExists = true;
273         }
274     }
275
276     if (!domainSettingsWindowExists)
277     {
278         // Instantiate the domain settings window.
279         DomainSettingsWindow *domainSettingsWindowPointer = new DomainSettingsWindow();
280
281         // Show the window.
282         domainSettingsWindowPointer->show();
283     }
284 }
285
286 void BrowserView::pageLinkHovered(const QString &linkUrl) const
287 {
288     // Emit a signal so that the browser window can update the status bar.
289     emit linkHovered(linkUrl);
290 }
291
292 void BrowserView::toggleJavaScript() const
293 {
294     // Toggle JavaScript.
295     webEngineSettingsPointer->setAttribute(QWebEngineSettings::JavascriptEnabled, !webEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled));
296
297     // Update the JavaScript button.
298     if (webEngineSettingsPointer->testAttribute(QWebEngineSettings::JavascriptEnabled))
299     {
300         javaScriptButtonPointer->setIcon(QIcon(":/icons/javascript-warning"));
301     }
302     else
303     {
304         javaScriptButtonPointer->setIcon(QIcon(":/icons/privacy-mode"));
305     }
306
307     // Reload the website.
308     webEngineViewPointer->reload();
309 }
310
311 void BrowserView::updateInterface() const
312 {
313     // Update the URL line edit if it does not have focus.
314     if (!urlLineEditPointer->hasFocus())
315     {
316         // Update the URL line edit.
317         urlLineEditPointer->setText(webEngineViewPointer->url().toString());
318     }
319
320     // Update the status of the forward and back buttons.
321     backButtonPointer->setEnabled(webEngineHistoryPointer->canGoBack());
322     forwardButtonPointer->setEnabled(webEngineHistoryPointer->canGoForward());
323
324     // Reapply the zoom factor.  This is a bug in QWebEngineView that resets the zoom with every load.  Hopefully it will be fixed in Qt6.  <https://bugreports.qt.io/browse/QTBUG-51992>
325     webEngineViewPointer->setZoomFactor(Settings::zoomFactor());
326 }