Rabbit Remote Control 0.1.0-bate2
Loading...
Searching...
No Matches
FrmWebView.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include "FrmWebView.h"
4#include "FrmWebBrowser.h"
5#include <QContextMenuEvent>
6#include <QMenu>
7#include <QMessageBox>
8#include <QAuthenticator>
9#include <QTimer>
10#include <QStyle>
11#include <QLoggingCategory>
12
13static Q_LOGGING_CATEGORY(log, "WebBrowser.View")
14
16 : QWebEngineView(parent)
17 , m_pBrowser(parent)
18{
19 connect(this, &QWebEngineView::loadStarted, [this]() {
20 m_loadProgress = 0;
21 emit favIconChanged(favIcon());
22 });
23 connect(this, &QWebEngineView::loadProgress, [this](int progress) {
24 m_loadProgress = progress;
25 });
26 connect(this, &QWebEngineView::loadFinished, [this](bool success) {
27 m_loadProgress = success ? 100 : -1;
28 emit favIconChanged(favIcon());
29 });
30 connect(this, &QWebEngineView::iconChanged, [this](const QIcon &) {
31 emit favIconChanged(favIcon());
32 });
33
34 connect(this, &QWebEngineView::renderProcessTerminated,
35 [this](QWebEnginePage::RenderProcessTerminationStatus termStatus, int statusCode) {
36 QString status;
37 switch (termStatus) {
38 case QWebEnginePage::NormalTerminationStatus:
39 status = tr("Render process normal exit");
40 break;
41 case QWebEnginePage::AbnormalTerminationStatus:
42 status = tr("Render process abnormal exit");
43 break;
44 case QWebEnginePage::CrashedTerminationStatus:
45 status = tr("Render process crashed");
46 break;
47 case QWebEnginePage::KilledTerminationStatus:
48 status = tr("Render process killed");
49 break;
50 }
51 QMessageBox::StandardButton btn = QMessageBox::question(window(), status,
52 tr("Render process exited with code: %1\n"
53 "Do you want to reload the page ?").arg(statusCode));
54 if (btn == QMessageBox::Yes)
55 QTimer::singleShot(0, this, &CFrmWebView::reload);
56 });
57}
58
59CFrmWebView::~CFrmWebView()
60{
61 qDebug(log) << Q_FUNC_INFO;
62 if (m_imageAnimationGroup)
63 delete m_imageAnimationGroup;
64
65 m_imageAnimationGroup = nullptr;
66 this->stop();
67}
68
69#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
70inline QString questionForPermissionType(QWebEnginePermission::PermissionType permissionType)
71{
72 switch (permissionType) {
73 case QWebEnginePermission::PermissionType::Geolocation:
74 return QObject::tr("Allow %1 to access your location information?");
75 case QWebEnginePermission::PermissionType::MediaAudioCapture:
76 return QObject::tr("Allow %1 to access your microphone?");
77 case QWebEnginePermission::PermissionType::MediaVideoCapture:
78 return QObject::tr("Allow %1 to access your webcam?");
79 case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
80 return QObject::tr("Allow %1 to access your microphone and webcam?");
81 case QWebEnginePermission::PermissionType::MouseLock:
82 return QObject::tr("Allow %1 to lock your mouse cursor?");
83 case QWebEnginePermission::PermissionType::DesktopVideoCapture:
84 return QObject::tr("Allow %1 to capture video of your desktop?");
85 case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
86 return QObject::tr("Allow %1 to capture audio and video of your desktop?");
87 case QWebEnginePermission::PermissionType::Notifications:
88 return QObject::tr("Allow %1 to show notification on your desktop?");
89 case QWebEnginePermission::PermissionType::ClipboardReadWrite:
90 return QObject::tr("Allow %1 to read from and write to the clipboard?");
91 case QWebEnginePermission::PermissionType::LocalFontsAccess:
92 return QObject::tr("Allow %1 to access fonts stored on this machine?");
93 case QWebEnginePermission::PermissionType::Unsupported:
94 break;
95 }
96 return QString();
97}
98#endif
99
100void CFrmWebView::setPage(QWebEnginePage *page)
101{
102 CreateWebActionTrigger(page,QWebEnginePage::Forward);
103 CreateWebActionTrigger(page,QWebEnginePage::Back);
104 CreateWebActionTrigger(page,QWebEnginePage::Reload);
105 CreateWebActionTrigger(page,QWebEnginePage::Stop);
106
107 if (auto oldPage = QWebEngineView::page()) {
108 oldPage->disconnect(this);
109 }
110 QWebEngineView::setPage(page);
111 connect(page, &QWebEnginePage::linkHovered, this, &CFrmWebView::sigLinkHovered);
112 connect(page, &QWebEnginePage::windowCloseRequested,
113 this, &CFrmWebView::sigCloseRequested);
114 connect(page, &QWebEnginePage::selectClientCertificate,
115 this, &CFrmWebView::slotSelectClientCertificate);
116 connect(page, &QWebEnginePage::authenticationRequired, this,
117 &CFrmWebView::slotAuthenticationRequired);
118 connect(page, &QWebEnginePage::proxyAuthenticationRequired, this,
119 &CFrmWebView::slotProxyAuthenticationRequired);
120 connect(page, &QWebEnginePage::registerProtocolHandlerRequested, this,
122 #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
123 connect(page, &QWebEnginePage::certificateError,
124 this, &CFrmWebView::slotCertificateError);
125 connect(page, &QWebEnginePage::permissionRequested, this,
126 &CFrmWebView::slotPermissionRequested);
127 #endif
128 #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
129 connect(page, &QWebEnginePage::fileSystemAccessRequested, this,
131 #endif
132 #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
133 connect(page, &QWebEnginePage::desktopMediaRequested, this,
134 &CFrmWebView::slotDesktopMediaRequest);
135 connect(page, &QWebEnginePage::webAuthUxRequested,
136 this, &CFrmWebView::slotWebAuthUxRequested);
137 #endif
138}
139
140int CFrmWebView::progress() const
141{
142 return m_loadProgress;
143}
144
145QIcon CFrmWebView::favIcon() const
146{
147 QIcon favIcon = icon();
148 if (!favIcon.isNull())
149 return favIcon;
150
151 if (m_loadProgress < 0) {
152 static QIcon errorIcon("dialog-error");
153 return errorIcon;
154 }
155 if (m_loadProgress < 100) {
156 static QIcon loadingIcon = QIcon::fromTheme("view-refresh");
157 return loadingIcon;
158 }
159
160 static QIcon defaultIcon("text-html");
161 return defaultIcon;
162}
163
164QWebEngineView *CFrmWebView::createWindow(QWebEnginePage::WebWindowType type)
165{
166 if(m_pBrowser)
167 return m_pBrowser->createWindow(type);
168 return this;
169}
170
171void CFrmWebView::CreateWebActionTrigger(QWebEnginePage *page, QWebEnginePage::WebAction webAction)
172{
173 QAction *action = page->action(webAction);
174#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
175 connect(action, &QAction::enabledChanged, [this, action, webAction](bool enabled){
176 qDebug(log) << "webAction:" << webAction << enabled;
177 emit sigWebActionEnabledChanged(webAction, enabled);
178 });
179#else
180 connect(action, &QAction::changed, [this, action, webAction]{
181 emit sigWebActionEnabledChanged(webAction, action->isEnabled());
182 });
183#endif
184}
185
186void CFrmWebView::contextMenuEvent(QContextMenuEvent *event)
187{
188 QMenu *menu;
189#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
190 menu = createStandardContextMenu();
191#else
192 menu = page()->createStandardContextMenu();
193#endif
194 const QList<QAction *> actions = menu->actions();
195 auto inspectElement = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::InspectElement));
196 if (inspectElement == actions.cend()) {
197 auto viewSource = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::ViewSource));
198 if (viewSource == actions.cend())
199 menu->addSeparator();
200
201 QAction *action = menu->addAction("Open inspector");
202 connect(action, &QAction::triggered, [this]() { emit sigDevToolsRequested(page());});
203 } else {
204 (*inspectElement)->setText(tr("Inspect element"));
205 }
206#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
207 // add context menu for image policy
208 QMenu *editImageAnimation = new QMenu(tr("Image animation policy"));
209
210 m_imageAnimationGroup = new QActionGroup(editImageAnimation);
211 m_imageAnimationGroup->setExclusive(true);
212
213 QAction *disableImageAnimation =
214 editImageAnimation->addAction(tr("Disable all image animation"));
215 disableImageAnimation->setCheckable(true);
216 m_imageAnimationGroup->addAction(disableImageAnimation);
217 connect(disableImageAnimation, &QAction::triggered, [this]() {
218 handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::Disallow);
219 });
220 QAction *allowImageAnimationOnce =
221 editImageAnimation->addAction(tr("Allow animated images, but only once"));
222 allowImageAnimationOnce->setCheckable(true);
223 m_imageAnimationGroup->addAction(allowImageAnimationOnce);
224 connect(allowImageAnimationOnce, &QAction::triggered,
225 [this]() { handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::AnimateOnce); });
226 QAction *allowImageAnimation = editImageAnimation->addAction(tr("Allow all animated images"));
227 allowImageAnimation->setCheckable(true);
228 m_imageAnimationGroup->addAction(allowImageAnimation);
229 connect(allowImageAnimation, &QAction::triggered, [this]() {
230 handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::Allow);
231 });
232
233 switch (page()->settings()->imageAnimationPolicy()) {
234 case QWebEngineSettings::ImageAnimationPolicy::Allow:
235 allowImageAnimation->setChecked(true);
236 break;
237 case QWebEngineSettings::ImageAnimationPolicy::AnimateOnce:
238 allowImageAnimationOnce->setChecked(true);
239 break;
240 case QWebEngineSettings::ImageAnimationPolicy::Disallow:
241 disableImageAnimation->setChecked(true);
242 break;
243 default:
244 allowImageAnimation->setChecked(true);
245 break;
246 }
247
248 menu->addMenu(editImageAnimation);
249#endif
250 menu->popup(event->globalPos());
251}
252
253void CFrmWebView::slotSelectClientCertificate(QWebEngineClientCertificateSelection clientCertSelection)
254{
255 qDebug(log) << Q_FUNC_INFO;
256 if(clientCertSelection.certificates().size() > 0) {
257 // Just select one.
258 clientCertSelection.select(clientCertSelection.certificates().at(0));
259 }
260}
261
262#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
263void CFrmWebView::slotCertificateError(QWebEngineCertificateError error)
264{
265 qDebug(log) << Q_FUNC_INFO;
266
267 // Automatically block certificate errors from page resources without prompting the user.
268 // This mirrors the behavior found in other major browsers.
269 if (!error.isMainFrame()) {
270 error.rejectCertificate();
271 return;
272 }
273
274 error.defer();
275 QString szMsg;
276 szMsg = error.description() + "\n\n";
277 szMsg += tr("If you wish so, you may continue with an unverified certificate. "
278 "Accepting an unverified certificate mean you may not be connected with the host you tried to connect to.") +
279 "\n\n" + tr("Do you wish to override the security check and continue ?");
280 int nRet = QMessageBox::critical(window(), tr("Certificate Error"), szMsg,
281 QMessageBox::StandardButton::Yes
282 | QMessageBox::StandardButton::No,
283 QMessageBox::StandardButton::No);
284 if(QMessageBox::StandardButton::Yes == nRet)
285 error.acceptCertificate();
286 else
287 error.rejectCertificate();
288}
289#endif
290
291void CFrmWebView::slotAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth)
292{
293 qDebug(log) << Q_FUNC_INFO;
294 /*
295 QDialog dialog(window());
296 dialog.setModal(true);
297 dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
298
299 Ui::PasswordDialog passwordDialog;
300 passwordDialog.setupUi(&dialog);
301
302 passwordDialog.m_iconLabel->setText(QString());
303 QIcon icon(window()->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, window()));
304 passwordDialog.m_iconLabel->setPixmap(icon.pixmap(32, 32));
305
306 QString introMessage(tr("Enter username and password for \"%1\" at %2")
307 .arg(auth->realm(),
308 requestUrl.toString().toHtmlEscaped()));
309 passwordDialog.m_infoLabel->setText(introMessage);
310 passwordDialog.m_infoLabel->setWordWrap(true);
311
312 if (dialog.exec() == QDialog::Accepted) {
313 auth->setUser(passwordDialog.m_userNameLineEdit->text());
314 auth->setPassword(passwordDialog.m_passwordLineEdit->text());
315 } else {
316 // Set authenticator null if dialog is cancelled
317 *auth = QAuthenticator();
318 }
319 */
320}
321
322#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
323void CFrmWebView::slotPermissionRequested(QWebEnginePermission permission)
324{
325 qDebug(log) << Q_FUNC_INFO;
326 QString title = tr("Permission Request");
327 QString question = questionForPermissionType(permission.permissionType()).arg(permission.origin().host());
328 if (!question.isEmpty() && QMessageBox::question(window(), title, question) == QMessageBox::Yes)
329 permission.grant();
330 else
331 permission.deny();
332}
333
334void CFrmWebView::handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy policy)
335{
336 qDebug(log) << Q_FUNC_INFO;
337 if (!page())
338 return;
339
340 page()->settings()->setImageAnimationPolicy(policy);
341}
342#endif
343
344void CFrmWebView::slotProxyAuthenticationRequired(const QUrl &url, QAuthenticator *auth,
345 const QString &proxyHost)
346{
347 qDebug(log) << Q_FUNC_INFO;
348 /*
349 QDialog dialog(window());
350 dialog.setModal(true);
351 dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
352
353 Ui::PasswordDialog passwordDialog;
354 passwordDialog.setupUi(&dialog);
355
356 passwordDialog.m_iconLabel->setText(QString());
357 QIcon icon(window()->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, window()));
358 passwordDialog.m_iconLabel->setPixmap(icon.pixmap(32, 32));
359
360 QString introMessage = tr("Connect to proxy \"%1\" using:");
361 introMessage = introMessage.arg(proxyHost.toHtmlEscaped());
362 passwordDialog.m_infoLabel->setText(introMessage);
363 passwordDialog.m_infoLabel->setWordWrap(true);
364
365 if (dialog.exec() == QDialog::Accepted) {
366 auth->setUser(passwordDialog.m_userNameLineEdit->text());
367 auth->setPassword(passwordDialog.m_passwordLineEdit->text());
368 } else {
369 // Set authenticator null if dialog is cancelled
370 *auth = QAuthenticator();
371 }
372 */
373}
374
375#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
376void CFrmWebView::slotDesktopMediaRequest(const QWebEngineDesktopMediaRequest &request)
377{
378 qDebug(log) << Q_FUNC_INFO;
379 // select the primary screen
380 request.selectScreen(request.screensModel()->index(0));
381}
382
383void CFrmWebView::slotWebAuthUxRequested(QWebEngineWebAuthUxRequest *request)
384{
385 qDebug(log) << Q_FUNC_INFO;
386 /*
387 if (m_authDialog)
388 delete m_authDialog;
389
390 m_authDialog = new WebAuthDialog(request, window());
391 m_authDialog->setModal(false);
392 m_authDialog->setWindowFlags(m_authDialog->windowFlags() & ~Qt::WindowContextHelpButtonHint);
393
394 connect(request, &QWebEngineWebAuthUxRequest::stateChanged, this, &CFrmWebView::onStateChanged);
395 m_authDialog->show();
396*/
397}
398
399void CFrmWebView::onStateChanged(QWebEngineWebAuthUxRequest::WebAuthUxState state)
400{
401 qDebug(log) << Q_FUNC_INFO;
402 /*
403 if (QWebEngineWebAuthUxRequest::WebAuthUxState::Completed == state
404 || QWebEngineWebAuthUxRequest::WebAuthUxState::Cancelled == state) {
405 if (m_authDialog) {
406 delete m_authDialog;
407 m_authDialog = nullptr;
408 }
409 } else {
410 m_authDialog->updateDisplay();
411 }
412*/
413}
414#endif
415
418 QWebEngineRegisterProtocolHandlerRequest request)
419{
420 qDebug(log) << Q_FUNC_INFO;
421 auto answer = QMessageBox::question(window(), tr("Permission Request"),
422 tr("Allow %1 to open all %2 links?")
423 .arg(request.origin().host())
424 .arg(request.scheme()));
425 if (answer == QMessageBox::Yes)
426 request.accept();
427 else
428 request.reject();
429}
431
432#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
433void CFrmWebView::slotFileSystemAccessRequested(QWebEngineFileSystemAccessRequest request)
434{
435 qDebug(log) << Q_FUNC_INFO;
436 QString accessType;
437 switch (request.accessFlags()) {
438 case QWebEngineFileSystemAccessRequest::Read:
439 accessType = "read";
440 break;
441 case QWebEngineFileSystemAccessRequest::Write:
442 accessType = "write";
443 break;
444 case QWebEngineFileSystemAccessRequest::Read | QWebEngineFileSystemAccessRequest::Write:
445 accessType = "read and write";
446 break;
447 default:
448 Q_UNREACHABLE();
449 }
450
451 auto answer = QMessageBox::question(window(), tr("File system access request"),
452 tr("Give %1 %2 access to %3?")
453 .arg(request.origin().host())
454 .arg(accessType)
455 .arg(request.filePath().toString()));
456 if (answer == QMessageBox::Yes)
457 request.accept();
458 else
459 request.reject();
460}
461#endif
void slotFileSystemAccessRequested(QWebEngineFileSystemAccessRequest request)
[registerProtocolHandlerRequested]
void handleRegisterProtocolHandlerRequested(QWebEngineRegisterProtocolHandlerRequest request)
[registerProtocolHandlerRequested]