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