玉兔远程控制 0.1.0-bate4
载入中...
搜索中...
未找到
FrmWebView.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include <QContextMenuEvent>
4#include <QMenu>
5#include <QMessageBox>
6#include <QAuthenticator>
7#include <QTimer>
8#include <QStyle>
9#include <QLoggingCategory>
10#include <QWebEngineProfile>
11#include <QWebEngineScript>
12#include <QWebEngineScriptCollection>
13
14#include "FrmWebView.h"
15#include "FrmWebBrowser.h"
16#include "DlgUserPassword.h"
17
18static Q_LOGGING_CATEGORY(log, "WebBrowser.View")
19
20CFrmWebView::CFrmWebView(CFrmWebBrowser *pFrmWebBrowser, QWidget *parent)
21 : QWebEngineView(parent)
22 , m_pBrowser(pFrmWebBrowser)
23#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
24 , m_pDlgWebAuth(nullptr)
25#endif
26 , m_pWebChannel(nullptr)
27 , m_pPasswordStore(nullptr)
28{
29 bool check = false;
30 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
31 setMinimumSize(200, 100);
32 Q_ASSERT(m_pBrowser);
33 check = connect(this, &QWebEngineView::loadStarted, [this]() {
34 m_loadProgress = 0;
35 emit favIconChanged(favIcon());
36 });
37 Q_ASSERT(check);
38 check = connect(this, &QWebEngineView::loadProgress, [this](int progress) {
39 m_loadProgress = progress;
40 });
41 Q_ASSERT(check);
42 check = connect(this, &QWebEngineView::loadFinished, [this](bool success) {
43 m_loadProgress = success ? 100 : -1;
44 emit favIconChanged(favIcon());
45 if(m_pBrowser->m_pPara->GetAutoFillUserAndPassword() && m_pWebChannel && m_pPasswordStore)
46 InjectScriptAutoFill();
47 return;
48 QString szUser, szPassword;
49 int nRet = GetUserAndPassword(this->url(), szUser, szPassword);
50 if(0 == nRet) {
51 SetupAutoFillScript();
52 GlobalFillForm(szUser, szPassword);
53 //FillForm(szUser, szPassword);
54 }
55 });
56 Q_ASSERT(check);
57 check = connect(this, &QWebEngineView::iconChanged, [this](const QIcon &) {
58 emit favIconChanged(favIcon());
59 });
60 Q_ASSERT(check);
61 check = connect(this, &QWebEngineView::renderProcessTerminated,
62 [this](QWebEnginePage::RenderProcessTerminationStatus termStatus, int statusCode) {
63 QString status;
64 switch (termStatus) {
65 case QWebEnginePage::NormalTerminationStatus:
66 status = tr("Render process normal exit");
67 break;
68 case QWebEnginePage::AbnormalTerminationStatus:
69 status = tr("Render process abnormal exit");
70 break;
71 case QWebEnginePage::CrashedTerminationStatus:
72 status = tr("Render process crashed");
73 break;
74 case QWebEnginePage::KilledTerminationStatus:
75 status = tr("Render process killed");
76 break;
77 }
78 QMessageBox::StandardButton btn = QMessageBox::question(window(), status,
79 tr("Render process exited with code: %1\n"
80 "Do you want to reload the page ?").arg(statusCode));
81 if (btn == QMessageBox::Yes)
82 QTimer::singleShot(0, this, &CFrmWebView::reload);
83 });
84 Q_ASSERT(check);
85}
86
87CFrmWebView::~CFrmWebView()
88{
89 qDebug(log) << Q_FUNC_INFO;
90 if (m_imageAnimationGroup)
91 delete m_imageAnimationGroup;
92
93 m_imageAnimationGroup = nullptr;
94}
95
96#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
97inline QString questionForPermissionType(QWebEnginePermission::PermissionType permissionType)
98{
99 switch (permissionType) {
100 case QWebEnginePermission::PermissionType::Geolocation:
101 return QObject::tr("Allow %1 to access your location information?");
102 case QWebEnginePermission::PermissionType::MediaAudioCapture:
103 return QObject::tr("Allow %1 to access your microphone?");
104 case QWebEnginePermission::PermissionType::MediaVideoCapture:
105 return QObject::tr("Allow %1 to access your webcam?");
106 case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
107 return QObject::tr("Allow %1 to access your microphone and webcam?");
108 case QWebEnginePermission::PermissionType::MouseLock:
109 return QObject::tr("Allow %1 to lock your mouse cursor?");
110 case QWebEnginePermission::PermissionType::DesktopVideoCapture:
111 return QObject::tr("Allow %1 to capture video of your desktop?");
112 case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
113 return QObject::tr("Allow %1 to capture audio and video of your desktop?");
114 case QWebEnginePermission::PermissionType::Notifications:
115 return QObject::tr("Allow %1 to show notification on your desktop?");
116 case QWebEnginePermission::PermissionType::ClipboardReadWrite:
117 return QObject::tr("Allow %1 to read from and write to the clipboard?");
118 case QWebEnginePermission::PermissionType::LocalFontsAccess:
119 return QObject::tr("Allow %1 to access fonts stored on this machine?");
120 case QWebEnginePermission::PermissionType::Unsupported:
121 break;
122 }
123 return QString();
124}
125#endif
126
127void CFrmWebView::setPage(QWebEnginePage *page)
128{
129 Q_ASSERT(page);
130 CreateWebActionTrigger(page,QWebEnginePage::Forward);
131 CreateWebActionTrigger(page,QWebEnginePage::Back);
132 CreateWebActionTrigger(page,QWebEnginePage::Reload);
133 CreateWebActionTrigger(page,QWebEnginePage::Stop);
134
135 if (auto oldPage = QWebEngineView::page()) {
136 oldPage->disconnect(this);
137 }
138 QWebEngineView::setPage(page);
139 connect(page, &QWebEnginePage::linkHovered, this, &CFrmWebView::sigLinkHovered);
140 connect(page, &QWebEnginePage::windowCloseRequested,
141 this, &CFrmWebView::sigCloseRequested);
142 connect(page, &QWebEnginePage::selectClientCertificate,
143 this, &CFrmWebView::slotSelectClientCertificate);
144 connect(page, &QWebEnginePage::authenticationRequired, this,
145 &CFrmWebView::slotAuthenticationRequired);
146 connect(page, &QWebEnginePage::proxyAuthenticationRequired, this,
147 &CFrmWebView::slotProxyAuthenticationRequired);
148 connect(page, &QWebEnginePage::registerProtocolHandlerRequested, this,
150 #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
151 connect(page, &QWebEnginePage::certificateError,
152 this, &CFrmWebView::slotCertificateError);
153 connect(page, &QWebEnginePage::permissionRequested, this,
154 &CFrmWebView::slotPermissionRequested);
155 #endif
156 #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
157 connect(page, &QWebEnginePage::fileSystemAccessRequested, this,
159 #endif
160 #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
161 connect(page, &QWebEnginePage::desktopMediaRequested, this,
162 &CFrmWebView::slotDesktopMediaRequest);
163 connect(page, &QWebEnginePage::webAuthUxRequested,
164 this, &CFrmWebView::slotWebAuthUxRequested);
165 #endif
166
167 Q_ASSERT(m_pBrowser);
168 if(m_pBrowser->m_pPara->GetAutoFillUserAndPassword()) {
169 if(!m_pWebChannel)
170 m_pWebChannel = new QWebChannel(this);
171 if(!m_pPasswordStore)
172 m_pPasswordStore = new CPasswordStore(m_pBrowser->m_pPara, this);
173 if(m_pWebChannel && m_pPasswordStore) {
174 // 创建并注册 QWebChannel 对象,暴露 passwordStore 给 JS
175 m_pWebChannel->registerObject(QStringLiteral("PasswordManager"), m_pPasswordStore);
176 page->setWebChannel(m_pWebChannel);
177 InjectScriptQWebChannel();
178 } else
179 qCritical(log) << "Don't register PasswordManager";
180 }
181}
182
183int CFrmWebView::progress() const
184{
185 return m_loadProgress;
186}
187
188QIcon CFrmWebView::favIcon() const
189{
190 QIcon favIcon = icon();
191 if (!favIcon.isNull())
192 return favIcon;
193
194 if (m_loadProgress < 0) {
195 static QIcon errorIcon("dialog-error");
196 return errorIcon;
197 }
198 if (m_loadProgress < 100) {
199 static QIcon loadingIcon = QIcon::fromTheme("view-refresh");
200 return loadingIcon;
201 }
202
203 static QIcon defaultIcon("text-html");
204 return defaultIcon;
205}
206
207QWebEngineView *CFrmWebView::createWindow(QWebEnginePage::WebWindowType type)
208{
209 if(m_pBrowser)
210 return m_pBrowser->CreateWindow(
211 type, this->page()->profile()->isOffTheRecord());
212 return this;
213}
214
215void CFrmWebView::CreateWebActionTrigger(QWebEnginePage *page, QWebEnginePage::WebAction webAction)
216{
217 QAction *action = page->action(webAction);
218#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
219 connect(action, &QAction::enabledChanged, [this, action, webAction](bool enabled){
220 qDebug(log) << "webAction:" << webAction << enabled;
221 emit sigWebActionEnabledChanged(webAction, enabled);
222 });
223#else
224 connect(action, &QAction::changed, [this, action, webAction]{
225 emit sigWebActionEnabledChanged(webAction, action->isEnabled());
226 });
227#endif
228}
229
230void CFrmWebView::contextMenuEvent(QContextMenuEvent *event)
231{
232 QMenu *menu;
233#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
234 menu = createStandardContextMenu();
235#else
236 menu = page()->createStandardContextMenu();
237#endif
238 const QList<QAction *> actions = menu->actions();
239 auto inspectElement = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::InspectElement));
240 if (inspectElement == actions.cend()) {
241 auto viewSource = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::ViewSource));
242 if (viewSource == actions.cend())
243 menu->addSeparator();
244 QAction *action = menu->addAction(tr("Open inspector"));
245 connect(action, &QAction::triggered,
246 [this]() { emit sigDevToolsRequested(page());});
247 } else {
248 (*inspectElement)->setText(tr("Inspect element"));
249 }
250#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
251 // add context menu for image policy
252 QMenu *editImageAnimation = new QMenu(tr("Image animation policy"));
253
254 m_imageAnimationGroup = new QActionGroup(editImageAnimation);
255 m_imageAnimationGroup->setExclusive(true);
256
257 QAction *disableImageAnimation =
258 editImageAnimation->addAction(tr("Disable all image animation"));
259 disableImageAnimation->setCheckable(true);
260 m_imageAnimationGroup->addAction(disableImageAnimation);
261 connect(disableImageAnimation, &QAction::triggered, [this]() {
262 handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::Disallow);
263 });
264 QAction *allowImageAnimationOnce =
265 editImageAnimation->addAction(tr("Allow animated images, but only once"));
266 allowImageAnimationOnce->setCheckable(true);
267 m_imageAnimationGroup->addAction(allowImageAnimationOnce);
268 connect(allowImageAnimationOnce, &QAction::triggered,
269 [this]() { handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::AnimateOnce); });
270 QAction *allowImageAnimation = editImageAnimation->addAction(tr("Allow all animated images"));
271 allowImageAnimation->setCheckable(true);
272 m_imageAnimationGroup->addAction(allowImageAnimation);
273 connect(allowImageAnimation, &QAction::triggered, [this]() {
274 handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::Allow);
275 });
276
277 switch (page()->settings()->imageAnimationPolicy()) {
278 case QWebEngineSettings::ImageAnimationPolicy::Allow:
279 allowImageAnimation->setChecked(true);
280 break;
281 case QWebEngineSettings::ImageAnimationPolicy::AnimateOnce:
282 allowImageAnimationOnce->setChecked(true);
283 break;
284 case QWebEngineSettings::ImageAnimationPolicy::Disallow:
285 disableImageAnimation->setChecked(true);
286 break;
287 default:
288 allowImageAnimation->setChecked(true);
289 break;
290 }
291
292 menu->addMenu(editImageAnimation);
293#endif
294 menu->popup(event->globalPos());
295}
296
297void CFrmWebView::slotSelectClientCertificate(QWebEngineClientCertificateSelection clientCertSelection)
298{
299 qDebug(log) << Q_FUNC_INFO;
300 if(clientCertSelection.certificates().size() > 0) {
301 // Just select one.
302 clientCertSelection.select(clientCertSelection.certificates().at(0));
303 }
304}
305
306#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
307void CFrmWebView::slotCertificateError(QWebEngineCertificateError error)
308{
309 qDebug(log) << Q_FUNC_INFO;
310
311 // Automatically block certificate errors from page resources without prompting the user.
312 // This mirrors the behavior found in other major browsers.
313 if (!error.isMainFrame()) {
314 error.rejectCertificate();
315 return;
316 }
317
318 error.defer();
319 QString szMsg;
320 szMsg = error.description() + "\n\n";
321 szMsg += tr("If you wish so, you may continue with an unverified certificate. "
322 "Accepting an unverified certificate mean you may not be connected with the host you tried to connect to.") +
323 "\n\n" + tr("Do you wish to override the security check and continue ?");
324 int nRet = QMessageBox::critical(window(), tr("Certificate Error"), szMsg,
325 QMessageBox::StandardButton::Yes
326 | QMessageBox::StandardButton::No,
327 QMessageBox::StandardButton::No);
328 if(QMessageBox::StandardButton::Yes == nRet)
329 error.acceptCertificate();
330 else
331 error.rejectCertificate();
332}
333#endif
334
335// Test:
336// - https://postman-echo.com/basic-auth
337// - https://httpbin.org/basic-auth/user/passwd
338// - httpbin.org 提供基本的 HTTP 认证测试
339// - 用户名: user, 密码: passwd
340void CFrmWebView::slotAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth)
341{
342 qDebug(log) << Q_FUNC_INFO;
343 CParameterUser user(nullptr);
344 CDlgUserPassword dlg(this);
345 dlg.SetUser(tr("Set user and password") + "\n" + requestUrl.toString(), &user);
346 if (dlg.exec() == QDialog::Accepted) {
347 auth->setUser(user.GetName());
348 auth->setPassword(user.GetPassword());
349 } else {
350 // Set authenticator null if dialog is cancelled
351 *auth = QAuthenticator();
352 }
353}
354
355void CFrmWebView::slotProxyAuthenticationRequired(const QUrl &url, QAuthenticator *auth,
356 const QString &proxyHost)
357{
358 qDebug(log) << Q_FUNC_INFO;
359 CParameterUser user(nullptr);
360 CDlgUserPassword dlg(this);
361 dlg.SetUser(tr("Set user and password of proxy") + "\n" + proxyHost.toHtmlEscaped(), &user);
362 if (dlg.exec() == QDialog::Accepted) {
363 auth->setUser(user.GetName());
364 auth->setPassword(user.GetPassword());
365 } else {
366 // Set authenticator null if dialog is cancelled
367 *auth = QAuthenticator();
368 }
369 /*
370 QDialog dialog(window());
371 dialog.setModal(true);
372 dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
373
374 Ui::PasswordDialog passwordDialog;
375 passwordDialog.setupUi(&dialog);
376
377 passwordDialog.m_iconLabel->setText(QString());
378 QIcon icon(window()->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, window()));
379 passwordDialog.m_iconLabel->setPixmap(icon.pixmap(32, 32));
380
381 QString introMessage = tr("Connect to proxy \"%1\" using:");
382 introMessage = introMessage.arg(proxyHost.toHtmlEscaped());
383 passwordDialog.m_infoLabel->setText(introMessage);
384 passwordDialog.m_infoLabel->setWordWrap(true);
385
386 if (dialog.exec() == QDialog::Accepted) {
387 auth->setUser(passwordDialog.m_userNameLineEdit->text());
388 auth->setPassword(passwordDialog.m_passwordLineEdit->text());
389 } else {
390 // Set authenticator null if dialog is cancelled
391 *auth = QAuthenticator();
392 }
393 */
394}
395
396#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
397void CFrmWebView::slotPermissionRequested(QWebEnginePermission permission)
398{
399 qDebug(log) << Q_FUNC_INFO;
400 QString title = tr("Permission Request");
401 QString question = questionForPermissionType(permission.permissionType()).arg(permission.origin().host());
402 if (!question.isEmpty() && QMessageBox::question(window(), title, question) == QMessageBox::Yes)
403 permission.grant();
404 else
405 permission.deny();
406}
407
408void CFrmWebView::handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy policy)
409{
410 qDebug(log) << Q_FUNC_INFO;
411 if (!page())
412 return;
413
414 page()->settings()->setImageAnimationPolicy(policy);
415}
416#endif
417
418#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
419void CFrmWebView::slotDesktopMediaRequest(const QWebEngineDesktopMediaRequest &request)
420{
421 qDebug(log) << Q_FUNC_INFO;
422 // select the primary screen
423 request.selectScreen(request.screensModel()->index(0));
424}
425
426void CFrmWebView::slotWebAuthUxRequested(QWebEngineWebAuthUxRequest *request)
427{
428 qDebug(log) << Q_FUNC_INFO;
429
430 if (m_pDlgWebAuth)
431 delete m_pDlgWebAuth;
432
433 m_pDlgWebAuth = new CDlgWebAuth(request, window());
434 m_pDlgWebAuth->setModal(false);
435 m_pDlgWebAuth->setWindowFlags(m_pDlgWebAuth->windowFlags() & ~Qt::WindowContextHelpButtonHint);
436
437 connect(request, &QWebEngineWebAuthUxRequest::stateChanged, this, &CFrmWebView::onStateChanged);
438 m_pDlgWebAuth->show();
439
440}
441
442void CFrmWebView::onStateChanged(QWebEngineWebAuthUxRequest::WebAuthUxState state)
443{
444 qDebug(log) << Q_FUNC_INFO;
445
446 if (QWebEngineWebAuthUxRequest::WebAuthUxState::Completed == state
447 || QWebEngineWebAuthUxRequest::WebAuthUxState::Cancelled == state) {
448 if (m_pDlgWebAuth) {
449 delete m_pDlgWebAuth;
450 m_pDlgWebAuth = nullptr;
451 }
452 } else {
453 m_pDlgWebAuth->updateDisplay();
454 }
455}
456#endif
457
460 QWebEngineRegisterProtocolHandlerRequest request)
461{
462 qDebug(log) << Q_FUNC_INFO;
463 auto answer = QMessageBox::question(window(), tr("Permission Request"),
464 tr("Allow %1 to open all %2 links?")
465 .arg(request.origin().host())
466 .arg(request.scheme()));
467 if (answer == QMessageBox::Yes)
468 request.accept();
469 else
470 request.reject();
471}
473
474#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
475void CFrmWebView::slotFileSystemAccessRequested(QWebEngineFileSystemAccessRequest request)
476{
477 qDebug(log) << Q_FUNC_INFO;
478 QString accessType;
479 switch (request.accessFlags()) {
480 case QWebEngineFileSystemAccessRequest::Read:
481 accessType = "read";
482 break;
483 case QWebEngineFileSystemAccessRequest::Write:
484 accessType = "write";
485 break;
486 case QWebEngineFileSystemAccessRequest::Read | QWebEngineFileSystemAccessRequest::Write:
487 accessType = "read and write";
488 break;
489 default:
490 Q_UNREACHABLE();
491 }
492
493 auto answer = QMessageBox::question(window(), tr("File system access request"),
494 tr("Give %1 %2 access to %3?")
495 .arg(request.origin().host())
496 .arg(accessType)
497 .arg(request.filePath().toString()));
498 if (answer == QMessageBox::Yes)
499 request.accept();
500 else
501 request.reject();
502}
503#endif
504
505QString CFrmWebView::AutoFillForm()
506{
507 return R"(
508 // 自动填充用户名和密码字段
509 function autoFillForm(username, password) {
510 // 查找用户名输入框
511 var usernameFields = [
512 'input[type="text"]',
513 'input[type="email"]',
514 'input[name="username"]',
515 'input[name="user"]',
516 'input[name="email"]',
517 'input[id="username"]',
518 'input[id="user"]',
519 'input[id="email"]'
520 ];
521
522 // 查找密码输入框
523 var passwordFields = [
524 'input[type="password"]',
525 'input[name="password"]',
526 'input[name="pass"]',
527 'input[id="password"]',
528 'input[id="pass"]'
529 ];
530
531 var arrUser = [];
532 var arrPassword = [];
533
534 // 尝试填充用户名
535 usernameFields.forEach(function(selector) {
536 var field = document.querySelector(selector);
537 if (field && !field.value) {
538 arrUser.push(field);
539 // 触发 change 事件
540 //var event = new Event('input', { bubbles: true });
541 //field.dispatchEvent(event);
542 }
543 });
544
545 // 尝试填充密码
546 passwordFields.forEach(function(selector) {
547 var field = document.querySelector(selector);
548 if (field && !field.value) {
549 arrPassword.push(field);
550 // 触发 change 事件
551 //var event = new Event('input', { bubbles: true });
552 //field.dispatchEvent(event);
553 }
554 });
555
556 arrUser.forEach(function(input) {
557 if(!input.disabled && !input.readOnly)
558 input.value = username;
559 });
560 arrPassword.forEach(function(input) {
561 if(!input.disabled && !input.readOnly)
562 input.value = password;
563 });
564 }
565 )";
566}
567
568void CFrmWebView::SetupAutoFillScript() {
569 // 创建自动填充的 JavaScript
570 QString autoFillScript = "(function() {";
571 autoFillScript += AutoFillForm();
572 autoFillScript += R"(
573 // 暴露函数到全局作用域
574 window.autoFillForm = autoFillForm;
575 })();
576 )";
577
578 QWebEngineScript script;
579 script.setSourceCode(autoFillScript);
580 script.setInjectionPoint(QWebEngineScript::DocumentCreation);
581 script.setWorldId(QWebEngineScript::MainWorld);
582 page()->scripts().insert(script);
583}
584
585void CFrmWebView::GlobalFillForm(const QString &szUse, const QString &szPassword)
586{
587 QString js = QString("window.autoFillForm('%1', '%2');").arg(szUse, szPassword);
588 page()->runJavaScript(js);
589}
590
591void CFrmWebView::FillForm(const QString &szUse, const QString &szPassword)
592{
593 QString js = AutoFillForm();
594 js += QString("autoFillForm('%1', '%2');").arg(szUse, szPassword);
595 page()->runJavaScript(js);
596}
597
598int CFrmWebView::GetUserAndPassword(QUrl url, QString &szUser, QString &szPassword)
599{
600 int nRet = 0;
601 //TODO:
602 szUser = "aa2";
603 szPassword = "p12";
604 return nRet;
605}
606
607void CFrmWebView::InjectScriptQWebChannel()
608{
609 Q_ASSERT(page());
610
611 // 从资源载入 qwebchannel.js 和 autofill.js
612 // 你需要在 resources.qrc 中把 js/qwebchannel.js 和 js/autofill.js 注册为资源
613 auto loadResource = [](const QString &rcPath)->QString{
614 QFile f(rcPath);
615 if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
616 qCritical(log) << "Cannot open resource" << rcPath;
617 return QString();
618 }
619 return QString::fromUtf8(f.readAll());
620 };
621
622 QString qwebchannelJs = loadResource(":/js/QWebChannel.js");
623 if (qwebchannelJs.isEmpty()) {
624 qCritical(log) << "QWebChannel.js not found in resources!";
625 return;
626 }
627
628 // 先注入 QWebChannel.js,再注入 AutoFill 脚本。将两者合并到一个 QWebEngineScript 也可以。
629 QWebEngineScript channelScript;
630 channelScript.setName("qwebchannel.js");
631 channelScript.setInjectionPoint(QWebEngineScript::DocumentCreation);
632 channelScript.setRunsOnSubFrames(true);
633 channelScript.setWorldId(QWebEngineScript::MainWorld);
634 channelScript.setSourceCode(qwebchannelJs);
635 page()->scripts().insert(channelScript);
636}
637
638void CFrmWebView::InjectScriptAutoFill()
639{
640 Q_ASSERT(page());
641
642 // 从资源载入 qwebchannel.js 和 autofill.js
643 // 你需要在 resources.qrc 中把 js/qwebchannel.js 和 js/autofill.js 注册为资源
644 auto loadResource = [](const QString &rcPath)->QString{
645 QFile f(rcPath);
646 if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
647 qCritical(log) << "Cannot open resource" << rcPath;
648 return QString();
649 }
650 return QString::fromUtf8(f.readAll());
651 };
652
653 QString autofillJs = loadResource(":/js/AutoFill.js");
654 if (autofillJs.isEmpty()) {
655 qCritical(log) << "AutoFill.js not found in resources!";
656 return;
657 }
658 // QWebEngineScript autoScript;
659 // autoScript.setName("autofill.js");
660 // autoScript.setInjectionPoint(QWebEngineScript::DocumentReady);
661 // autoScript.setRunsOnSubFrames(true);
662 // autoScript.setWorldId(QWebEngineScript::MainWorld);
663 // autoScript.setSourceCode(autofillJs);
664 // page()->scripts().insert(autoScript);
665
666 page()->runJavaScript(autofillJs);
667 //qDebug(log) << "autofillJs:" << autofillJs;
668 // page()->runJavaScript("typeof qt !== 'undefined' ? 'qt-ok' : 'no-qt'",
669 // [](const QVariant &v){ qCritical(log) << v.toString(); });
670 // page()->runJavaScript("typeof qt !== 'undefined' && typeof qt.webChannelTransport !== 'undefined' ? 'transport-ok' : 'no-transport'",
671 // [](const QVariant &v){ qCritical(log) << v.toString(); });
672}
void slotFileSystemAccessRequested(QWebEngineFileSystemAccessRequest request)
[registerProtocolHandlerRequested]
void handleRegisterProtocolHandlerRequested(QWebEngineRegisterProtocolHandlerRequest request)
[registerProtocolHandlerRequested]
用户名与验证方式。此类仅在插件内有效。它的界面是 CParameterUserUI