WebEngine 小工具权限浏览器示例
演示如何处理网站权限请求和管理现有权限。
Permission Browser 演示了如何使用QWebEnginePermission 类来管理网站权限。该示例包括处理传入权限请求以及修改现有权限的代码。此外,还演示了在QWebEngineProfile 类中定义的不同权限持久性策略的效果。
运行示例
要从 Qt Creator,打开Welcome 模式,并从Examples 中选择示例。更多信息,请参阅Qt Creator: 教程:构建并运行。
类定义
主窗口
MainWindow
类继承于QMainWindow 。在该类中,我们声明了一个指向QVBoxLayout 的方便指针,该指针将用来布置用于操作单个权限的部件,以及另一个指向显示当前待处理权限请求的部件的方便指针。我们还声明了一个QWebEngineView ,用于显示实际的网页内容。
class MainWindow : public QMainWindow, public Ui_MainWindow { Q_OBJECT public: explicit MainWindow(const QUrl &url); ~MainWindow(); private slots: void handlePermissionRequested(QWebEnginePermission permission); void handleUrlChanged(const QUrl &url); void handlePermissionModified(PermissionWidget *widget); void handleDeleteAllClicked(); void handleNewClicked(); void handleRefreshClicked(); void handleBackClicked(); void handleForwardClicked(); void handlePolicyComboBoxIndexChanged(int index); private: bool containsPermission(const QWebEnginePermission &permission); PermissionWidget *createPermissionWidget(const QWebEnginePermission &permission); void loadStoredPermissions(); QVBoxLayout *m_layout; QWebEngineProfile *m_profile; QWebEngineView *m_webview; PermissionWidget *m_pendingWidget; };
应用程序的其余布局是在 mainwindow.ui 中定义的,并使用Qt Creator Design 模式创建。MainWindow
是 Ui_MainWindow 的子类,而 Ui_MainWindow 是根据 mainwindow.ui 中的定义在编译时生成的 C++ 类。
PermissionWidget 和 PermissionDialog
PermissionWidget
类定义了与单个QWebEnginePermission 实例相对应的 widget。为方便起见,QWebEnginePermission 对象存储在其中。Widget 本身具有授予、拒绝或删除权限的控件;所有这些都在PermissionWidget.ui
中定义。
class PermissionWidget : public QWidget, public Ui_PermissionWidget { Q_OBJECT public: PermissionWidget(const QWebEnginePermission &permission, QWidget *parent = nullptr); QWebEnginePermission m_permission; signals: void permissionModified(PermissionWidget *widget); private: void updateState(); };
点击主窗口用户界面上的 "新建 "按钮后,会弹出一个窗口,允许用户向已知来源预授予权限。该弹出窗口由PermissionDialog
类定义:
class PermissionDialog : public QDialog, public Ui_PermissionDialog { Q_OBJECT public: PermissionDialog(const QWebEngineProfile *profile, QWidget *parent = nullptr); ~PermissionDialog(); QWebEnginePermission permission(); private: const QWebEngineProfile *m_profile; QWebEnginePermission *m_permission; };
处理传入的权限请求
每当网站使用可能会泄露用户隐私的 API 时,浏览器都会弹出提示,要求用户授予或拒绝权限。PermissionBrowser 示例的右下方有一个专门的部分,每当出现这种情况时,该部分就会填充PermissionWidget
。
PermissionWidget
显示权限的来源、请求的QWebEnginePermission::PermissionType 以及该权限的当前状态。它还有授予和拒绝权限的按钮。由于权限状态(默认情况下)会被记住,删除按钮允许用户从底层存储中删除权限。
为此,我们首先将QWebEnginePage 的permissionRequested
信号连接到MainWindow
的handlePermissionRequested
插槽:
connect(m_webview->page(), &QWebEnginePage::permissionRequested, this, &MainWindow::handlePermissionRequested);
信号处理程序相对简单:它会尝试为所提供的QWebEnginePermission 对象创建一个PermissionWidget
实例,如果成功,就会将该部件插入QFrame 指定的待定权限中。我们还订阅了PermissionWidget
的permissionModified
信号,以便稍后将PermissionWidget
从右下方移到上方的现有部件列表中。
void MainWindow::handlePermissionRequested(QWebEnginePermission permission) { PermissionWidget *widget = createPermissionWidget(permission); if (widget) { m_pendingFrame->layout()->addWidget(widget); connect(widget, &PermissionWidget::permissionModified, this, &MainWindow::handlePermissionModified); if (m_pendingWidget) m_pendingWidget->deleteLater(); m_pendingWidget = widget; } }
只有当我们还没有一个现有的PermissionWidget
时,我们才会创建一个新的 :
PermissionWidget *MainWindow::createPermissionWidget(const QWebEnginePermission &permission) { if (containsPermission(permission)) return nullptr; return new PermissionWidget(permission, this); }
修改权限并显示给用户
QWebEnginePermission 接口提供了grant() 和deny() 函数,这是更改权限状态所需的全部功能。如果应用程序需要忘记某个权限,我们可以使用reset() 函数。
在PermissionWidget
构造函数中,我们将这些函数与按钮的clicked
信号挂钩,这样就可以在QWebEnginePermission 对象上执行相关功能。
每当按下一个按钮,我们就会发出permissionModified
信号,MainWindow
利用该信号了解何时需要将小部件从右下角移到现有权限列表中。我们还确保调用updateState()
,它负责处理 widget 的可视化更新。当删除按钮被按下时,我们也会确保标记要删除的 widget,因为我们只想向用户显示现有权限。
PermissionWidget::PermissionWidget(const QWebEnginePermission &permission, QWidget *parent) : QWidget(parent) , m_permission(permission) { setupUi(this); connect(m_deleteButton, &QPushButton::clicked, [this]() { m_permission.reset(); emit permissionModified(this); deleteLater(); }); connect(m_grantButton, &QPushButton::clicked, [this]() { m_permission.grant(); updateState(); emit permissionModified(this); }); connect(m_denyButton, &QPushButton::clicked, [this]() { m_permission.deny(); updateState(); emit permissionModified(this); }); updateState(); }
updateState()
函数向用户显示QWebEnginePermission 提供的数据。该函数还确保,当某个权限处于QWebEnginePermission::Invalid 状态时,授予或拒绝该权限的按钮将被禁用。
void PermissionWidget::updateState() { switch (m_permission.state()) { case QWebEnginePermission::State::Invalid: m_stateLabel->setText("<font color='gray'>Invalid</font>"); m_grantButton->setEnabled(false); m_denyButton->setEnabled(false); break; case QWebEnginePermission::State::Ask: m_stateLabel->setText("<font color='yellow'>Waiting for response</font>"); break; case QWebEnginePermission::State::Granted: m_stateLabel->setText("<font color='green'>Granted</font>"); break; case QWebEnginePermission::State::Denied: m_stateLabel->setText("<font color='red'>Denied</font>"); break; } m_typeLabel->setText(QMetaEnum::fromType<QWebEnginePermission::PermissionType>().valueToKey((quint8)m_permission.permissionType())); m_originLabel->setText(m_permission.origin().toDisplayString()); }
当一个待定权限被授予或拒绝时,我们要将相关的部件移到上面的列表中,该列表包含当前存在的所有权限。我们可以在MainWindow::handlePermissionModified
插槽中这样做。
void MainWindow::handlePermissionModified(PermissionWidget *widget) { if (!m_pendingWidget || m_pendingWidget != widget) return; m_pendingFrame->layout()->removeWidget(widget); m_pendingWidget = nullptr; if (!QWebEnginePermission::isPersistent(widget->m_permission.permissionType()) || widget->m_permission.state() == QWebEnginePermission::State::Ask || m_profile->persistentPermissionsPolicy() == QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime) { widget->deleteLater(); return; } m_layout->insertWidget(0, widget); }
值得注意的是,我们只在知道权限已被记住的情况下才这样做;有些PermissionTypes
是非持久性的,这意味着每次使用时都需要向用户显示权限提示。我们还排除了状态为QWebEnginePermission::Ask 的权限,这表示该权限是reset() ,当QWebEngineProfile::persistentPermissionsPolicy 设置为AskEveryTime
时,我们不会在现有权限列表中添加任何内容。
注: 请查看QWebEnginePermission::PermissionType 文档,了解哪些PermissionTypes
是持久的。
显示和修改现有权限
默认情况下,权限会存储到磁盘中,并在应用程序启动时再次检索。要获取所有现有网站权限的列表,我们需要调用QWebEngineProfile::listAllPermissions():
void MainWindow::loadStoredPermissions() { QList<QWebEnginePermission> permissionsList = m_profile->listAllPermissions(); for (QWebEnginePermission &permission : permissionsList) { PermissionWidget *widget = createPermissionWidget(permission); if (widget) m_layout->insertWidget(0, widget); } }
对于列表中的每个权限,我们只需构建一个新的PermissionWidget
,并将其添加到屏幕右侧的列表中。现有权限的修改使用与待定权限完全相同的 API。
预授予权限
某些权限可以提前授予,前提是知道来源和权限类型。单击右上角的 "新建 "按钮会弹出一个对话框,让您可以这样做。该对话框由PermissionDialog
类实现:
PermissionDialog::PermissionDialog(const QWebEngineProfile *profile, QWidget *parent) : QDialog(parent) , m_profile(profile) , m_permission(nullptr) { setupUi(this); auto metaEnum = QMetaEnum::fromType<QWebEnginePermission::PermissionType>(); Q_ASSERT(metaEnum.value((quint8)QWebEnginePermission::PermissionType::Unsupported) == 0); for (int i = 1; i < metaEnum.keyCount(); ++i) { QWebEnginePermission::PermissionType permissionType = QWebEnginePermission::PermissionType(metaEnum.value(i)); if (QWebEnginePermission::isPersistent(permissionType)) m_permissionTypeComboBox->addItem(metaEnum.valueToKey((quint8)permissionType), QVariant::fromValue(permissionType)); } }
我们使用与QWebEnginePermission::PermissionType 关联的QMetaEnum 类型填充QComboBox 。我们确保过滤掉非持久权限类型,因为不支持预先授予这些权限。
我们显示对话框,并在MainWindow::handleNewClicked
插槽内的用户界面中添加显示结果的PermissionWidget
。处理新权限的方式与处理网站请求权限的方式相同:调用handlePermissionRequested()
。
void MainWindow::handleNewClicked() { PermissionDialog dialog(m_profile); if (dialog.exec() == QDialog::Accepted) { handlePermissionRequested(dialog.permission()); } }
更改权限持久性策略
默认情况下,每个已命名QWebEngineProfile 的权限都存储在磁盘中,而每个未命名/未记录的权限都存储在内存中。通常情况下,运行时不会更改此设置,但本示例将探讨每个选项的效果。
- QWebEngineProfile::StoreOnDisk 是默认设置,它确保当前程序运行时授予的任何权限都会在下次启动时重新加载。一个权限只需被授予一次,随后对触发请求的 API 的使用将自动被授予权限,直到应用程序调用 () 为止。QWebEnginePermission::reset
- QWebEngineProfile::StoreInMemory 除了权限会在应用程序退出时销毁而不会存入磁盘外,其他行为与上述相同。
- QWebEngineProfile::AskEveryTime 确保权限永远不会被记住,并且所有权限都是非持久性的。因此,每次网络应用程序接口需要权限时,都会向用户显示新的提示。该选项用于向后兼容和实现自己权限存储的应用程序。
为确保向用户显示以前存在的权限,我们需要调用QWebEngineProfile::listAllPermissions():
void MainWindow::loadStoredPermissions() { QList<QWebEnginePermission> permissionsList = m_profile->listAllPermissions(); for (QWebEnginePermission &permission : permissionsList) { PermissionWidget *widget = createPermissionWidget(permission); if (widget) m_layout->insertWidget(0, widget); } }
启动时以及用户从右上角QComboBox 更改策略时都要执行一次。
void MainWindow::handlePolicyComboBoxIndexChanged(int index) { QWebEngineProfile::PersistentPermissionsPolicy policy; switch (index) { case 0: policy = QWebEngineProfile::PersistentPermissionsPolicy::AskEveryTime; break; case 1: policy = QWebEngineProfile::PersistentPermissionsPolicy::StoreInMemory; break; case 2: policy = QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk; break; } if (policy == m_profile->persistentPermissionsPolicy()) return; for (int i = m_layout->count() - 1; i >= 0; i--) { PermissionWidget *widget = qobject_cast<PermissionWidget *>(m_layout->itemAt(i)->widget()); if (!widget) continue; widget->deleteLater(); } m_profile->setPersistentPermissionsPolicy(policy); loadStoredPermissions(); }
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.