3#include "FrmWebBrowser.h"
9#include <QWebEngineProfile>
10#include <QWebEngineSettings>
11#include <QWebEngineFindTextResult>
12#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
13#include <QWebEngineProfileBuilder>
15#include <QLoggingCategory>
17static QScopedPointer<QWebEngineProfile> g_profile;
18static Q_LOGGING_CATEGORY(log,
"WebBrowser.Browser")
28 , m_pFindNext(
nullptr)
29 , m_pFindPrevious(
nullptr)
30 , m_pZoomOriginal(
nullptr)
33 , m_pFavAction(
nullptr)
36 , m_pAddWindow(
nullptr)
37 , m_pDownload(
nullptr)
38 , m_pInspector(
nullptr)
39 , m_pUrlLineEdit(
nullptr)
40 , m_pProgressBar(
nullptr)
42 , m_DownloadManager(pPara)
45 QVBoxLayout* pLayout =
new QVBoxLayout(
this);
49 pLayout->setSpacing(0);
50 pLayout->setContentsMargins(0, 0, 0, 0);
53 m_pToolBar =
new QToolBar(
this);
55 pLayout->addWidget(m_pToolBar);
58 m_pBack = m_pToolBar->addAction(
59 QIcon::fromTheme(
"go-previous"), tr(
"Back"),
62 if(pWeb && pWeb->page())
63 pWeb->page()->action(QWebEnginePage::Back)->trigger();
65 m_pBack->setEnabled(
false);
66 m_pBack->setStatusTip(m_pBack->text());
67 m_pForward = m_pToolBar->addAction(
68 QIcon::fromTheme(
"go-next"), tr(
"Forward"),
71 if(pWeb && pWeb->page())
72 pWeb->page()->action(QWebEnginePage::Forward)->trigger();
74 m_pForward->setEnabled(
false);
75 m_pForward->setStatusTip(m_pForward->text());
76 m_pRefresh = m_pToolBar->addAction(
77 QIcon::fromTheme(
"view-refresh"), tr(
"Refresh"),
80 if(pWeb && pWeb->page())
81 pWeb->page()->action(QWebEnginePage::Reload)->trigger();
83 m_pRefresh->setShortcut(QKeySequence(QKeySequence::Refresh));
84 m_pRefresh->setStatusTip(m_pRefresh->text());
86 m_pUrlLineEdit =
new QLineEdit(
this);
87 m_pFavAction =
new QAction(m_pUrlLineEdit);
88 m_pUrlLineEdit->addAction(m_pFavAction, QLineEdit::LeadingPosition);
89 m_pUrlLineEdit->setClearButtonEnabled(
true);
90 m_pToolBar->addWidget(m_pUrlLineEdit);
91 check = connect(m_pUrlLineEdit, &QLineEdit::returnPressed,
92 this, &CFrmWebBrowser::slotReturnPressed);
94 check = connect(m_pUrlLineEdit, &QLineEdit::editingFinished,
95 this, &CFrmWebBrowser::slotReturnPressed);
97 m_pGo =
new QAction(QIcon::fromTheme(
"go-next"), tr(
"go"), m_pUrlLineEdit);
98 m_pGo->setStatusTip(m_pGo->text());
99 check = connect(m_pGo, &QAction::triggered,
this, &CFrmWebBrowser::slotReturnPressed);
101 m_pUrlLineEdit->addAction(m_pGo, QLineEdit::TrailingPosition);
102 m_pGo->setVisible(
false);
103 check = connect(m_pUrlLineEdit, &QLineEdit::textEdited,
104 this, [&](
const QString &text){
105 QLineEdit* pLineEdit = qobject_cast<QLineEdit*>(sender());
108 if(m_pGo->isVisible())
109 m_pGo->setVisible(
false);
111 if(!m_pGo->isVisible())
112 m_pGo->setVisible(
true);
118 m_pAddPage = m_pToolBar->addAction(QIcon::fromTheme(
"add"), tr(
"Add tab page"),
120 auto pView = createWindow(QWebEnginePage::WebBrowserTab);
121 if(!m_pPara->GetTabUrl().isEmpty()) {
122 m_pUrlLineEdit->setText(m_pPara->GetTabUrl());
126 m_pAddPage->setStatusTip(m_pAddPage->text());
128 m_pDownload = m_pToolBar->addAction(QIcon::fromTheme(
"emblem-downloads"), tr(
"Download Manager"));
129 m_pDownload->setCheckable(
true);
130 m_pDownload->setStatusTip(m_pDownload->text());
131 check = connect(m_pDownload, &QAction::toggled,
132 this, [&](
bool checked){
134 m_DownloadManager.show();
136 m_DownloadManager.hide();
139 check = connect(&m_DownloadManager, &CFrmDownloadManager::sigVisible,
this,
142 m_pDownload->setChecked(visible);
146 m_pProgressBar =
new QProgressBar(
this);
148 pLayout->addWidget(m_pProgressBar);
149 m_pProgressBar->setMaximumHeight(1);
150 m_pProgressBar->setTextVisible(
false);
151 m_pProgressBar->show();
152 m_pProgressBar->setStyleSheet(
"QProgressBar {border: 0px} QProgressBar::chunk {background-color: #da4453}");
155 m_pTab =
new QTabWidget(
this);
157 m_pTab->setTabsClosable(
true);
158 m_pTab->setUsesScrollButtons(
true);
159 m_pTab->setMovable(
true);
160 m_pTab->setElideMode(Qt::TextElideMode::ElideRight);
161 pLayout->addWidget(m_pTab);
163 check = connect(m_pTab, &QTabWidget::currentChanged,
164 this, &CFrmWebBrowser::slotTabCurrentChanged);
166 check = connect(m_pTab, &QTabWidget::tabCloseRequested,
167 this, &CFrmWebBrowser::slotTabCloseRequested);
170 m_DownloadManager.hide();
171 QObject::connect(QWebEngineProfile::defaultProfile(), &QWebEngineProfile::downloadRequested,
172 &m_DownloadManager, &CFrmDownloadManager::slotDownloadRequested);
175CFrmWebBrowser::~CFrmWebBrowser()
178 m_pToolBar->deleteLater();
179 m_pToolBar =
nullptr;
182 m_pTab->deleteLater();
187QWebEngineView* CFrmWebBrowser::createWindow(QWebEnginePage::WebWindowType type)
189 qDebug(log) <<
"Create window:" << type;
193 case QWebEnginePage::WebBrowserTab: {
194 auto pTab = CreateTab(&pView);
195 int index = m_pTab->addTab(pTab, pView->favIcon(), tr(
"New page"));
197 m_pTab->setCurrentIndex(index);
200 case QWebEnginePage::WebBrowserBackgroundTab: {
201 auto pTab = CreateTab(&pView);
202 int index = m_pTab->currentIndex();
203 m_pTab->addTab(pTab, pView->favIcon(), tr(
"New page"));
205 m_pTab->setCurrentIndex(index);
208 case QWebEnginePage::WebBrowserWindow: {
209 auto pWin = CreateTab(&pView);
213 case QWebEnginePage::WebDialog: {
214 qDebug(log) <<
"Don't support type:" << type;
225 check = connect(pWeb, &CFrmWebView::sigWebActionEnabledChanged,
226 this, [
this, pWeb](QWebEnginePage::WebAction webAction,
bool enabled){
227 if(!IsCurrentView(pWeb))
return;
229 case QWebEnginePage::WebAction::Back:
230 m_pBack->setEnabled(enabled);
232 case QWebEnginePage::WebAction::Forward:
233 m_pForward->setEnabled(enabled);
235 case QWebEnginePage::WebAction::Reload:
236 m_pRefresh->setEnabled(enabled);
238 case QWebEnginePage::WebAction::Stop:
239 m_pStop->setEnabled(enabled);
245 check = connect(pWeb, &QWebEngineView::urlChanged,
246 this, [&](
const QUrl &url){
247 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
248 if(IsCurrentView(pWeb))
249 m_pUrlLineEdit->setText(url.toString());
252 check = connect(pWeb, &CFrmWebView::titleChanged,
253 this, [&](
const QString &title) {
254 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
255 int index = IndexOfTab(pWeb);
257 m_pTab->setTabText(index, title);
258 setWindowTitle(title);
263 check = connect(pWeb, &CFrmWebView::favIconChanged,
264 this, [&](
const QIcon &icon){
265 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
266 int index = IndexOfTab(pWeb);
268 m_pTab->setTabIcon(index, icon);
274 check = connect(pWeb, &CFrmWebView::sigLinkHovered,
275 this, &CFrmWebBrowser::sigInformation);
277 check = connect(pWeb, &CFrmWebView::sigCloseRequested,
278 this, &CFrmWebBrowser::slotViewCloseRequested);
280 check = connect(pWeb, &CFrmWebView::loadProgress,
281 this, [&](
int progress){
282 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
283 if(IsCurrentView(pWeb))
284 m_pProgressBar->setValue(progress);
287 m_pProgressBar->setValue(pWeb->progress());
290QWebEngineProfile* CFrmWebBrowser::GetProfile()
294#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
295 const QString name =
"io.github.KangLin.RabbitRemoteControl."
296 + QLatin1StringView(qWebEngineChromiumVersion());
297 QWebEngineProfileBuilder profileBuilder;
298 g_profile.reset(profileBuilder.createProfile(name));
300 const QString name =
"io.github.KangLin.RabbitRemoteControl";
301 g_profile.reset(
new QWebEngineProfile(name));
304 return QWebEngineProfile::defaultProfile();
306 g_profile->settings()->setAttribute(QWebEngineSettings::PluginsEnabled,
true);
307 g_profile->settings()->setAttribute(QWebEngineSettings::DnsPrefetchEnabled,
true);
308 g_profile->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls,
true);
309 g_profile->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls,
false);
310 g_profile->settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled,
true);
311 g_profile->settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled,
true);
312 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
313 g_profile->settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
false);
315 QObject::connect(g_profile.get(), &QWebEngineProfile::downloadRequested,
316 &m_DownloadManager, &CFrmDownloadManager::slotDownloadRequested);
317 qDebug(log) <<
"Persistent path:" << g_profile->persistentStoragePath()
318 <<
"Cache path:" << g_profile->cachePath()
319 <<
"Storage name:" << g_profile->storageName()
320 <<
"Is off the Record:" << g_profile->isOffTheRecord();
322 return g_profile? g_profile.get() : QWebEngineProfile::defaultProfile();
329 auto profile = GetProfile();
330#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
331 profile->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
333 auto page =
new QWebEnginePage(profile, pView);
334 pView->setPage(page);
339QWidget* CFrmWebBrowser::CreateTab(
CFrmWebView **view)
341 QSplitter *pSplitter =
new QSplitter(Qt::Vertical,
this);
349 pWeb = CreateWebView();
353 pWeb = CreateWebView();
356 pSplitter->addWidget(pWeb);
358 auto pDevTools = CreateWebView();
361 pSplitter->addWidget(pDevTools);
362 bool check = connect(pWeb, &CFrmWebView::sigDevToolsRequested,
364 m_pInspector->setChecked(
true);
373CFrmWebView *CFrmWebBrowser::CurrentView(ViewType idx)
375 auto w = m_pTab->currentWidget();
376 if(!w)
return nullptr;
377 QSplitter *pSplitter = qobject_cast<QSplitter*>(w);
378 if(!pSplitter)
return nullptr;
379 int index = (int)idx;
380 if(0 > index && pSplitter->count() <= index)
return nullptr;
381 return qobject_cast<CFrmWebView*>(pSplitter->widget(index));
384bool CFrmWebBrowser::IsCurrentView(
CFrmWebView *pView)
386 auto w = m_pTab->currentWidget();
388 QSplitter *pSplitter = qobject_cast<QSplitter*>(w);
389 if(!pSplitter)
return false;
390 return -1 != pSplitter->indexOf(pView);
396 if(!pView)
return nRet;
397 QWidget* p = qobject_cast<QWidget*>(pView->parent());
399 nRet = m_pTab->indexOf(p);
403int CFrmWebBrowser::InitMenu(QMenu *pMenu)
407 pMenu->addAction(m_pBack);
408 pMenu->addAction(m_pForward);
409 pMenu->addAction(m_pRefresh);
410 m_pStop = pMenu->addAction(QIcon::fromTheme(
"stop"), tr(
"Stop"),
413 if(pWeb && pWeb->page())
414 pWeb->page()->action(QWebEnginePage::Stop)->trigger();
416 m_pStop->setEnabled(
false);
418 pMenu->addSeparator();
419 pMenu->addAction(m_pAddPage);
420 m_pAddWindow = pMenu->addAction(
421 QIcon::fromTheme(
"add"), tr(
"Add window"),
423 createWindow(QWebEnginePage::WebBrowserWindow);
425 m_pAddWindow->setVisible(
false);
426 m_pAddWindow->setStatusTip(m_pAddWindow->text());
428 pMenu->addSeparator();
429 m_pFind = pMenu->addAction(
430 QIcon::fromTheme(
"edit-find"), tr(
"&Find"),
this,
435 QString search = QInputDialog::getText(
this, tr(
"Find"),
436 tr(
"Find:"), QLineEdit::Normal,
438 if (ok && !search.isEmpty()) {
439 m_szFindText = search;
440#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
441 pWeb->findText(m_szFindText, QWebEnginePage::FindFlags(),
442 [&](
const QWebEngineFindTextResult& result){
443 if (result.numberOfMatches() == 0) {
444 emit sigInformation(tr(
"\"%1\" not found.").arg(m_szFindText));
446 emit sigInformation(tr(
"\"%1\" found: %2/%3").arg(m_szFindText, QString::number(result.activeMatch()), QString::number(result.numberOfMatches())));
450 pWeb->findText(m_szFindText, QWebEnginePage::FindFlags(), [
this](
bool found) {
452 emit sigInformation(tr(
"\"%1\" not found.").arg(m_szFindText));
458 m_pFind->setShortcuts(QKeySequence::Find);
459 m_pFind->setStatusTip(m_pFind->text());
461 m_pFindNext = pMenu->addAction(
462 QIcon::fromTheme(
"go-next"), tr(
"Find &Next"),
this,
465 if(pWeb && !m_szFindText.isEmpty()) {
466 pWeb->findText(m_szFindText);
469 m_pFindNext->setShortcut(QKeySequence::FindNext);
470 m_pFindNext->setText(m_pFindNext->text());
472 m_pFindPrevious = pMenu->addAction(
473 QIcon::fromTheme(
"go-previous"), tr(
"Find &Previous"),
this,
476 if(pWeb && !m_szFindText.isEmpty()) {
477 pWeb->findText(m_szFindText, QWebEnginePage::FindBackward);
480 m_pFindPrevious->setShortcut(QKeySequence::FindPrevious);
481 m_pFindPrevious->setStatusTip(m_pFindPrevious->text());
483 pMenu->addSeparator();
484 m_pZoomOriginal = pMenu->addAction(
485 QIcon::fromTheme(
"zoom-original"), tr(
"Original"));
486 m_pZoomOriginal->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0));
487 m_pZoomOriginal->setStatusTip(tr(
"Original"));
488 m_pZoomOriginal->setToolTip(tr(
"Original"));
489 check = connect(m_pZoomOriginal, &QAction::triggered,
this,
493 pWeb->setZoomFactor(1.0);
496 m_pZoomIn = pMenu->addAction(QIcon::fromTheme(
"zoom-in"), tr(
"Zoom in"));
497 m_pZoomIn->setShortcut(QKeySequence::ZoomIn);
498 m_pZoomIn->setStatusTip(tr(
"Zoom in"));
499 m_pZoomIn->setToolTip(tr(
"Zoom in"));
501 m_pZoomIn, &QAction::triggered,
this,
505 pWeb->setZoomFactor(pWeb->zoomFactor() + 0.1);
508 m_pZoomOut = pMenu->addAction(
509 QIcon::fromTheme(
"zoom-out"), tr(
"Zoom out"));
510 m_pZoomOut->setShortcut(QKeySequence::ZoomOut);
511 m_pZoomOut->setStatusTip(tr(
"Zoom out"));
512 m_pZoomOut->setToolTip(tr(
"Zoom out"));
514 m_pZoomOut, &QAction::triggered,
this,
518 pWeb->setZoomFactor(pWeb->zoomFactor() - 0.1);
522 pMenu->addSeparator();
523 pMenu->addAction(m_pDownload);
525 m_pInspector = pMenu->addAction(QIcon::fromTheme(
"tools"), tr(
"Inspector"));
526 check = connect(m_pInspector, &QAction::toggled,
527 this, &CFrmWebBrowser::slotInspector);
529 m_pInspector->setCheckable(
true);
530 m_pInspector->setEnabled(
false);
537int CFrmWebBrowser::Start()
541 m_pAddPage->trigger();
545void CFrmWebBrowser::slotTabCurrentChanged(
int index)
548 if(-1 == index)
return;
551 m_pUrlLineEdit->setText(pWeb->url().toString());
552 m_pProgressBar->setValue(pWeb->progress());
554 auto page = pWeb->page();
556 auto action = page->action(QWebEnginePage::Back);
557 if(action && m_pBack) {
558 m_pBack->setEnabled(action->isEnabled());
560 action = page->action(QWebEnginePage::Forward);
561 if(action && m_pForward) {
562 m_pForward->setEnabled(action->isEnabled());
564 action = page->action(QWebEnginePage::Reload);
565 if(action && m_pRefresh)
566 m_pRefresh->setEnabled(action->isEnabled());
567 action = page->action(QWebEnginePage::Stop);
568 if(action && m_pStop)
569 m_pStop->setEnabled(action->isEnabled());
570 m_pInspector->setChecked(!CurrentView(ViewType::DevTools)->isHidden());
573 m_pUrlLineEdit->setText(
"");
574 m_pProgressBar->setValue(100);
576 m_pInspector->setChecked(
false);
580void CFrmWebBrowser::EnableAction(
bool enable)
582 m_pBack->setEnabled(
false);
583 m_pForward->setEnabled(
false);
584 m_pRefresh->setEnabled(enable);
585 m_pStop->setEnabled(
false);
586 m_pFind->setEnabled(enable);
587 m_pFindNext->setEnabled(enable);
588 m_pFindPrevious->setEnabled(enable);
589 m_pZoomOriginal->setEnabled(enable);
590 m_pZoomIn->setEnabled(enable);
591 m_pZoomOut->setEnabled(enable);
592 m_pInspector->setEnabled(enable);
595void CFrmWebBrowser::slotTabCloseRequested(
int index)
597 qDebug(log) <<
"slotTabCloseRequested:" << index;
598 if(-1 == index)
return;
599 auto pView = m_pTab->widget(index);
601 pView->deleteLater();
602 m_pTab->removeTab(index);
605void CFrmWebBrowser::slotViewCloseRequested()
607 CFrmWebView* p = qobject_cast<CFrmWebView*>(sender());
609 int index = IndexOfTab(p);
610 slotTabCloseRequested(index);
613void CFrmWebBrowser::slotReturnPressed()
615 QUrl u = QUrl::fromUserInput(m_pUrlLineEdit->text());
616 emit sigInformation(u.toString());
619 pWeb = qobject_cast<CFrmWebView*>(createWindow(QWebEnginePage::WebBrowserTab));
621 if(m_pGo->isVisible())
622 m_pGo->setVisible(
false);
625void CFrmWebBrowser::slotInspector(
bool checked)
628 auto dev = CurrentView(ViewType::DevTools);
629 if(pWeb && pWeb->page() && checked) {
630 pWeb->page()->setDevToolsPage(dev->page());
634 pWeb->page()->setDevToolsPage(
nullptr);
void sigUpdateTitle()
Title or icon changed.