summaryrefslogtreecommitdiffstats
path: root/tests/manual/widgets
diff options
context:
space:
mode:
authorMartin Negyokru <[email protected]>2024-10-31 16:07:11 +0100
committerMartin Negyokru <[email protected]>2025-05-29 08:54:07 +0200
commit91f45c8e59bc4b9afd7b3a52d3c05e339a19ce71 (patch)
treea07418490848e27e46389d396fa628768000b671 /tests/manual/widgets
parent7b22a723e8515329dd334d35377f86e371f29c7d (diff)
Add API for extension managementHEADdev
Introduce QWebEngineExtensionManager and QWebEngineExtensionInfo. The manager has methods to load and install Chrome extensions from the filesystem. QWebEngineExtensionInfo provides information about a loaded extension. The current state of our js extension API support is very limited meaning most of the extensions downloaded from Chrome extension store won't work. Adding support for these APIs will be done in followup patches. Fixes: QTBUG-118452 Task-number: QTBUG-61676 Change-Id: I017ad5e8d2ba963afbd2f31ac36fee9451a951bd Reviewed-by: Allan Sandfeld Jensen <[email protected]>
Diffstat (limited to 'tests/manual/widgets')
-rw-r--r--tests/manual/widgets/CMakeLists.txt3
-rw-r--r--tests/manual/widgets/extensions/CMakeLists.txt24
-rw-r--r--tests/manual/widgets/extensions/main.cpp287
3 files changed, 314 insertions, 0 deletions
diff --git a/tests/manual/widgets/CMakeLists.txt b/tests/manual/widgets/CMakeLists.txt
index 94ee1c138..6d6555d83 100644
--- a/tests/manual/widgets/CMakeLists.txt
+++ b/tests/manual/widgets/CMakeLists.txt
@@ -8,3 +8,6 @@ endif()
if(TARGET Qt6::HttpServer)
add_subdirectory(webrtc)
endif()
+if(QT_FEATURE_webengine_extensions)
+ add_subdirectory(extensions)
+endif()
diff --git a/tests/manual/widgets/extensions/CMakeLists.txt b/tests/manual/widgets/extensions/CMakeLists.txt
new file mode 100644
index 000000000..a2d916f1f
--- /dev/null
+++ b/tests/manual/widgets/extensions/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(extensions LANGUAGES CXX)
+ find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
+ endif()
+
+qt_internal_add_manual_test(extensions
+ SOURCES
+ main.cpp
+)
+
+set_target_properties(extensions PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(extensions PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::WebEngineWidgets
+)
diff --git a/tests/manual/widgets/extensions/main.cpp b/tests/manual/widgets/extensions/main.cpp
new file mode 100644
index 000000000..938bf1fab
--- /dev/null
+++ b/tests/manual/widgets/extensions/main.cpp
@@ -0,0 +1,287 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QAbstractListModel>
+#include <QApplication>
+#include <QDirListing>
+#include <QFileDialog>
+#include <QHBoxLayout>
+#include <QListView>
+#include <QMainWindow>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QTemporaryDir>
+#include <QTimer>
+#include <QWebEngineProfileBuilder>
+#include <QWebEngineView>
+#include <QWebEngineProfile>
+#include <QWidget>
+
+#include <QWebEngineExtensionManager>
+#include <QWebEngineExtensionInfo>
+
+// Test for extension management APIs using QWebEngineExtensionManager
+// and QWebEngineExtensionInfo
+
+class ExtensionsListModel : public QAbstractListModel
+{
+public:
+ ExtensionsListModel(const QList<QWebEngineExtensionInfo> &extensions)
+ : m_extensionsList(std::move(extensions))
+ {
+ }
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override
+ {
+ Q_UNUSED(parent)
+ return m_extensionsList.count();
+ }
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
+ {
+ if (!index.isValid())
+ return QVariant();
+ switch (role) {
+ case Qt::DisplayRole: {
+ auto extension = m_extensionsList.at(index.row());
+ QString enabled = extension.isEnabled() ? " enabled" : " disabled";
+ return extension.name() + enabled + "\nId: " + extension.id()
+ + "\npath: " + extension.path();
+ }
+ case Qt::UserRole: {
+ QWebEngineExtensionInfo extension = m_extensionsList.at(index.row());
+ return QVariant::fromValue(extension);
+ }
+ default:
+ break;
+ }
+ return QVariant();
+ }
+
+private:
+ QList<QWebEngineExtensionInfo> m_extensionsList;
+};
+
+class ExtensionsWidget : public QWidget
+{
+public:
+ ExtensionsWidget(QWebEngineProfile *profile, QWebEngineExtensionManager *manager)
+ : m_profile(profile), m_extensionManager(manager)
+ {
+ update();
+
+ setLayout(new QVBoxLayout);
+ m_extensionsView.setSpacing(2);
+ m_extensionsView.setWrapping(true);
+ layout()->addWidget(&m_extensionsView);
+
+ auto *actionsBtn = new QPushButton("open actions menu");
+ QObject::connect(actionsBtn, &QPushButton::clicked, this,
+ &ExtensionsWidget::openActionsMenu);
+ layout()->addWidget(actionsBtn);
+
+ auto *enabeBtn = new QPushButton("enable selected");
+ QObject::connect(enabeBtn, &QPushButton::clicked, this, &ExtensionsWidget::enable);
+ layout()->addWidget(enabeBtn);
+
+ auto *disableBtn = new QPushButton("disable selected");
+ QObject::connect(disableBtn, &QPushButton::clicked, this, &ExtensionsWidget::disable);
+ layout()->addWidget(disableBtn);
+
+ auto *loadBtn = new QPushButton("load unpacked");
+ QObject::connect(loadBtn, &QPushButton::clicked, this, &ExtensionsWidget::loadUnpacked);
+ layout()->addWidget(loadBtn);
+
+ auto *installBtn = new QPushButton("install packed");
+ QObject::connect(installBtn, &QPushButton::clicked, this, [this]() { install(true); });
+ layout()->addWidget(installBtn);
+
+ auto *installUnpackedBtn = new QPushButton("install unpacked");
+ QObject::connect(installUnpackedBtn, &QPushButton::clicked, this,
+ [this]() { install(false); });
+ layout()->addWidget(installUnpackedBtn);
+
+ auto *unLoadBtn = new QPushButton("unload");
+ QObject::connect(unLoadBtn, &QPushButton::clicked, this, &ExtensionsWidget::unload);
+ layout()->addWidget(unLoadBtn);
+
+ auto *uninstallBtn = new QPushButton("uninstall");
+ QObject::connect(uninstallBtn, &QPushButton::clicked, this, &ExtensionsWidget::uninstall);
+ layout()->addWidget(uninstallBtn);
+
+ QObject::connect(m_extensionManager, &QWebEngineExtensionManager::extensionLoadFinished,
+ [this](QWebEngineExtensionInfo extension) {
+ if (!extension.isLoaded()) {
+ showInfoDialog("Failed to load extension\n\nFile:"
+ + extension.path()
+ + "\nError: " + extension.error());
+ return;
+ }
+ m_extensionManager->setExtensionEnabled(extension, true);
+ showInfoDialog("Extension loaded\n\nName:" + extension.name()
+ + "\nFile: " + extension.path());
+ update();
+ });
+
+ QObject::connect(m_extensionManager, &QWebEngineExtensionManager::extensionInstallFinished,
+ [this](QWebEngineExtensionInfo extension) {
+ if (!extension.isInstalled()) {
+ showInfoDialog("Failed to install extension\n\nFile: "
+ + extension.path()
+ + "\nError: " + extension.error());
+ return;
+ }
+ showInfoDialog("Extension installed\n\nName:" + extension.name()
+ + "\nFile: " + extension.path());
+ m_extensionManager->setExtensionEnabled(extension, true);
+ update();
+ });
+ QObject::connect(m_extensionManager, &QWebEngineExtensionManager::extensionUnloadFinished,
+ [this](QWebEngineExtensionInfo extension) {
+ if (!extension.error().isEmpty()) {
+ showInfoDialog("Failed to unload " + extension.name()
+ + "\n\nFile: " + extension.path()
+ + "\nError: " + extension.error());
+ return;
+ }
+ showInfoDialog("Extension unloaded\n\nName: " + extension.name()
+ + "\nFile: " + extension.path());
+ update();
+ });
+ QObject::connect(
+ m_extensionManager, &QWebEngineExtensionManager::extensionUninstallFinished,
+ [this](QWebEngineExtensionInfo extension) {
+ if (!extension.error().isEmpty()) {
+ showInfoDialog("Failed to uninstall " + extension.name() + "\n\nFile: "
+ + extension.path() + "\nError: " + extension.error());
+ return;
+ }
+ showInfoDialog("Extension uninstalled\n\nName: " + extension.name()
+ + "\nFile: " + extension.path());
+ update();
+ });
+ }
+
+private:
+ void update()
+ {
+ auto *oldModel = m_extensionsView.selectionModel();
+ m_extensionsView.setModel(
+ new ExtensionsListModel(std::move(m_extensionManager->extensions())));
+ if (oldModel)
+ delete oldModel;
+ }
+
+ QWebEngineExtensionInfo getSelectedExtension()
+ {
+ QModelIndex idx = m_extensionsView.currentIndex();
+ QVariant var = m_extensionsView.model()->data(idx, Qt::UserRole);
+ QWebEngineExtensionInfo extension = var.value<QWebEngineExtensionInfo>();
+ return extension;
+ }
+
+ void enable()
+ {
+ m_extensionManager->setExtensionEnabled(getSelectedExtension(), true);
+ update();
+ }
+
+ void disable()
+ {
+ m_extensionManager->setExtensionEnabled(getSelectedExtension(), false);
+ update();
+ }
+
+ void loadUnpacked()
+ {
+ QFileDialog dialog(this);
+ dialog.setFileMode(QFileDialog::Directory);
+ if (dialog.exec())
+ m_extensionManager->loadExtension(dialog.selectedFiles()[0]);
+ }
+
+ void install(bool packed)
+ {
+ QFileDialog dialog(this);
+ if (packed) {
+ dialog.setFileMode(QFileDialog::ExistingFile);
+ dialog.setNameFilter("Extensions(*.zip)");
+ } else {
+ dialog.setFileMode(QFileDialog::Directory);
+ }
+ if (dialog.exec())
+ m_extensionManager->installExtension(dialog.selectedFiles()[0]);
+ }
+
+ void unload()
+ {
+ m_extensionManager->unloadExtension(getSelectedExtension());
+ update();
+ }
+
+ void uninstall()
+ {
+ m_extensionManager->uninstallExtension(getSelectedExtension());
+ update();
+ }
+
+ void openActionsMenu()
+ {
+ const auto url = getSelectedExtension().actionPopupUrl();
+ if (url.isEmpty()) {
+ showInfoDialog("No popup page set for this extension");
+ return;
+ }
+
+ auto *view = new QWebEngineView(m_profile);
+ view->setAttribute(Qt::WA_DeleteOnClose, true);
+ view->load(url);
+ view->show();
+ }
+
+ void showInfoDialog(const QString &msg)
+ {
+ QMessageBox *msgBox = new QMessageBox;
+ msgBox->setWindowModality(Qt::NonModal);
+ msgBox->setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
+ msgBox->setAttribute(Qt::WA_DeleteOnClose, true);
+ msgBox->resize(400, 100);
+ msgBox->setText(msg);
+ QTimer::singleShot(4000, msgBox, &QMessageBox::close);
+ msgBox->show();
+ }
+
+ QWebEngineProfile *m_profile;
+ QWebEngineExtensionManager *m_extensionManager;
+ QListView m_extensionsView;
+};
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication::setOrganizationName("QtExamples");
+ QApplication app(argc, argv);
+ QMainWindow window;
+ window.setCentralWidget(new QWidget);
+
+ auto *layout = new QHBoxLayout;
+ window.centralWidget()->setLayout(layout);
+
+ QTemporaryDir tempDir;
+ QWebEngineProfileBuilder profileBuilder;
+ QWebEngineProfile *profile = profileBuilder.createProfile("ExtensionsManualTest");
+
+ auto *extensionManager = profile->extensionManager();
+ qDebug() << "installDir" << extensionManager->installDirectory();
+
+ QWebEngineView view(profile);
+ view.setUrl(QUrl(QStringLiteral("https://www.google.com/")));
+ view.resize(1024, 750);
+
+ window.centralWidget()->layout()->addWidget(&view);
+ window.centralWidget()->layout()->addWidget(
+ new ExtensionsWidget(profile, extensionManager));
+
+ window.show();
+
+ return app.exec();
+}