From 36abfa58fd1f174867ba35ee61108981eae04fb7 Mon Sep 17 00:00:00 2001 From: Sheen Tian Shen Date: Thu, 24 Aug 2023 00:15:31 +0800 Subject: [PATCH] Fix popup window logic - use CEF built-in popup management --- example/QCefViewTest/CefViewWidget.cpp | 3 +- example/QCefViewTest/CefViewWidget.h | 3 +- example/QCefViewTest/MainWindow.cpp | 2 +- include/QCefView.h | 37 +++--- src/QCefView.cpp | 33 +++--- src/details/CCefClientDelegate.h | 2 +- .../CCefClientDelegate_LifeSpanHandler.cpp | 51 ++++++--- src/details/QCefViewPrivate.cpp | 105 +++++++----------- src/details/QCefViewPrivate.h | 36 ++---- 9 files changed, 129 insertions(+), 143 deletions(-) diff --git a/example/QCefViewTest/CefViewWidget.cpp b/example/QCefViewTest/CefViewWidget.cpp index 87eed258..282e2d90 100644 --- a/example/QCefViewTest/CefViewWidget.cpp +++ b/example/QCefViewTest/CefViewWidget.cpp @@ -59,7 +59,8 @@ CefViewWidget::onBeforePopup(qint64 frameId, const QString& targetFrameName, QCefView::CefWindowOpenDisposition targetDisposition, QRect& rect, - QCefSetting& settings) + QCefSetting& settings, + bool& disableJavascript) { // create new QCefView as popup browser settings.setBackgroundColor(Qt::red); diff --git a/example/QCefViewTest/CefViewWidget.h b/example/QCefViewTest/CefViewWidget.h index e4795c8e..7665c67d 100644 --- a/example/QCefViewTest/CefViewWidget.h +++ b/example/QCefViewTest/CefViewWidget.h @@ -35,7 +35,8 @@ protected slots: const QString& targetFrameName, QCefView::CefWindowOpenDisposition targetDisposition, QRect& rect, - QCefSetting& settings) override; + QCefSetting& settings, + bool& disableJavascript) override; void onNewDownloadItem(const QSharedPointer& item, const QString& suggestedName) override; diff --git a/example/QCefViewTest/MainWindow.cpp b/example/QCefViewTest/MainWindow.cpp index 99175149..40929745 100644 --- a/example/QCefViewTest/MainWindow.cpp +++ b/example/QCefViewTest/MainWindow.cpp @@ -102,7 +102,7 @@ MainWindow::createRightCefView() // this site is for test web events m_pRightCefViewWidget = new CefViewWidget("", &setting, this); - m_pRightCefViewWidget->navigateToUrl("https://fastest.fish/test-files"); + m_pRightCefViewWidget->navigateToUrl("https://www.javatpoint.com/oprweb/test.jsp?filename=javascript-window-close-method1"); // // m_pRightCefViewWidget = new CefViewWidget("https://mdn.dev/", &setting, this); diff --git a/include/QCefView.h b/include/QCefView.h index 9966cc6e..f55f0462 100644 --- a/include/QCefView.h +++ b/include/QCefView.h @@ -77,7 +77,7 @@ class QCEFVIEW_EXPORT QCefView : public QWidget /// /// The parent /// The Qt WindowFlags - QCefView(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + explicit QCefView(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); /// /// Destructs the QCefView instance @@ -107,12 +107,6 @@ class QCEFVIEW_EXPORT QCefView : public QWidget /// The browser id int browserId(); - /// - /// Gets whether the browser is created as popup browser - /// - /// True if it is popup browser; otherwise false - bool isPopup(); - /// /// Navigates to the content. /// @@ -408,19 +402,6 @@ class QCEFVIEW_EXPORT QCefView : public QWidget /// The native browser windows void nativeBrowserCreated(QWindow* window); - /// - /// Gets called right after the popup browser was created. - /// - /// The new created popup QCefView instance - /// - /// The lifecycle of the popup browser is managed by the owner of the popup browser, - /// thus do not try to hold the popup browser instance. - /// If you need to implement browser tab, you should override the method - /// and create your own QCefView browser instance then you can manipulate the created one as whatever - /// you want. - /// - void popupCreated(QCefView* popup); - protected: /// /// Gets called before the popup browser created @@ -437,7 +418,21 @@ class QCEFVIEW_EXPORT QCefView : public QWidget const QString& targetFrameName, QCefView::CefWindowOpenDisposition targetDisposition, QRect& rect, - QCefSetting& settings); + QCefSetting& settings, + bool& disableJavascript); + + /// + /// Gets called right after the popup browser was created. + /// + /// The new created popup QCefView instance + /// + /// The lifecycle of the popup browser is managed by the owner of the popup browser, + /// thus do not try to hold the popup browser instance. + /// If you need to implement browser tab, you should override the method + /// and create your own QCefView browser instance then you can manipulate the created one as whatever + /// you want. + /// + void onPopupCreated(QWidget* popup, const QString& url, const QString& name); /// /// Gets called on new download item was required. Keep reference to the download item diff --git a/src/QCefView.cpp b/src/QCefView.cpp index 2f36b578..5ef16213 100644 --- a/src/QCefView.cpp +++ b/src/QCefView.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #pragma endregion qt_headers #include @@ -32,7 +33,7 @@ QCefView::QCefView(const QString url, setMouseTracking(true); setFocusPolicy(Qt::WheelFocus); - // create browser + // create cef browser d_ptr->createCefBrowser(this, url, setting); } @@ -46,9 +47,6 @@ QCefView::~QCefView() qDebug() << this << "is being destructed"; if (d_ptr) { - // close all popup browsers - d_ptr->closeAllPopupBrowsers(); - // destroy under layer cef browser d_ptr->destroyCefBrowser(); d_ptr.reset(); @@ -82,14 +80,6 @@ QCefView::browserId() return d->browserId(); } -bool -QCefView::isPopup() -{ - Q_D(QCefView); - - return d->isPopup(); -} - void QCefView::navigateToString(const QString& content) { @@ -288,11 +278,28 @@ QCefView::onBeforePopup(qint64 frameId, const QString& targetFrameName, QCefView::CefWindowOpenDisposition targetDisposition, QRect& rect, - QCefSetting& settings) + QCefSetting& settings, + bool& disableJavascript) { return false; } +void +QCefView::onPopupCreated(QWidget* popup, const QString& url, const QString& name) +{ + // create a top level window container + QMainWindow* popupWin = new QMainWindow(); + popupWin->setAttribute(Qt::WA_DeleteOnClose, true); + popupWin->setWindowTitle(name); + popupWin->resize(popup->size()); + + // add the popup browser widget into the window + popupWin->setCentralWidget(popup); + + // show window + popupWin->show(); +} + void QCefView::onNewDownloadItem(const QSharedPointer& item, const QString& suggestedName) { diff --git a/src/details/CCefClientDelegate.h b/src/details/CCefClientDelegate.h index 0b600ce4..a03e0d6c 100644 --- a/src/details/CCefClientDelegate.h +++ b/src/details/CCefClientDelegate.h @@ -141,7 +141,7 @@ class CCefClientDelegate CefLifeSpanHandler::WindowOpenDisposition targetDisposition, CefWindowInfo& windowInfo, CefBrowserSettings& settings, - bool& DisableJavascriptAccess) override; + bool& disableJavascriptAccess) override; virtual void onAfterCreate(CefRefPtr& browser) override; virtual bool doClose(CefRefPtr browser) override; virtual void onBeforeClose(CefRefPtr browser) override; diff --git a/src/details/CCefClientDelegate_LifeSpanHandler.cpp b/src/details/CCefClientDelegate_LifeSpanHandler.cpp index 5c1402b3..ecd685db 100644 --- a/src/details/CCefClientDelegate_LifeSpanHandler.cpp +++ b/src/details/CCefClientDelegate_LifeSpanHandler.cpp @@ -13,24 +13,34 @@ CCefClientDelegate::onBeforePopup(CefRefPtr& browser, CefLifeSpanHandler::WindowOpenDisposition targetDisposition, CefWindowInfo& windowInfo, CefBrowserSettings& settings, - bool& DisableJavascriptAccess) + bool& disableJavascriptAccess) { - if (pCefViewPrivate_) { + bool cancelPopup = true; - Qt::ConnectionType c = - pCefViewPrivate_->q_ptr->thread() == QThread::currentThread() ? Qt::DirectConnection : Qt::QueuedConnection; + if (!pCefViewPrivate_) + return cancelPopup; - QMetaObject::invokeMethod( - pCefViewPrivate_, - [=]() { - pCefViewPrivate_->onBeforeCefPopupCreate( - browser, frameId, targetUrl, targetFrameName, targetDisposition, windowInfo, settings); - }, - c); - } + // determine the connection type + Qt::ConnectionType c = + pCefViewPrivate_->q_ptr->thread() == QThread::currentThread() ? Qt::DirectConnection : Qt::BlockingQueuedConnection; + + // invoke the method + QMetaObject::invokeMethod( + pCefViewPrivate_, + [&]() { + cancelPopup = pCefViewPrivate_->onBeforeCefPopupBrowserCreate(browser, // + frameId, // + targetUrl, // + targetFrameName, // + targetDisposition, // + windowInfo, // + settings, // + disableJavascriptAccess // + ); + }, + c); - // QCefView doesn't use CEF built-in popup browser - return true; + return cancelPopup; } void @@ -41,7 +51,7 @@ CCefClientDelegate::onAfterCreate(CefRefPtr& browser) QWindow* w = nullptr; // #if !defined(CEF_USE_OSR) - if (!pCefViewPrivate_->isOSRModeEnabled() /*|| browser->IsPopup()*/) { + if (!pCefViewPrivate_->isOSRModeEnabled() || browser->IsPopup()) { // create QWindow from native browser window handle w = QWindow::fromWinId((WId)(browser->GetHost()->GetWindowHandle())); } @@ -51,7 +61,7 @@ CCefClientDelegate::onAfterCreate(CefRefPtr& browser) if (pCefViewPrivate_->q_ptr->thread() != QThread::currentThread()) { // change connection type // #if !defined(CEF_USE_OSR) - if (!pCefViewPrivate_->isOSRModeEnabled() /*|| browser->IsPopup()*/) { + if (!pCefViewPrivate_->isOSRModeEnabled() || browser->IsPopup()) { c = Qt::QueuedConnection; } else { // #else @@ -65,8 +75,13 @@ CCefClientDelegate::onAfterCreate(CefRefPtr& browser) } } - QMetaObject::invokeMethod( - pCefViewPrivate_, [=]() { pCefViewPrivate_->onCefBrowserCreated(browser, w); }, c); + if (browser->IsPopup()) { + QMetaObject::invokeMethod( + pCefViewPrivate_, [=]() { pCefViewPrivate_->onCefPopupBrowserCreated(browser, w); }, c); + } else { + QMetaObject::invokeMethod( + pCefViewPrivate_, [=]() { pCefViewPrivate_->onCefBrowserCreated(browser, w); }, c); + } } bool diff --git a/src/details/QCefViewPrivate.cpp b/src/details/QCefViewPrivate.cpp index 89b066cb..fab67471 100644 --- a/src/details/QCefViewPrivate.cpp +++ b/src/details/QCefViewPrivate.cpp @@ -62,7 +62,10 @@ QCefViewPrivate::~QCefViewPrivate() } void -QCefViewPrivate::createCefBrowser(QCefView* view, const QString url, const QCefSetting* setting) +QCefViewPrivate::createCefBrowser(QCefView* view, + const QString url, + const QCefSetting* setting, + bool isPopup /*= false*/) { // create browser client handler delegate auto pClientDelegate = std::make_shared(this); @@ -158,14 +161,6 @@ QCefViewPrivate::destroyCefBrowser() pCefBrowser_ = nullptr; } -void -QCefViewPrivate::closeAllPopupBrowsers() -{ - for (auto popup : popupBrowsers_) { - popup->close(); - } -} - void QCefViewPrivate::addLocalFolderResource(const QString& path, const QString& url, int priority /*= 0*/) { @@ -263,28 +258,17 @@ QCefViewPrivate::onCefBrowserCreated(CefRefPtr browser, QWindow* win UpdateCefWindowMask(ncw.qBrowserWindow_, q_ptr->mask()); } // #endif - - // if is popup browser record and show - if (isPopup_) { - // record - popupBrowsers_.insert(q_ptr); - - // show - q_ptr->show(); - - // emit the signal - q_ptr->popupCreated(q_ptr); - } } -void -QCefViewPrivate::onBeforeCefPopupCreate(const CefRefPtr& browser, - int64_t frameId, - const std::string& targetUrl, - const std::string& targetFrameName, - CefLifeSpanHandler::WindowOpenDisposition targetDisposition, - const CefWindowInfo& windowInfo, - const CefBrowserSettings& settings) +bool +QCefViewPrivate::onBeforeCefPopupBrowserCreate(const CefRefPtr& browser, + int64_t frameId, + const std::string& targetUrl, + const std::string& targetFrameName, + CefLifeSpanHandler::WindowOpenDisposition targetDisposition, + CefWindowInfo& windowInfo, + CefBrowserSettings& settings, + bool& disableJavascript) { Q_Q(QCefView); @@ -293,38 +277,47 @@ QCefViewPrivate::onBeforeCefPopupCreate(const CefRefPtr& browser, auto d = (QCefView::CefWindowOpenDisposition)targetDisposition; auto rc = QRect(windowInfo.bounds.x, windowInfo.bounds.y, windowInfo.bounds.width, windowInfo.bounds.height); - if (rc.width() <= 0) { + if (rc.width() <= 0) rc.setWidth(DEFAULT_POPUP_WIDTH); - } - - if (rc.height() <= 0) { + if (rc.height() <= 0) rc.setHeight(DEFAULT_POPUP_HEIGHT); - } + QCefSetting s; QCefSettingPrivate::CopyFromCefBrowserSettings(&s, &settings); + bool cancelPopup = q->onBeforePopup(frameId, url, name, d, rc, s, disableJavascript); + QCefSettingPrivate::CopyToCefBrowserSettings(&s, &settings); - if (q->onBeforePopup(frameId, url, name, d, rc, s)) { - // cancel popup - return; + if (!cancelPopup) { +#if CEF_VERSION_MAJOR > 85 + windowInfo.SetAsChild((CefWindowHandle)q->winId(), { 0, 0, rc.width(), rc.height() }); +#else + windowInfo.SetAsChild((CefWindowHandle)q->winId(), 0, 0, rc.width(), rc.height()); +#endif } - // allow popup, create QCefView as new popup browser - QCefView* popup = new QCefView(url, &s, nullptr, Qt::Window); - if (!popup) { - // failed to create QCefView, cancel popup - return; - } - popup->d_ptr->isPopup_ = true; + return cancelPopup; +} - connect(popup, SIGNAL(destroyed(QObject*)), this, SLOT(onPopupBrowserDestroyed(QObject*))); +void +QCefViewPrivate::onCefPopupBrowserCreated(CefRefPtr browser, QWindow* window) +{ + // extract parameters + auto frame = browser->GetMainFrame(); + auto name = QString::fromStdString(frame->GetName()); + auto url = QString::fromStdString(frame->GetURL()); - // config the popup QCefView - if (!name.isEmpty()) { - popup->setWindowTitle(name); - } + // create widget for browser + QWidget* popup = QWidget::createWindowContainer(window, nullptr, Qt::WindowFlags()); popup->setAttribute(Qt::WA_DeleteOnClose, true); - popup->resize(rc.size()); + popup->setWindowTitle(name); + popup->resize(window->size() * window->devicePixelRatio()); + + // notify the event of popup browser + q_ptr->onPopupCreated(popup, url, name); + + // show widget + popup->show(); } void @@ -363,12 +356,6 @@ QCefViewPrivate::handleLoadError(CefRefPtr& browser, return false; } -void -QCefViewPrivate::onPopupBrowserDestroyed(QObject* popup) -{ - popupBrowsers_.remove(static_cast(popup)); -} - void QCefViewPrivate::onAppFocusChanged(QWidget* old, QWidget* now) { @@ -980,12 +967,6 @@ QCefViewPrivate::browserId() return -1; } -bool -QCefViewPrivate::isPopup() -{ - return isPopup_; -} - void QCefViewPrivate::navigateToString(const QString& content) { diff --git a/src/details/QCefViewPrivate.h b/src/details/QCefViewPrivate.h index 0c32eefe..627a4ce6 100644 --- a/src/details/QCefViewPrivate.h +++ b/src/details/QCefViewPrivate.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -38,11 +37,6 @@ class QCefViewPrivate : public QObject /// static void destroyAllInstance(); - /// - /// - /// - bool isPopup_ = false; - /// /// /// @@ -170,11 +164,6 @@ class QCefViewPrivate : public QObject QElapsedTimer paintTimer_; #endif - /// - /// The list of popup child-browsers of this browser. - /// - QSet popupBrowsers_; - public: explicit QCefViewPrivate(QCefContextPrivate* ctx, QCefView* view, @@ -183,12 +172,10 @@ class QCefViewPrivate : public QObject ~QCefViewPrivate(); - void createCefBrowser(QCefView* view, const QString url, const QCefSetting* setting); + void createCefBrowser(QCefView* view, const QString url, const QCefSetting* setting, bool isPopup = false); void destroyCefBrowser(); - void closeAllPopupBrowsers(); - void addLocalFolderResource(const QString& path, const QString& url, int priority = 0); void addArchiveResource(const QString& path, const QString& url, const QString& password = "", int priority = 0); @@ -200,13 +187,16 @@ class QCefViewPrivate : public QObject protected: void onCefBrowserCreated(CefRefPtr browser, QWindow* window); - void onBeforeCefPopupCreate(const CefRefPtr& browser, - int64_t frameId, - const std::string& targetUrl, - const std::string& targetFrameName, - CefLifeSpanHandler::WindowOpenDisposition targetDisposition, - const CefWindowInfo& windowInfo, - const CefBrowserSettings& settings); + bool onBeforeCefPopupBrowserCreate(const CefRefPtr& browser, + int64_t frameId, + const std::string& targetUrl, + const std::string& targetFrameName, + CefLifeSpanHandler::WindowOpenDisposition targetDisposition, + CefWindowInfo& windowInfo, + CefBrowserSettings& settings, + bool& disableJavascript); + + void onCefPopupBrowserCreated(CefRefPtr browser, QWindow* window); void onNewDownloadItem(QSharedPointer item, const QString& suggestedName); @@ -219,8 +209,6 @@ class QCefViewPrivate : public QObject const std::string& failedUrl); public slots: - void onPopupBrowserDestroyed(QObject* popup); - void onAppFocusChanged(QWidget* old, QWidget* now); void onViewScreenChanged(QScreen* screen); @@ -287,8 +275,6 @@ public slots: public: int browserId(); - bool isPopup(); - void navigateToString(const QString& content); void navigateToUrl(const QString& url);