玉兔远程控制 0.1.0-bate5
载入中...
搜索中...
未找到
FrmWebBrowser.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include <QPainter>
4#include <QTabBar>
5#include <QMessageBox>
6#include <QMenu>
7#include <QFile>
8#include <QDir>
9#include <QPrinter>
10#include <QInputDialog>
11#include <QSplitter>
12#include <QVBoxLayout>
13#include <QWebEngineProfile>
14#include <QWebEngineHistory>
15#include <QWebEngineSettings>
16#include <QWebEngineFindTextResult>
17#include <QRegularExpression>
18#include <QWebEngineCookieStore>
19#include <QStandardPaths>
20#include <QClipboard>
21#include <QApplication>
22#include <QDesktopServices>
23#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
24 #include <QWebEngineProfileBuilder>
25#endif
26#include <QLoggingCategory>
27#include "RabbitCommonDir.h"
28#include "RabbitCommonTools.h"
29#include "FrmWebBrowser.h"
30#include "FrmPopup.h"
31#include "CaptureFullPage.h"
32
33static Q_LOGGING_CATEGORY(log, "WebBrowser.Browser")
34CFrmWebBrowser::CFrmWebBrowser(CParameterWebBrowser *pPara, bool bMenuBar, QWidget *parent)
35 : QWidget{parent}
36 , m_pPara(pPara)
37 , m_pMenuBar(nullptr)
38 , m_pToolBar(nullptr)
39 , m_pBack(nullptr)
40 , m_pForward(nullptr)
41 , m_pRefresh(nullptr)
42 , m_pStop(nullptr)
43 , m_pFind(nullptr)
44 , m_pFindNext(nullptr)
45 , m_pFindPrevious(nullptr)
46 , m_pZoomOriginal(nullptr)
47 , m_pZoomIn(nullptr)
48 , m_pZoomOut(nullptr)
49 , m_pFavAction(nullptr)
50 , m_pGo(nullptr)
51 , m_pAddPage(nullptr)
52 , m_pAddPageIncognito(nullptr)
53 , m_pAddWindow(nullptr)
54 , m_pPrint(nullptr)
55 , m_pPrintToPdf(nullptr)
56 , m_pAddWindowIncognito(nullptr)
57 , m_pDownload(nullptr)
58 , m_pInspector(nullptr)
59 , m_pUrl(nullptr)
60 , m_pUrlLineEdit(nullptr)
61 , m_pProgressBar(nullptr)
62 , m_pTab(nullptr)
63 , m_DownloadManager(pPara)
64 , m_pCapturePage(nullptr)
65 , m_pCaptureFulPage(nullptr)
66 , m_pRecord(nullptr)
67 , m_pMultimediaRecord(nullptr)
68{
69 qDebug(log) << Q_FUNC_INFO;
70 bool check = false;
71
72 setWindowIcon(QIcon::fromTheme("web-browser"));
73
74 QVBoxLayout* pLayout = new QVBoxLayout(this);
75 if(!pLayout) {
76 return;
77 }
78 pLayout->setSpacing(0);
79 pLayout->setContentsMargins(0, 0, 0, 0);
80 setLayout(pLayout);
81
82 m_pToolBar = new QToolBar(this);
83 if(m_pToolBar) {
84 pLayout->addWidget(m_pToolBar);
85 }
86
87 m_pBack = m_pToolBar->addAction(
88 QIcon::fromTheme("go-previous"), tr("Back"),
89 this, [&](){
90 CFrmWebView* pWeb = CurrentView();
91 if(pWeb && pWeb->page())
92 pWeb->page()->action(QWebEnginePage::Back)->trigger();
93 });
94 m_pBack->setEnabled(false);
95 m_pBack->setShortcuts(QKeySequence::Back);
96 m_pBack->setStatusTip(m_pBack->text());
97 m_pForward = m_pToolBar->addAction(
98 QIcon::fromTheme("go-next"), tr("Forward"),
99 this, [&](){
100 CFrmWebView* pWeb = CurrentView();
101 if(pWeb && pWeb->page())
102 pWeb->page()->action(QWebEnginePage::Forward)->trigger();
103 });
104 m_pForward->setEnabled(false);
105 m_pForward->setShortcuts(QKeySequence::Forward);
106 m_pForward->setStatusTip(m_pForward->text());
107 m_pRefresh = m_pToolBar->addAction(
108 QIcon::fromTheme("view-refresh"), tr("Refresh"),
109 this, [&](){
110 CFrmWebView* pWeb = CurrentView();
111 if(pWeb && pWeb->page())
112 pWeb->page()->action(QWebEnginePage::Reload)->trigger();
113 });
114 m_pRefresh->setShortcuts(QKeySequence::Refresh);
115 m_pRefresh->setStatusTip(m_pRefresh->text());
116
117 m_pUrlLineEdit = new QLineEdit(this);
118 m_pFavAction = new QAction(m_pUrlLineEdit);
119 m_pUrlLineEdit->addAction(m_pFavAction, QLineEdit::LeadingPosition);
120 m_pUrlLineEdit->setClearButtonEnabled(true);
121 m_pUrl = m_pToolBar->addWidget(m_pUrlLineEdit);
122 check = connect(m_pUrlLineEdit, &QLineEdit::returnPressed,
123 this, &CFrmWebBrowser::slotReturnPressed);
124 Q_ASSERT(check);
125 check = connect(m_pUrlLineEdit, &QLineEdit::editingFinished,
126 this, &CFrmWebBrowser::slotReturnPressed);
127 Q_ASSERT(check);
128 m_pGo = new QAction(QIcon::fromTheme("go-next"), tr("go"), m_pUrlLineEdit);
129 m_pGo->setStatusTip(m_pGo->text());
130 check = connect(m_pGo, &QAction::triggered, this, &CFrmWebBrowser::slotReturnPressed);
131 Q_ASSERT(check);
132 m_pUrlLineEdit->addAction(m_pGo, QLineEdit::TrailingPosition);
133 m_pGo->setVisible(false);
134 check = connect(m_pUrlLineEdit, &QLineEdit::textEdited,
135 this, [&](const QString &text){
136 QLineEdit* pLineEdit = qobject_cast<QLineEdit*>(sender());
137 if(pLineEdit) {
138 if(text.isEmpty()) {
139 if(m_pGo->isVisible())
140 m_pGo->setVisible(false);
141 } else {
142 if(!m_pGo->isVisible())
143 m_pGo->setVisible(true);
144 }
145 }
146 });
147 Q_ASSERT(check);
148
149 m_pAddPage = m_pToolBar->addAction(QIcon::fromTheme("add"), tr("Add tab page"),
150 this, [&](){
151 CreateWindow(QWebEnginePage::WebBrowserTab);
152 if(!m_pPara->GetTabUrl().isEmpty()) {
153 m_pUrlLineEdit->setText(m_pPara->GetTabUrl());
154 slotReturnPressed();
155 }
156 });
157 m_pAddPage->setStatusTip(m_pAddPage->text());
158 m_pAddPage->setShortcuts(QKeySequence::AddTab);
159 Q_ASSERT(check);
160 m_pDownload = m_pToolBar->addAction(
161 QIcon::fromTheme("emblem-downloads"), tr("Download Manager"));
162 m_pDownload->setCheckable(true);
163 m_pDownload->setStatusTip(m_pDownload->text());
164 m_pDownload->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_D));
165 check = connect(m_pDownload, &QAction::toggled,
166 this, [&](bool checked){
167 if(checked)
168 m_DownloadManager.show();
169 else
170 m_DownloadManager.hide();
171 });
172 Q_ASSERT(check);
173 check = connect(&m_DownloadManager, &CFrmDownloadManager::sigVisible, this,
174 [&](bool visible){
175 if(m_pDownload)
176 m_pDownload->setChecked(visible);
177 });
178 Q_ASSERT(check);
179
180 m_pProgressBar = new QProgressBar(this);
181 if(m_pProgressBar) {
182 pLayout->addWidget(m_pProgressBar);
183 m_pProgressBar->setMaximumHeight(1);
184 m_pProgressBar->setTextVisible(false);
185 m_pProgressBar->show();
186 m_pProgressBar->setStyleSheet("QProgressBar {border: 0px} QProgressBar::chunk {background-color: #da4453}");
187 }
188
189 m_pTab = new QTabWidget(this);
190 if(m_pTab) {
191 m_pTab->setTabsClosable(true);
192 m_pTab->setUsesScrollButtons(true);
193 m_pTab->setMovable(true);
194 m_pTab->setElideMode(Qt::TextElideMode::ElideRight);
195 pLayout->addWidget(m_pTab);
196 }
197 check = connect(m_pTab, &QTabWidget::currentChanged,
198 this, &CFrmWebBrowser::slotTabCurrentChanged);
199 Q_ASSERT(check);
200 check = connect(m_pTab, &QTabWidget::tabCloseRequested,
201 this, &CFrmWebBrowser::slotTabCloseRequested);
202 Q_ASSERT(check);
203
204 m_DownloadManager.hide();
205 QWebEngineProfile::defaultProfile()->setDownloadPath(m_pPara->GetDownloadFolder());
206 check = connect(QWebEngineProfile::defaultProfile(), &QWebEngineProfile::downloadRequested,
207 &m_DownloadManager, &CFrmDownloadManager::slotDownloadRequested);
208 Q_ASSERT(check);
209 check = connect(m_pPara, &CParameterWebBrowser::sigDownloadFolderChanged,
210 this, [&](){
211 QWebEngineProfile::defaultProfile()->setDownloadPath(m_pPara->GetDownloadFolder());
212 if(m_profile)
213 m_profile->setDownloadPath(m_pPara->GetDownloadFolder());
214 });
215 Q_ASSERT(check);
216
217 InitMenu(&m_Menu);
218 if(bMenuBar)
219 {
220 m_pMenuBar = new QMenuBar(this);
221 if(m_pMenuBar) {
222 m_Menu.setTitle(tr("Operate"));
223 m_pMenuBar->addMenu(&m_Menu);
224 pLayout->setMenuBar(m_pMenuBar);
225 }
226 }
227
228 check = connect(&m_tmRecord, &QTimer::timeout, this, &CFrmWebBrowser::slotRecordTimeout);
229 Q_ASSERT(check);
230}
231
232CFrmWebBrowser::~CFrmWebBrowser()
233{
234 qDebug(log) << Q_FUNC_INFO;
235 if(m_pToolBar) {
236 m_pToolBar->deleteLater();
237 m_pToolBar = nullptr;
238 }
239 if(m_pTab) {
240 m_pTab->deleteLater();
241 m_pTab = nullptr;
242 }
243
244 if(m_pPara->GetClearCookie() && m_profile)
245 m_profile->cookieStore()->deleteAllCookies();
246 if(m_pPara->GetClearHttpCache() && m_profile)
247 m_profile->clearHttpCache();
248}
249
250QMenu* CFrmWebBrowser::GetMenu(QWidget *parent)
251{
252 if(m_Menu.actions().isEmpty())
253 return nullptr;
254 return &m_Menu;
255}
256
257QWebEngineView* CFrmWebBrowser::CreateWindow(
258 QWebEnginePage::WebWindowType type, bool offTheRecord)
259{
260 qDebug(log) << "Create window:" << type;
261
262 CFrmWebView* pView = nullptr;
263 switch (type) {
264 case QWebEnginePage::WebBrowserTab: {
265 auto pTab = CreateTab(&pView, offTheRecord);
266 if(!pTab || !m_pTab) break;
267 int index = m_pTab->addTab(pTab, pView->favIcon(), tr("New page"));
268 if(-1 < index)
269 m_pTab->setCurrentIndex(index);
270 break;
271 }
272 case QWebEnginePage::WebBrowserBackgroundTab: {
273 auto pTab = CreateTab(&pView, offTheRecord);
274 if(!pTab || !m_pTab) break;
275 int index = m_pTab->currentIndex();
276 m_pTab->addTab(pTab, pView->favIcon(), tr("New page"));
277 if(-1 < index && index != m_pTab->currentIndex())
278 m_pTab->setCurrentIndex(index);
279 break;
280 }
281 case QWebEnginePage::WebBrowserWindow: {
282 auto pWin = new CFrmWebBrowser(m_pPara, true);
283 if(pWin) {
284 pView = qobject_cast<CFrmWebView*>(
285 pWin->CreateWindow(QWebEnginePage::WebBrowserTab));
286 pWin->setAttribute(Qt::WA_DeleteOnClose);
287 auto pMainWin = RabbitCommon::CTools::GetMainWindow();
288 if(pMainWin)
289 pWin->resize(pMainWin->frameGeometry().width(),
290 pMainWin->frameGeometry().height());
291 pWin->show();
292 }
293 break;
294 }
295 case QWebEnginePage::WebDialog: {
296 // Test: Resource\html\TestQWebEnginePageWebDialog.html
297 auto popup = new CFrmPopup(GetProfile(offTheRecord), this);
298 pView = popup->GetView();
299 }
300 }
301
302 return pView;
303}
304
305void CFrmWebBrowser::SetConnect(CFrmWebView* pWeb)
306{
307 bool check = false;
308 if(!pWeb) return;
309 check = connect(pWeb, &CFrmWebView::sigDevToolsRequested,
310 this, [&](){
311 if(m_pInspector)
312 m_pInspector->setChecked(true);
313 });
314 Q_ASSERT(check);
315 check = connect(pWeb, &CFrmWebView::sigWebActionEnabledChanged,
316 this, [this, pWeb](QWebEnginePage::WebAction webAction, bool enabled){
317 if(!IsCurrentView(pWeb)) return;
318 switch(webAction){
319 case QWebEnginePage::WebAction::Back:
320 m_pBack->setEnabled(enabled);
321 break;
322 case QWebEnginePage::WebAction::Forward:
323 m_pForward->setEnabled(enabled);
324 break;
325 case QWebEnginePage::WebAction::Reload: {
326 m_pRefresh->setEnabled(enabled);
327 if(m_pRefresh->isEnabled())
328 m_pToolBar->insertAction(m_pUrl, m_pRefresh);
329 else
330 m_pToolBar->removeAction(m_pRefresh);
331 break;
332 }
333 case QWebEnginePage::WebAction::Stop: {
334 m_pStop->setEnabled(enabled);
335 if(m_pStop->isEnabled())
336 m_pToolBar->insertAction(m_pUrl, m_pStop);
337 else
338 m_pToolBar->removeAction(m_pStop);
339 break;
340 }
341 default:
342 break;
343 }
344 });
345 check = connect(pWeb, &QWebEngineView::urlChanged,
346 this, [&](const QUrl &url){
347 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
348 if(IsCurrentView(pWeb))
349 m_pUrlLineEdit->setText(url.toString());
350 });
351 Q_ASSERT(check);
352 check = connect(pWeb, &CFrmWebView::titleChanged,
353 this, [&](const QString &title) {
354 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
355 int index = IndexOfTab(pWeb);
356 if(-1 < index) {
357 if(m_pTab)
358 m_pTab->setTabText(index, title);
359 setWindowTitle(title);
360 emit sigUpdateTitle();
361 }
362 });
363 Q_ASSERT(check);
364 check = connect(pWeb, &CFrmWebView::favIconChanged,
365 this, [&](const QIcon &icon){
366 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
367 int index = IndexOfTab(pWeb);
368 if(-1 < index) {
369 if(m_pTab)
370 m_pTab->setTabIcon(index, icon);
371 setWindowIcon(icon);
372 emit sigUpdateTitle();
373 }
374 });
375 Q_ASSERT(check);
376 check = connect(pWeb, &CFrmWebView::sigLinkHovered,
377 this, &CFrmWebBrowser::sigInformation);
378 Q_ASSERT(check);
379 check = connect(pWeb, &CFrmWebView::sigCloseRequested,
380 this, &CFrmWebBrowser::slotViewCloseRequested);
381 Q_ASSERT(check);
382 check = connect(pWeb, &CFrmWebView::loadProgress,
383 this, [&](int progress){
384 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
385 if(IsCurrentView(pWeb))
386 m_pProgressBar->setValue(progress);
387 });
388 Q_ASSERT(check);
389 m_pProgressBar->setValue(pWeb->progress());
390#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
391 check = connect(pWeb, &CFrmWebView::printFinished,
392 this, &CFrmWebBrowser::slotPrintFinished);
393 Q_ASSERT(check);
394 check = connect(pWeb, &CFrmWebView::pdfPrintingFinished,
395 this, &CFrmWebBrowser::slotPdfPrintingFinished);
396 Q_ASSERT(check);
397#endif
398}
399
400QWebEngineProfile* CFrmWebBrowser::GetProfile(bool offTheRecord)
401{
402 if(offTheRecord)
403 return QWebEngineProfile::defaultProfile();
404 if(m_profile)
405 return m_profile.get();
406
407 QSettings set(RabbitCommon::CDir::Instance()->GetDirUserData()
408 + QDir::separator() + "WebBrowser.ini",
409 QSettings::IniFormat);
410 QString name = "io.github.KangLin.RabbitRemoteControl";
411#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
412 name += QLatin1StringView(qWebEngineChromiumVersion());
413 name = set.value("Profile/Name", name).toString();
414 QWebEngineProfileBuilder profileBuilder;
415 m_profile.reset(profileBuilder.createProfile(name));
416#else
417 name = set.value("Profile/Name", name).toString();
418 m_profile.reset(new QWebEngineProfile(name));
419#endif
420 if(!m_profile)
421 return QWebEngineProfile::defaultProfile();
422
423 set.setValue("Profile/Name", name);
424
425 m_profile->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true);
426 m_profile->settings()->setAttribute(QWebEngineSettings::DnsPrefetchEnabled, true);
427 m_profile->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
428 m_profile->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, false);
429#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
430 m_profile->settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
431#endif
432#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
433 m_profile->settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
434#endif
435#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
436 m_profile->settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, true);
437#endif
438#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
439 m_profile->settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, false);
440#endif
441 m_profile->setDownloadPath(m_pPara->GetDownloadFolder());
442 bool check = connect(m_profile.get(), &QWebEngineProfile::downloadRequested,
443 &m_DownloadManager, &CFrmDownloadManager::slotDownloadRequested);
444 Q_ASSERT(check);
445 qDebug(log) << "User agent:" << m_profile->httpUserAgent()
446 << "Persistent path:" << m_profile->persistentStoragePath()
447 << "Cache path:" << m_profile->cachePath()
448 << "Storage name:" << m_profile->storageName()
449 << "Cookie:" << m_profile->cookieStore()
450 << "Is off the Record:" << m_profile->isOffTheRecord()
451 << "Download:" << m_profile->downloadPath();
452 return m_profile.get();
453}
454
455CFrmWebView *CFrmWebBrowser::CreateWebView(bool offTheRecord)
456{
457 auto pView = new CFrmWebView(this);
458 if(pView) {
459 auto profile = GetProfile(offTheRecord);
460// #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
461// profile->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
462// #endif
463 auto page = new QWebEnginePage(profile, pView);
464 pView->setPage(page);
465 }
466 return pView;
467}
468
469QWidget* CFrmWebBrowser::CreateTab(CFrmWebView **view, bool offTheRecord)
470{
471 QSplitter *pSplitter = new QSplitter(Qt::Vertical, this);
472 if(pSplitter) {
473 pSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
474 //pSplitter->setContentsMargins(0, 0, 0, 0);
475 CFrmWebView* pWeb = nullptr;
476 if(view) {
477 if(*view) {
478 pWeb = *view;
479 } else {
480 pWeb = CreateWebView(offTheRecord);
481 *view = pWeb;
482 }
483 } else {
484 pWeb = CreateWebView(offTheRecord);
485 }
486 if(pWeb) {
487 pSplitter->addWidget(pWeb);
488 SetConnect(pWeb);
489 }
490 }
491 return pSplitter;
492}
493
494CFrmWebView *CFrmWebBrowser::CurrentView(ViewType type)
495{
496 if(!m_pTab) return nullptr;
497 auto w = m_pTab->currentWidget();
498 if(!w) return nullptr;
499 QSplitter *pSplitter = qobject_cast<QSplitter*>(w);
500 if(!pSplitter) return nullptr;
501 int index = (int)type;
502 if(0 > index && pSplitter->count() <= index) return nullptr;
503 return qobject_cast<CFrmWebView*>(pSplitter->widget(index));
504}
505
506CFrmWebView* CFrmWebBrowser::GetView(int index, ViewType type)
507{
508 if(0 > index || !m_pTab || m_pTab->count() <= index) return nullptr;
509 auto w = m_pTab->widget(index);
510 if(!w) return nullptr;
511 QSplitter *pSplitter = qobject_cast<QSplitter*>(w);
512 if(!pSplitter) return nullptr;
513 int idx = (int)type;
514 if(0 > idx && pSplitter->count() <= idx) return nullptr;
515 return qobject_cast<CFrmWebView*>(pSplitter->widget(idx));
516}
517
518bool CFrmWebBrowser::IsCurrentView(CFrmWebView *pView)
519{
520 if(!m_pTab) return false;
521 auto w = m_pTab->currentWidget();
522 if(!w) return false;
523 QSplitter *pSplitter = qobject_cast<QSplitter*>(w);
524 if(!pSplitter) return false;
525 return -1 != pSplitter->indexOf(pView);
526}
527
528int CFrmWebBrowser::IndexOfTab(CFrmWebView *pView)
529{
530 int nRet = -1;
531 if(!pView) return nRet;
532 QWidget* p = qobject_cast<QWidget*>(pView->parent());
533 if(!p) return nRet;
534 if(m_pTab)
535 nRet = m_pTab->indexOf(p);
536 return nRet;
537}
538
539int CFrmWebBrowser::InitMenu(QMenu *pMenu)
540{
541 bool check = false;
542 if(!pMenu) return 0;
543 pMenu->addAction(m_pBack);
544 pMenu->addAction(m_pForward);
545 pMenu->addAction(m_pRefresh);
546 m_pStop = pMenu->addAction(
547 QIcon::fromTheme("media-playback-stop"), tr("Stop"), this, [&](){
548 CFrmWebView* pWeb = CurrentView();
549 if(pWeb && pWeb->page())
550 pWeb->page()->action(QWebEnginePage::Stop)->trigger();
551 });
552 m_pStop->setEnabled(false);
553 m_pStop->setShortcuts(QKeySequence::Cancel);
554 m_pStop->setStatusTip(m_pStop->text());
555
556 pMenu->addSeparator();
557 pMenu->addAction(m_pAddPage);
558 m_pAddPageIncognito = pMenu->addAction(
559 QIcon::fromTheme("add"), tr("Add incognito tab"),
560 this, [&](){
561 CreateWindow(QWebEnginePage::WebBrowserTab, true);
562 if(!m_pPara->GetTabUrl().isEmpty()) {
563 m_pUrlLineEdit->setText(m_pPara->GetTabUrl());
564 slotReturnPressed();
565 }
566 });
567 m_pAddPageIncognito->setStatusTip(m_pAddPageIncognito->text());
568 m_pAddWindow = pMenu->addAction(
569 QIcon::fromTheme("add"), tr("Add window"),
570 this, [&](){
571 CreateWindow(QWebEnginePage::WebBrowserWindow);
572 });
573 m_pAddWindow->setVisible(false);
574 m_pAddWindow->setStatusTip(m_pAddWindow->text());
575 m_pAddWindowIncognito = pMenu->addAction(
576 QIcon::fromTheme("add"), tr("Add Incognito Window"),
577 this, [&](){
578 CreateWindow(QWebEnginePage::WebBrowserWindow, true);
579 });
580 m_pAddWindowIncognito->setVisible(false);
581 m_pAddWindowIncognito->setStatusTip(m_pAddWindowIncognito->text());
582
583 pMenu->addSeparator();
584 m_pFind = pMenu->addAction(
585 QIcon::fromTheme("edit-find"), tr("&Find"), this,
586 [&](){
587 CFrmWebView* pWeb = CurrentView();
588 if(pWeb) {
589 bool ok = false;
590 if(pWeb->selectedText().isEmpty()) {
591 if(QApplication::clipboard()
592 && !QApplication::clipboard()->text().isEmpty())
593 m_szFindText = QApplication::clipboard()->text();
594 } else {
595 m_szFindText = pWeb->selectedText();
596 }
597 QString search = QInputDialog::getText(
598 this, tr("Find"),
599 tr("Find:"), QLineEdit::Normal,
600 m_szFindText, &ok);
601 if (ok && !search.isEmpty()) {
602 m_szFindText = search;
603#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
604 pWeb->findText(m_szFindText, QWebEnginePage::FindFlags(),
605 [&](const QWebEngineFindTextResult& result){
606 if (result.numberOfMatches() == 0) {
607 emit sigInformation(tr("\"%1\" not found.").arg(m_szFindText));
608 } else {
609 emit sigInformation(tr("\"%1\" found: %2/%3").arg(m_szFindText, QString::number(result.activeMatch()), QString::number(result.numberOfMatches())));
610 }
611 });
612#else
613 pWeb->findText(m_szFindText, QWebEnginePage::FindFlags(), [this](bool found) {
614 if (!found)
615 emit sigInformation(tr("\"%1\" not found.").arg(m_szFindText));
616 });
617#endif
618 }
619 }
620 });
621 m_pFind->setShortcuts(QKeySequence::Find);
622 m_pFind->setStatusTip(m_pFind->text());
623
624 m_pFindNext = pMenu->addAction(
625 QIcon::fromTheme("go-next"), tr("Find &Next"), this,
626 [this]() {
627 CFrmWebView* pWeb = CurrentView();
628 if(pWeb && !m_szFindText.isEmpty()) {
629 pWeb->findText(m_szFindText);
630 }
631 });
632 m_pFindNext->setShortcuts(QKeySequence::FindNext);
633 m_pFindNext->setText(m_pFindNext->text());
634
635 m_pFindPrevious = pMenu->addAction(
636 QIcon::fromTheme("go-previous"), tr("Find &Previous"), this,
637 [&]() {
638 CFrmWebView* pWeb = CurrentView();
639 if(pWeb && !m_szFindText.isEmpty()) {
640 pWeb->findText(m_szFindText, QWebEnginePage::FindBackward);
641 }
642 });
643 m_pFindPrevious->setShortcuts(QKeySequence::FindPrevious);
644 m_pFindPrevious->setStatusTip(m_pFindPrevious->text());
645
646 pMenu->addSeparator();
647 m_pZoomOriginal = pMenu->addAction(
648 QIcon::fromTheme("zoom-original"), tr("Original"));
649 m_pZoomOriginal->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0));
650 m_pZoomOriginal->setStatusTip(tr("Original"));
651 m_pZoomOriginal->setToolTip(tr("Original"));
652 check = connect(m_pZoomOriginal, &QAction::triggered, this,
653 [&](){
654 CFrmWebView* pWeb = CurrentView();
655 if(pWeb)
656 pWeb->setZoomFactor(1.0);
657 });
658 Q_ASSERT(check);
659 m_pZoomIn = pMenu->addAction(QIcon::fromTheme("zoom-in"), tr("Zoom in"));
660 m_pZoomIn->setShortcuts(QKeySequence::ZoomIn);
661 m_pZoomIn->setStatusTip(tr("Zoom in"));
662 m_pZoomIn->setToolTip(tr("Zoom in"));
663 check = connect(
664 m_pZoomIn, &QAction::triggered, this,
665 [&](){
666 CFrmWebView* pWeb = CurrentView();
667 if(pWeb)
668 pWeb->setZoomFactor(pWeb->zoomFactor() + 0.1);
669 });
670 Q_ASSERT(check);
671 m_pZoomOut = pMenu->addAction(
672 QIcon::fromTheme("zoom-out"), tr("Zoom out"));
673 m_pZoomOut->setShortcuts(QKeySequence::ZoomOut);
674 m_pZoomOut->setStatusTip(tr("Zoom out"));
675 m_pZoomOut->setToolTip(tr("Zoom out"));
676 check = connect(
677 m_pZoomOut, &QAction::triggered, this,
678 [&](){
679 CFrmWebView* pWeb = CurrentView();
680 if(pWeb)
681 pWeb->setZoomFactor(pWeb->zoomFactor() - 0.1);
682 });
683 Q_ASSERT(check);
684
685 pMenu->addSeparator();
686#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
687 m_pPrint = pMenu->addAction(
688 QIcon::fromTheme("document-print"), tr("Print"),
689 this, &CFrmWebBrowser::slotPrint);
690 m_pPrint->setVisible(false);
691 m_pPrint->setShortcuts(QKeySequence::Print);
692 m_pPrintToPdf = pMenu->addAction(
693 QIcon::fromTheme("document-print"), tr("Print to PDF"),
694 this, &CFrmWebBrowser::slotPrintToPdf);
695#endif
696
697 pMenu->addAction(m_pDownload);
698 if(!m_pInspector) {
699 m_pInspector = pMenu->addAction(QIcon::fromTheme("tools"), tr("Inspector"));
700 check = connect(m_pInspector, &QAction::toggled,
701 this, &CFrmWebBrowser::slotInspector);
702 Q_ASSERT(check);
703 m_pInspector->setStatusTip(tr("Inspector"));
704 m_pInspector->setCheckable(true);
705 m_pInspector->setEnabled(false);
706 m_pInspector->setShortcuts({
707 QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_I),
708 QKeySequence(Qt::Key_F12)
709 });
710 }
711
712 pMenu->addSeparator();
713 m_pCapturePage = pMenu->addAction(
714 QIcon::fromTheme("screen-shot"), tr("Capture page"),
715 this, &CFrmWebBrowser::slotCapturePage);
716 m_pCapturePage->setStatusTip(tr("Capture page"));
717 m_pCaptureFulPage = pMenu->addAction(
718 QIcon::fromTheme("screen-shot"), tr("Capture full page"),
719 this, &CFrmWebBrowser::slotCaptureFullPage);
720 m_pCaptureFulPage->setStatusTip(tr("Capture full page"));
721 m_pRecord = pMenu->addAction(QIcon::fromTheme("media-record"), tr("Record"),
722 this, &CFrmWebBrowser::slotRecord);
723 m_pRecord->setCheckable(true);
724 m_pRecord->setStatusTip(tr("Record"));
725#ifdef HAVE_QT6_RECORD
726 m_pRecord->setEnabled(true);
727#else
728 m_pRecord->setEnabled(false);
729#endif
730
731 EnableAction(false);
732 return 0;
733}
734
735int CFrmWebBrowser::Start()
736{
737 int nRet = 0;
738 if(m_pTab && m_pTab->count() == 0) {
739 // Add new web view
740 m_pAddPage->trigger();
741 }
742 return nRet;
743}
744
745int CFrmWebBrowser::Stop()
746{
747 int nRet = 0;
748
749 if(m_pRecord && m_pRecord->isChecked())
750 slotRecord();
751 if(m_pTab) {
752 for(int i = 0; i < m_pTab->count(); i++) {
753 auto v = GetView(i);
754 if(!v) continue;
755 v->stop();
756 }
757 }
758 return nRet;
759}
760
761void CFrmWebBrowser::slotTabCurrentChanged(int index)
762{
763 if(-1 == index) return;
764 CFrmWebView* pWeb = CurrentView();
765 if(pWeb) {
766 setWindowTitle(pWeb->title());
767 setWindowIcon(pWeb->icon());
768 m_pUrlLineEdit->setText(pWeb->url().toString());
769 m_pProgressBar->setValue(pWeb->progress());
770 EnableAction(true);
771 auto page = pWeb->page();
772 if(page) {
773 auto action = page->action(QWebEnginePage::Back);
774 if(action && m_pBack) {
775 m_pBack->setEnabled(action->isEnabled());
776 }
777 action = page->action(QWebEnginePage::Forward);
778 if(action && m_pForward) {
779 m_pForward->setEnabled(action->isEnabled());
780 }
781 action = page->action(QWebEnginePage::Reload);
782 if(action && m_pRefresh)
783 m_pRefresh->setEnabled(action->isEnabled());
784 if(m_pRefresh->isEnabled())
785 m_pToolBar->insertAction(m_pUrl, m_pRefresh);
786 else
787 m_pToolBar->removeAction(m_pRefresh);
788 action = page->action(QWebEnginePage::Stop);
789 if(action && m_pStop)
790 m_pStop->setEnabled(action->isEnabled());
791 if(m_pStop->isEnabled())
792 m_pToolBar->insertAction(m_pUrl, m_pStop);
793 else
794 m_pToolBar->removeAction(m_pStop);
795 if(m_pInspector) {
796 m_pInspector->setChecked(CurrentView(ViewType::DevTools));
797 }
798 }
799 } else {
800 setWindowTitle(tr("Web browser"));
801 setWindowIcon(QIcon::fromTheme("web-browser"));
802 m_pUrlLineEdit->setText("");
803 m_pProgressBar->setValue(100);
804 EnableAction(false);
805 if(m_pInspector)
806 m_pInspector->setChecked(false);
807 }
808 emit sigUpdateTitle();
809}
810
811void CFrmWebBrowser::EnableAction(bool enable)
812{
813 if(m_pBack)
814 m_pBack->setEnabled(false);
815 if(m_pForward)
816 m_pForward->setEnabled(false);
817 if(m_pRefresh)
818 m_pRefresh->setEnabled(enable);
819 if(m_pStop)
820 m_pStop->setEnabled(false);
821 if(m_pFind)
822 m_pFind->setEnabled(enable);
823 if(m_pFindNext)
824 m_pFindNext->setEnabled(enable);
825 if(m_pFindPrevious)
826 m_pFindPrevious->setEnabled(enable);
827 if(m_pZoomOriginal)
828 m_pZoomOriginal->setEnabled(enable);
829 if(m_pZoomIn)
830 m_pZoomIn->setEnabled(enable);
831 if(m_pZoomOut)
832 m_pZoomOut->setEnabled(enable);
833 if(m_pInspector)
834 m_pInspector->setEnabled(enable);
835 if(m_pRecord)
836 m_pRecord->setEnabled(enable);
837}
838
839void CFrmWebBrowser::slotTabCloseRequested(int index)
840{
841 qDebug(log) << "slotTabCloseRequested:" << index;
842 if(-1 == index || !m_pTab) return;
843 auto pView = m_pTab->widget(index);
844 if(pView)
845 pView->deleteLater();
846 m_pTab->removeTab(index);
847}
848
849void CFrmWebBrowser::slotViewCloseRequested()
850{
851 CFrmWebView* p = qobject_cast<CFrmWebView*>(sender());
852 if(!p) return;
853 int index = IndexOfTab(p);
854 slotTabCloseRequested(index);
855}
856
857void CFrmWebBrowser::slotReturnPressed()
858{
859 QUrl u = QUrl::fromUserInput(m_pUrlLineEdit->text());
860 qDebug(log) << u << m_pUrlLineEdit->text();
861 if(u.isEmpty()) {
862 QString szSearch;
863 if(m_pPara) {
864 szSearch = m_pPara->GetSearchEngine();
865 u = szSearch.replace(m_pPara->GetSearchRelaceString(),
866 QUrl::toPercentEncoding(m_pUrlLineEdit->text()));
867 }
868 }
869 qDebug(log) << u << m_pUrlLineEdit->text();
870 emit sigInformation(u.toString());
871 CFrmWebView* pWeb = CurrentView();
872 if(!pWeb)
873 pWeb = qobject_cast<CFrmWebView*>(CreateWindow(QWebEnginePage::WebBrowserTab));
874 pWeb->load(u);
875 if(m_pGo->isVisible())
876 m_pGo->setVisible(false);
877}
878
879void CFrmWebBrowser::slotInspector(bool checked)
880{
881 CFrmWebView* pWeb = CurrentView();
882 if(!pWeb || !m_pTab) return;
883 auto dev = CurrentView(ViewType::DevTools);
884 if(pWeb && pWeb->page() && checked) {
885 if(!dev) {
886 auto w = m_pTab->currentWidget();
887 if(!w) return;
888 QSplitter *pSplitter = qobject_cast<QSplitter*>(w);
889 if(!pSplitter || 1 != pSplitter->count()) return;
890 dev = CreateWebView(pWeb->page()->profile()->isOffTheRecord());
891 if(dev) {
892 dev->show();
893 pSplitter->addWidget(dev);
894 bool check = connect(pWeb, &CFrmWebView::sigDevToolsRequested,
895 this, [&](){
896 if(m_pInspector)
897 m_pInspector->setChecked(true);
898 });
899 Q_ASSERT(check);
900 check = connect(dev, &CFrmWebView::sigCloseRequested,
901 this, [this]() {
902 m_pInspector->setChecked(false);
903 });
904 Q_ASSERT(check);
905 }
906 }
907 if(dev) {
908 pWeb->page()->setDevToolsPage(dev->page());
909 if(dev->isHidden())
910 dev->show();
911 }
912 } else {
913 pWeb->page()->setDevToolsPage(nullptr);
914 if(dev) {
915 dev->setParent(nullptr);
916 dev->deleteLater();
917 }
918 }
919}
920
921int CFrmWebBrowser::Load(QSettings &set)
922{
923 if(m_pPara && m_pPara->GetOpenPrevious()) {
924 set.beginGroup("OpenPrevious");
925 int nCount = 0;
926 nCount = set.value("Count", 0).toInt();
927 int nCurrent = set.value("Current", -1).toInt();
928 for(int i = 0; i < nCount; i++)
929 {
930 QString u = set.value("Url/" + QString::number(i)).toString();
931 auto pView = CreateWindow(QWebEnginePage::WebBrowserTab);
932 pView->load(QUrl(u));
933
934 QByteArray history;
935 history = set.value("History/" + QString::number(i)).toByteArray();
936 QDataStream d(&history, QIODevice::ReadWrite);
937 d >> *pView->history();
938 }
939 if(-1 < nCurrent && m_pTab->count() > nCurrent)
940 m_pTab->setCurrentIndex(nCurrent);
941 set.endGroup();
942 }
943 return 0;
944}
945
946int CFrmWebBrowser::Save(QSettings &set)
947{
948 if(m_pPara && m_pPara->GetOpenPrevious()) {
949 set.beginGroup("OpenPrevious");
950 set.setValue("Count", m_pTab->count());
951 set.setValue("Current", m_pTab->currentIndex());
952
953 for(int i = 0; i < m_pTab->count(); i++) {
954 auto v = GetView(i);
955 if(v) {
956 set.setValue("Url/" + QString::number(i), v->url().toString());
957 QByteArray history;
958 QDataStream d(&history, QIODevice::ReadWrite);
959 d << *v->history();
960 set.setValue("History/" + QString::number(i), history);
961 }
962 }
963
964 set.endGroup();
965 }
966 return 0;
967}
968
969void CFrmWebBrowser::slotPrint()
970{
971 CFrmWebView* pWeb = CurrentView();
972 if(pWeb) {
973#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
974 QPrinter printer;
975 pWeb->print(&printer);
976#endif
977 }
978}
979
980void CFrmWebBrowser::slotPrintFinished(bool success)
981{
982 if(success && m_pPara->GetPromptPrintFinished()) {
983 QMessageBox::information(this, tr("Print finished"),
984 tr("Successfully printed"));
985 }
986}
987
988void CFrmWebBrowser::slotPrintToPdf()
989{
990 CFrmWebView* pWeb = CurrentView();
991 if(pWeb) {
992 QString szPath;
993 szPath += RabbitCommon::CDir::Instance()->GetDirUserData()
994 + QDir::separator() + "pdf";
995 QDir d(szPath);
996 if(!d.exists())
997 d.mkpath(szPath);
998 szPath += QDir::separator() + pWeb->page()->title() + ".pdf";
999 qDebug(log) << "pdf:" << szPath;
1000#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
1001 pWeb->printToPdf(szPath);
1002#endif
1003 }
1004}
1005
1006void CFrmWebBrowser::slotPdfPrintingFinished(const QString& szFile, bool success) {
1007 if(success && m_pPara->GetPromptPrintFinished())
1008 QMessageBox::information(this, tr("Print to PDF finished"),
1009 tr("Successfully printed to PDF.") + "\n"
1010 + tr("PDF file: ") + szFile);
1011 qDebug(log) << "Print to PDF:" << szFile << success;
1012}
1013
1014void CFrmWebBrowser::slotFullScreen(bool bFullScreen)
1015{
1016 if(sender() == this)
1017 return;
1018 if(bFullScreen) {
1019 m_pToolBar->hide();
1020 m_pProgressBar->hide();
1021 m_pTab->tabBar()->setVisible(false);
1022 m_szStyleSheet = m_pTab->styleSheet();
1023 m_pTab->setStyleSheet("QTabWidget::pane{top:0px;left:0px;border:none;}");
1024 } else {
1025 m_pToolBar->show();
1026 m_pProgressBar->show();
1027 m_pTab->tabBar()->setVisible(true);
1028 m_pTab->setStyleSheet(m_szStyleSheet);
1029 if(CurrentView() && CurrentView()->page() && CurrentView()->page()->action(QWebEnginePage::ExitFullScreen))
1030 CurrentView()->page()->action(QWebEnginePage::ExitFullScreen)->toggle();
1031 }
1032 emit sigFullScreen(bFullScreen);
1033}
1034
1035void CFrmWebBrowser::slotCapturePage()
1036{
1037 auto pWeb = CurrentView();
1038 if(!pWeb) return;
1039
1040 QString szMsg;
1041 QPixmap pixmap = pWeb->grab();
1042 QImage image = pixmap.toImage();
1043 QString szFile = m_pPara->m_Record.GetImageFile(true);
1044 if(szFile.isEmpty() && image.isNull()) return;
1045 if(!image.save(szFile, "PNG"))
1046 {
1047 QString szErr;
1048 szErr = tr("Fail: Save capture page to the file: ") + szFile;
1049 qCritical(log) << szErr;
1050 emit sigError(RV::FailCapturePage, szErr);
1051 return;
1052 }
1053 szMsg = tr("Save capture page to the file:") + szFile;
1054 qInfo(log) << szMsg;
1055 emit sigInformation(szMsg);
1056 qDebug(log) << "End action:" << m_pPara->m_Record.GetEndAction();
1057 switch(m_pPara->m_Record.GetEndAction())
1058 {
1059 case CParameterRecord::ENDACTION::OpenFile: {
1060 bool bRet = QDesktopServices::openUrl(QUrl::fromLocalFile(szFile));
1061 if(!bRet)
1062 qCritical(log) << "Fail: Open capture page the file:" << szFile;
1063 break;
1064 }
1065 case CParameterRecord::ENDACTION::OpenFolder: {
1066 QFileInfo fi(szFile);
1067 QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absolutePath()));
1068 break;
1069 }
1070 default:
1071 break;
1072 }
1073}
1074
1075void CFrmWebBrowser::slotCaptureFullPage()
1076{
1077 auto pWeb = CurrentView();
1078 if(!pWeb) return;
1079 qDebug(log) << "contents size:" << pWeb->page()->contentsSize();
1080 QString szFile = m_pPara->m_Record.GetImageFile(true);
1081 CCaptureFullPage *pCapture = new CCaptureFullPage();
1082 if(szFile.isEmpty() || !pCapture)
1083 return;
1084 bool check = connect(pCapture, &CCaptureFullPage::sigFinished,
1085 this, [this, pWeb, szFile, pCapture](){
1086 pWeb->setEnabled(true);
1087 qDebug(log) << "End action:" << m_pPara->m_Record.GetEndAction();
1088 QFileInfo fi(szFile);
1089 if(!fi.exists()) {
1090 QString szErr = tr("Fail: capture full page");
1091 qCritical(log) << szErr;
1092 emit sigError(FailCaptureFullPage, szErr);
1093 } else {
1094 QString szMsg = tr("Capture full page to") + " " + szFile;
1095 qInfo(log) << szMsg;
1096 emit sigInformation(szMsg);
1097 switch(m_pPara->m_Record.GetEndAction())
1098 {
1099 case CParameterRecord::ENDACTION::OpenFile: {
1100 bool bRet = QDesktopServices::openUrl(QUrl::fromLocalFile(szFile));
1101 if(!bRet)
1102 qCritical(log) << "Fail: Open capture page the file:" << szFile;
1103 break;
1104 }
1105 case CParameterRecord::ENDACTION::OpenFolder: {
1106 QFileInfo fi(szFile);
1107 QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absolutePath()));
1108 break;
1109 }
1110 default:
1111 break;
1112 }
1113 pCapture->deleteLater();
1114 }
1115 });
1116 Q_ASSERT(check);
1117 pWeb->setEnabled(false);
1118 pCapture->Start(pWeb, pWeb->url(), szFile);
1119
1120 emit sigInformation(tr("Start capture full page"));
1121}
1122
1123void CFrmWebBrowser::slotRecord()
1124{
1125 if(m_pRecord->isChecked()) {
1126 m_pRecord->setText(tr("Stop record"));
1127 m_pRecord->setIcon(QIcon::fromTheme("media-stop"));
1128
1129 Q_ASSERT(!m_pMultimediaRecord);
1130 // Note: It automatically releases memory when exiting.
1131 // See: CMultimediaRecordThread::CMultimediaRecordThread()
1132 m_pMultimediaRecord = new CMultimediaRecordThread(m_pPara);
1133 if(m_pMultimediaRecord) {
1134 connect(m_pMultimediaRecord, &CMultimediaRecordThread::finished,
1135 this, [this]() {
1136 m_pMultimediaRecord = nullptr;
1137 if(m_pRecord->isChecked()) {
1138 m_pRecord->activate(QAction::Trigger);
1139 emit sigError(RV::FailRecordPage, tr("Fail record web page"));
1140 }
1141 });
1142 m_pMultimediaRecord->start();
1143 }
1144
1145 // modify interval
1146 qreal rate = m_pPara->m_Record.GetVideoFrameRate();
1147 if(rate <= 0)
1148 rate = 24;
1149 m_tmRecord.start(qreal(1000) / rate);
1150 emit sigInformation(tr("Start record web page"));
1151 } else {
1152 m_pRecord->setIcon(QIcon::fromTheme("media-record"));
1153 m_pRecord->setText(tr("Record"));
1154 m_tmRecord.stop();
1155 if(m_pMultimediaRecord) {
1156 QMetaObject::invokeMethod(m_pMultimediaRecord,
1157 "slotQuit",
1158 Qt::AutoConnection);
1159 m_pMultimediaRecord = nullptr;
1160 emit sigInformation(tr("Record web page is stopped"));
1161 }
1162 }
1163 m_pRecord->setStatusTip(m_pRecord->text());
1164}
1165
1166void CFrmWebBrowser::slotRecordTimeout()
1167{
1168 //qDebug(log) << Q_FUNC_INFO;
1169 if(!m_pMultimediaRecord) return;
1170 auto pWeb = CurrentView();
1171 if(!pWeb) return;
1172 QPixmap pixmap = pWeb->grab();
1173 QImage image = pixmap.toImage();
1174 if(image.isNull()) return;
1175 QMetaObject::invokeMethod(m_pMultimediaRecord,
1176 "slotUpdateVideoFrame",
1177 Qt::AutoConnection,
1178 Q_ARG(QImage, image));
1179}
void sigUpdateTitle()
Title or icon changed
The CMultimediaRecordThread class