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)
44 QVBoxLayout* pLayout =
new QVBoxLayout(
this);
48 pLayout->setSpacing(0);
49 pLayout->setContentsMargins(0, 0, 0, 0);
52 m_pToolBar =
new QToolBar(
this);
54 pLayout->addWidget(m_pToolBar);
57 m_pBack = m_pToolBar->addAction(
58 QIcon::fromTheme(
"go-previous"), tr(
"Back"),
61 if(pWeb && pWeb->page())
62 pWeb->page()->action(QWebEnginePage::Back)->trigger();
64 m_pBack->setEnabled(
false);
65 m_pBack->setStatusTip(m_pBack->text());
66 m_pForward = m_pToolBar->addAction(
67 QIcon::fromTheme(
"go-next"), tr(
"Forward"),
70 if(pWeb && pWeb->page())
71 pWeb->page()->action(QWebEnginePage::Forward)->trigger();
73 m_pForward->setEnabled(
false);
74 m_pForward->setStatusTip(m_pForward->text());
75 m_pRefresh = m_pToolBar->addAction(
76 QIcon::fromTheme(
"view-refresh"), tr(
"Refresh"),
79 if(pWeb && pWeb->page())
80 pWeb->page()->action(QWebEnginePage::Reload)->trigger();
82 m_pRefresh->setShortcut(QKeySequence(QKeySequence::Refresh));
83 m_pRefresh->setStatusTip(m_pRefresh->text());
85 m_pUrlLineEdit =
new QLineEdit(
this);
86 m_pFavAction =
new QAction(m_pUrlLineEdit);
87 m_pUrlLineEdit->addAction(m_pFavAction, QLineEdit::LeadingPosition);
88 m_pUrlLineEdit->setClearButtonEnabled(
true);
89 m_pToolBar->addWidget(m_pUrlLineEdit);
90 check = connect(m_pUrlLineEdit, &QLineEdit::returnPressed,
91 this, &CFrmWebBrowser::slotReturnPressed);
93 check = connect(m_pUrlLineEdit, &QLineEdit::editingFinished,
94 this, &CFrmWebBrowser::slotReturnPressed);
96 m_pGo =
new QAction(QIcon::fromTheme(
"go-next"), tr(
"go"), m_pUrlLineEdit);
97 m_pGo->setStatusTip(m_pGo->text());
98 check = connect(m_pGo, &QAction::triggered,
this, &CFrmWebBrowser::slotReturnPressed);
100 m_pUrlLineEdit->addAction(m_pGo, QLineEdit::TrailingPosition);
101 m_pGo->setVisible(
false);
102 check = connect(m_pUrlLineEdit, &QLineEdit::textEdited,
103 this, [&](
const QString &text){
104 QLineEdit* pLineEdit = qobject_cast<QLineEdit*>(sender());
107 if(m_pGo->isVisible())
108 m_pGo->setVisible(
false);
110 if(!m_pGo->isVisible())
111 m_pGo->setVisible(
true);
117 m_pAddPage = m_pToolBar->addAction(QIcon::fromTheme(
"add"), tr(
"Add tab page"),
119 auto pView = createWindow(QWebEnginePage::WebBrowserTab);
120 if(!m_pPara->GetTabUrl().isEmpty()) {
121 m_pUrlLineEdit->setText(m_pPara->GetTabUrl());
125 m_pAddPage->setStatusTip(m_pAddPage->text());
127 m_pDownload = m_pToolBar->addAction(QIcon::fromTheme(
"emblem-downloads"), tr(
"Download"));
128 m_pDownload->setCheckable(
true);
129 m_pDownload->setStatusTip(m_pDownload->text());
130 check = connect(m_pDownload, &QAction::toggled,
131 this, [&](
bool checked){
133 m_DownloadManager.show();
135 m_DownloadManager.hide();
138 check = connect(&m_DownloadManager, &CFrmDownloadManager::sigVisible,
this,
141 m_pDownload->setChecked(visible);
145 m_pProgressBar =
new QProgressBar(
this);
147 pLayout->addWidget(m_pProgressBar);
148 m_pProgressBar->setMaximumHeight(1);
149 m_pProgressBar->setTextVisible(
false);
150 m_pProgressBar->show();
151 m_pProgressBar->setStyleSheet(
"QProgressBar {border: 0px} QProgressBar::chunk {background-color: #da4453}");
154 m_pTab =
new QTabWidget(
this);
156 m_pTab->setTabsClosable(
true);
157 m_pTab->setUsesScrollButtons(
true);
158 m_pTab->setMovable(
true);
159 m_pTab->setElideMode(Qt::TextElideMode::ElideRight);
160 pLayout->addWidget(m_pTab);
162 check = connect(m_pTab, &QTabWidget::currentChanged,
163 this, &CFrmWebBrowser::slotTabCurrentChanged);
165 check = connect(m_pTab, &QTabWidget::tabCloseRequested,
166 this, &CFrmWebBrowser::slotTabCloseRequested);
169 m_DownloadManager.hide();
170 QObject::connect(QWebEngineProfile::defaultProfile(), &QWebEngineProfile::downloadRequested,
171 &m_DownloadManager, &CFrmDownloadManager::slotDownloadRequested);
174CFrmWebBrowser::~CFrmWebBrowser()
177 m_pToolBar->deleteLater();
178 m_pToolBar =
nullptr;
181 m_pTab->deleteLater();
186QWebEngineView* CFrmWebBrowser::createWindow(QWebEnginePage::WebWindowType type)
188 qDebug(log) <<
"Create window:" << type;
192 case QWebEnginePage::WebBrowserTab: {
193 auto pTab = CreateTab(&pView);
194 int index = m_pTab->addTab(pTab, pView->favIcon(), tr(
"New page"));
196 m_pTab->setCurrentIndex(index);
199 case QWebEnginePage::WebBrowserBackgroundTab: {
200 auto pTab = CreateTab(&pView);
201 int index = m_pTab->currentIndex();
202 m_pTab->addTab(pTab, pView->favIcon(), tr(
"New page"));
204 m_pTab->setCurrentIndex(index);
207 case QWebEnginePage::WebBrowserWindow: {
208 auto pWin = CreateTab(&pView);
212 case QWebEnginePage::WebDialog: {
213 qDebug(log) <<
"Don't support type:" << type;
224 check = connect(pWeb, &CFrmWebView::sigWebActionEnabledChanged,
225 this, [
this, pWeb](QWebEnginePage::WebAction webAction,
bool enabled){
226 if(!IsCurrentView(pWeb))
return;
228 case QWebEnginePage::WebAction::Back:
229 m_pBack->setEnabled(enabled);
231 case QWebEnginePage::WebAction::Forward:
232 m_pForward->setEnabled(enabled);
234 case QWebEnginePage::WebAction::Reload:
235 m_pRefresh->setEnabled(enabled);
237 case QWebEnginePage::WebAction::Stop:
238 m_pStop->setEnabled(enabled);
244 check = connect(pWeb, &QWebEngineView::urlChanged,
245 this, [&](
const QUrl &url){
246 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
247 if(IsCurrentView(pWeb))
248 m_pUrlLineEdit->setText(url.toString());
251 check = connect(pWeb, &CFrmWebView::titleChanged,
252 this, [&](
const QString &title) {
253 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
254 int index = IndexOfTab(pWeb);
256 m_pTab->setTabText(index, title);
257 setWindowTitle(title);
262 check = connect(pWeb, &CFrmWebView::favIconChanged,
263 this, [&](
const QIcon &icon){
264 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
265 int index = IndexOfTab(pWeb);
267 m_pTab->setTabIcon(index, icon);
273 check = connect(pWeb, &CFrmWebView::sigLinkHovered,
274 this, &CFrmWebBrowser::sigInformation);
276 check = connect(pWeb, &CFrmWebView::sigCloseRequested,
277 this, &CFrmWebBrowser::slotViewCloseRequested);
279 check = connect(pWeb, &CFrmWebView::loadProgress,
280 this, [&](
int progress){
281 CFrmWebView* pWeb = qobject_cast<CFrmWebView*>(sender());
282 if(IsCurrentView(pWeb))
283 m_pProgressBar->setValue(progress);
286 m_pProgressBar->setValue(pWeb->progress());
289QWebEngineProfile* CFrmWebBrowser::GetProfile()
293#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
294 const QString name =
"io.github.KangLin.RabbitRemoteControl."
295 + QLatin1StringView(qWebEngineChromiumVersion());
296 QWebEngineProfileBuilder profileBuilder;
297 g_profile.reset(profileBuilder.createProfile(name));
299 const QString name =
"io.github.KangLin.RabbitRemoteControl";
300 g_profile.reset(
new QWebEngineProfile(name));
303 return QWebEngineProfile::defaultProfile();
305 g_profile->settings()->setAttribute(QWebEngineSettings::PluginsEnabled,
true);
306 g_profile->settings()->setAttribute(QWebEngineSettings::DnsPrefetchEnabled,
true);
307 g_profile->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls,
true);
308 g_profile->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls,
false);
309 g_profile->settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled,
true);
310 g_profile->settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled,
true);
311 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
312 g_profile->settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
false);
314 QObject::connect(g_profile.get(), &QWebEngineProfile::downloadRequested,
315 &m_DownloadManager, &CFrmDownloadManager::slotDownloadRequested);
316 qDebug(log) <<
"Persistent path:" << g_profile->persistentStoragePath()
317 <<
"Cache path:" << g_profile->cachePath()
318 <<
"Storage name:" << g_profile->storageName()
319 <<
"Is off the Record:" << g_profile->isOffTheRecord();
321 return g_profile? g_profile.get() : QWebEngineProfile::defaultProfile();
328 auto profile = GetProfile();
329#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
330 profile->setPersistentPermissionsPolicy(QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime);
332 auto page =
new QWebEnginePage(profile, pView);
333 pView->setPage(page);
338QWidget* CFrmWebBrowser::CreateTab(
CFrmWebView **view)
340 QSplitter *pSplitter =
new QSplitter(Qt::Vertical,
this);
348 pWeb = CreateWebView();
352 pWeb = CreateWebView();
355 pSplitter->addWidget(pWeb);
357 auto pDevTools = CreateWebView();
360 pSplitter->addWidget(pDevTools);
361 bool check = connect(pWeb, &CFrmWebView::sigDevToolsRequested,
363 m_pInspector->setChecked(
true);
372CFrmWebView *CFrmWebBrowser::CurrentView(ViewType idx)
374 auto w = m_pTab->currentWidget();
375 if(!w)
return nullptr;
376 QSplitter *pSplitter = qobject_cast<QSplitter*>(w);
377 if(!pSplitter)
return nullptr;
378 int index = (int)idx;
379 if(0 > index && pSplitter->count() <= index)
return nullptr;
380 return qobject_cast<CFrmWebView*>(pSplitter->widget(index));
383bool CFrmWebBrowser::IsCurrentView(
CFrmWebView *pView)
385 auto w = m_pTab->currentWidget();
387 QSplitter *pSplitter = qobject_cast<QSplitter*>(w);
388 if(!pSplitter)
return false;
389 return -1 != pSplitter->indexOf(pView);
395 if(!pView)
return nRet;
396 QWidget* p = qobject_cast<QWidget*>(pView->parent());
398 nRet = m_pTab->indexOf(p);
402int CFrmWebBrowser::InitMenu(QMenu *pMenu)
406 pMenu->addAction(m_pBack);
407 pMenu->addAction(m_pForward);
408 pMenu->addAction(m_pRefresh);
409 m_pStop = pMenu->addAction(QIcon::fromTheme(
"stop"), tr(
"Stop"),
412 if(pWeb && pWeb->page())
413 pWeb->page()->action(QWebEnginePage::Stop)->trigger();
415 m_pStop->setEnabled(
false);
417 pMenu->addSeparator();
418 pMenu->addAction(m_pAddPage);
419 m_pAddWindow = pMenu->addAction(
420 QIcon::fromTheme(
"add"), tr(
"Add window"),
422 createWindow(QWebEnginePage::WebBrowserWindow);
424 m_pAddWindow->setVisible(
false);
425 m_pAddWindow->setStatusTip(m_pAddWindow->text());
427 pMenu->addSeparator();
428 m_pFind = pMenu->addAction(
429 QIcon::fromTheme(
"edit-find"), tr(
"&Find"),
this,
434 QString search = QInputDialog::getText(
this, tr(
"Find"),
435 tr(
"Find:"), QLineEdit::Normal,
437 if (ok && !search.isEmpty()) {
438 m_szFindText = search;
439#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
440 pWeb->findText(m_szFindText, QWebEnginePage::FindFlags(),
441 [&](
const QWebEngineFindTextResult& result){
442 if (result.numberOfMatches() == 0) {
443 emit sigInformation(tr(
"\"%1\" not found.").arg(m_szFindText));
445 emit sigInformation(tr(
"\"%1\" found: %2/%3").arg(m_szFindText, QString::number(result.activeMatch()), QString::number(result.numberOfMatches())));
449 pWeb->findText(m_szFindText, QWebEnginePage::FindFlags(), [
this](
bool found) {
451 emit sigInformation(tr(
"\"%1\" not found.").arg(m_szFindText));
457 m_pFind->setShortcuts(QKeySequence::Find);
458 m_pFind->setStatusTip(m_pFind->text());
460 m_pFindNext = pMenu->addAction(
461 QIcon::fromTheme(
"go-next"), tr(
"Find &Next"),
this,
464 if(pWeb && !m_szFindText.isEmpty()) {
465 pWeb->findText(m_szFindText);
468 m_pFindNext->setShortcut(QKeySequence::FindNext);
469 m_pFindNext->setText(m_pFindNext->text());
471 m_pFindPrevious = pMenu->addAction(
472 QIcon::fromTheme(
"go-previous"), tr(
"Find &Previous"),
this,
475 if(pWeb && !m_szFindText.isEmpty()) {
476 pWeb->findText(m_szFindText, QWebEnginePage::FindBackward);
479 m_pFindPrevious->setShortcut(QKeySequence::FindPrevious);
480 m_pFindPrevious->setStatusTip(m_pFindPrevious->text());
482 pMenu->addSeparator();
483 m_pZoomOriginal = pMenu->addAction(
484 QIcon::fromTheme(
"zoom-original"), tr(
"Original"));
485 m_pZoomOriginal->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0));
486 m_pZoomOriginal->setStatusTip(tr(
"Original"));
487 m_pZoomOriginal->setToolTip(tr(
"Original"));
488 check = connect(m_pZoomOriginal, &QAction::triggered,
this,
492 pWeb->setZoomFactor(1.0);
495 m_pZoomIn = pMenu->addAction(QIcon::fromTheme(
"zoom-in"), tr(
"Zoom in"));
496 m_pZoomIn->setShortcut(QKeySequence::ZoomIn);
497 m_pZoomIn->setStatusTip(tr(
"Zoom in"));
498 m_pZoomIn->setToolTip(tr(
"Zoom in"));
500 m_pZoomIn, &QAction::triggered,
this,
504 pWeb->setZoomFactor(pWeb->zoomFactor() + 0.1);
507 m_pZoomOut = pMenu->addAction(
508 QIcon::fromTheme(
"zoom-out"), tr(
"Zoom out"));
509 m_pZoomOut->setShortcut(QKeySequence::ZoomOut);
510 m_pZoomOut->setStatusTip(tr(
"Zoom out"));
511 m_pZoomOut->setToolTip(tr(
"Zoom out"));
513 m_pZoomOut, &QAction::triggered,
this,
517 pWeb->setZoomFactor(pWeb->zoomFactor() - 0.1);
521 pMenu->addSeparator();
522 pMenu->addAction(m_pDownload);
524 m_pInspector = pMenu->addAction(QIcon::fromTheme(
"tools"), tr(
"Inspector"));
525 check = connect(m_pInspector, &QAction::toggled,
526 this, &CFrmWebBrowser::slotInspector);
528 m_pInspector->setCheckable(
true);
529 m_pInspector->setEnabled(
false);
536int CFrmWebBrowser::Start()
540 m_pAddPage->trigger();
544void CFrmWebBrowser::slotTabCurrentChanged(
int index)
547 if(-1 == index)
return;
550 m_pUrlLineEdit->setText(pWeb->url().toString());
551 m_pProgressBar->setValue(pWeb->progress());
553 auto page = pWeb->page();
555 auto action = page->action(QWebEnginePage::Back);
556 if(action && m_pBack) {
557 m_pBack->setEnabled(action->isEnabled());
559 action = page->action(QWebEnginePage::Forward);
560 if(action && m_pForward) {
561 m_pForward->setEnabled(action->isEnabled());
563 action = page->action(QWebEnginePage::Reload);
564 if(action && m_pRefresh)
565 m_pRefresh->setEnabled(action->isEnabled());
566 action = page->action(QWebEnginePage::Stop);
567 if(action && m_pStop)
568 m_pStop->setEnabled(action->isEnabled());
569 m_pInspector->setChecked(!CurrentView(ViewType::DevTools)->isHidden());
572 m_pUrlLineEdit->setText(
"");
573 m_pProgressBar->setValue(100);
575 m_pInspector->setChecked(
false);
579void CFrmWebBrowser::EnableAction(
bool enable)
581 m_pBack->setEnabled(
false);
582 m_pForward->setEnabled(
false);
583 m_pRefresh->setEnabled(enable);
584 m_pStop->setEnabled(
false);
585 m_pFind->setEnabled(enable);
586 m_pFindNext->setEnabled(enable);
587 m_pFindPrevious->setEnabled(enable);
588 m_pZoomOriginal->setEnabled(enable);
589 m_pZoomIn->setEnabled(enable);
590 m_pZoomOut->setEnabled(enable);
591 m_pInspector->setEnabled(enable);
594void CFrmWebBrowser::slotTabCloseRequested(
int index)
596 qDebug(log) <<
"slotTabCloseRequested:" << index;
597 if(-1 == index)
return;
598 auto pView = m_pTab->widget(index);
600 pView->deleteLater();
601 m_pTab->removeTab(index);
604void CFrmWebBrowser::slotViewCloseRequested()
606 CFrmWebView* p = qobject_cast<CFrmWebView*>(sender());
608 int index = IndexOfTab(p);
609 slotTabCloseRequested(index);
612void CFrmWebBrowser::slotReturnPressed()
614 QUrl u = QUrl::fromUserInput(m_pUrlLineEdit->text());
615 emit sigInformation(u.toString());
618 pWeb = qobject_cast<CFrmWebView*>(createWindow(QWebEnginePage::WebBrowserTab));
620 if(m_pGo->isVisible())
621 m_pGo->setVisible(
false);
624void CFrmWebBrowser::slotInspector(
bool checked)
627 auto dev = CurrentView(ViewType::DevTools);
628 if(pWeb && pWeb->page() && checked) {
629 pWeb->page()->setDevToolsPage(dev->page());
633 pWeb->page()->setDevToolsPage(
nullptr);
void sigUpdateTitle()
Title or icon changed.