Demonstrates how to handle website permission requests, and manage existing permissions.

Permission Browser demonstrates how to use the QWebEnginePermission class to manage website permissions. The example includes code for handling incoming permission requests, as well as modifying already existing permissions. Also demonstrated are the effects of the different permission persistence policies defined within the QWebEngineProfile class.
To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, see Qt Creator: Tutorial: Build and run.
The MainWindow class inherits QMainWindow. Inside, we declare a convenience pointer to the QVBoxLayout which will lay out the
widgets used to manipulate individual permissions, as well as another convenience pointer to the widget displaying the currently pending permission request. We also declare a QWebEngineView, which will be used to display the actual webpage contents.
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; };
The rest of the layout for the application is defined inside mainwindow.ui, and was created using Qt Creator's Design mode. MainWindow is a child class of Ui_MainWindow, which is a C++ class
generated at compile time from the definitions found inside mainwindow.ui.
The PermissionWidget class defines a widget corresponding to a single QWebEnginePermission instance. For convenience, the QWebEnginePermission object is stored within. The widget itself has controls for granting, denying, or deleting the permission; all of this is defined inside 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(); };
When clicking the "New" button in the main window's UI, a pop-up window will appear, allowing the user to pre-grant permission to a known origin. That pop-up is defined by the PermissionDialog
class:
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; };
Whenever a website uses an API that might compromise the user's privacy, the browser is expected to show them a prompt asking to either grant or deny permission. The PermissionBrowser example has a dedicated section at the
bottom right, which gets populated with a PermissionWidget whenever that happens.
The PermissionWidget displays the permission's origin, the requested QWebEnginePermission::PermissionType, as well as the current
status of that permission. It also has buttons for granting and denying the permission. Since the permission status is (by default) remembered, the delete button allows the user to remove the permission from the underlying
storage.
To achieve all this, we first connect QWebEnginePage's permissionRequested signal to MainWindow's handlePermissionRequested slot:
connect(m_webview->page(), &QWebEnginePage::permissionRequested, this, &MainWindow::handlePermissionRequested);
The signal handler is relatively simple: it attempts to create a PermissionWidget instance for the provided QWebEnginePermission object, and if it
succeeds it plugs that widget into the QFrame designated for pending permissions. We also subscribe to PermissionWidget's permissionModified
signal so that we can later move the PermissionWidget from the bottom right to the list of existing widgets above.
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; } }
We only create a new PermissionWidget if we don't already have an existing one:
PermissionWidget *MainWindow::createPermissionWidget(const QWebEnginePermission &permission) { if (containsPermission(permission)) return nullptr; return new PermissionWidget(permission, this); }
The QWebEnginePermission interface provides the grant() and deny() functions, which are all that's needed to change the status of a permission. If the application needs to forget about a permission, we use the reset() function.
Inside the PermissionWidget constructor, we hook those function up to the buttons' clicked signal, so that we can execute the relevant functionality on the QWebEnginePermission object.
Whenever a button is pressed, we emit the permissionModified signal, which MainWindow uses to know when it needs to move the widget from the bottom-right to the list of
existing permissions. We also make sure to call updateState(), which handles visual updates to the widget. When the delete button is pressed, we also make sure mark the widget for deletion, since we
only want to display existing permissions to the user.
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(); }
The updateState() function displays the data supplied by QWebEnginePermission to the user. It also makes sure that, when a permission is in the QWebEnginePermission::Invalid state, the buttons for granting or denying it are disabled.
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()); }
When a pending permission is granted or denied, we want to move the associated widget to the list above, which contains all currently existing permissions. We do this in the MainWindow::handlePermissionModified slot.
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); }
Notably, we only do this in cases where we know the permission is remembered; some PermissionTypes are non-persistent, which means they require a permission prompt be shown to the user every time
they're used. We also exclude permissions with a QWebEnginePermission::Ask state, which indicates that the permission was reset(), and we don't add anything to the list of existing permissions when QWebEngineProfile::persistentPermissionsPolicy is set to AskEveryTime.
Note: Check the QWebEnginePermission::PermissionType documentation to see which PermissionTypes are persistent.
By default, permissions are stored to disk and retrieved again on application startup. To get a list of all existing website permissions, we call 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); } }
For every permission in the list, we simply construct a new PermissionWidget, and add it to the list on the right-hand side of the screen. Existing permissions are modified using the exact same API as pending ones.
Certain permissions may be granted in advance, provided the origin and permission type are known. Clicking on the "New" button in the top right will create a pop-up dialog that allows you to do just that. The dialog is
implemented by the PermissionDialog class:
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)); } }
We populate the QComboBox using the QMetaEnum type associated with QWebEnginePermission::PermissionType. We make sure to filter out non-persistent permission types, since pre-granting these is not supported.
We display the dialog and add show the resulting PermissionWidget in the UI inside the MainWindow::handleNewClicked slot. The new permission is handled the same way we
would if a website requested it: by calling handlePermissionRequested().
void MainWindow::handleNewClicked() { PermissionDialog dialog(m_profile); if (dialog.exec() == QDialog::Accepted) { handlePermissionRequested(dialog.permission()); } }
By default, permissions are stored to disk for every named QWebEngineProfile, and in memory for every unnamed/off-the-record one. Normally, this setting won't be changed at runtime, but this example explores the effects of each option.
To ensure the user will be shown previously existing permissions, we need to call 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); } }
This is done one time at startup, as well as whenever the user changes the policy from the QComboBox from the top right.
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(); }